10 #include "clang/Frontend/CompilerInstance.h"
11 #include "clang/Lex/Lexer.h"
12 #include "clang/Lex/Preprocessor.h"
22 constexpr
char StdMemoryHeader[] =
"memory";
23 constexpr
char ConstructorCall[] =
"constructorCall";
24 constexpr
char ResetCall[] =
"resetCall";
25 constexpr
char NewExpression[] =
"newExpression";
27 std::string GetNewExprName(
const CXXNewExpr *NewExpr,
28 const SourceManager &SM,
29 const LangOptions &Lang) {
30 StringRef WrittenName = Lexer::getSourceText(
31 CharSourceRange::getTokenRange(
32 NewExpr->getAllocatedTypeSourceInfo()->getTypeLoc().getSourceRange()),
34 if (NewExpr->isArray()) {
35 return (WrittenName +
"[]").str();
37 return WrittenName.str();
42 const char MakeSmartPtrCheck::PointerType[] =
"pointerType";
45 StringRef MakeSmartPtrFunctionName)
47 IncludeStyle(Options.getLocalOrGlobal(
"IncludeStyle",
48 utils::IncludeSorter::IS_LLVM)),
49 MakeSmartPtrFunctionHeader(
50 Options.get(
"MakeSmartPtrFunctionHeader", StdMemoryHeader)),
51 MakeSmartPtrFunctionName(
52 Options.get(
"MakeSmartPtrFunction", MakeSmartPtrFunctionName)),
53 IgnoreMacros(Options.getLocalOrGlobal(
"IgnoreMacros", true)) {}
57 Options.
store(Opts,
"MakeSmartPtrFunctionHeader", MakeSmartPtrFunctionHeader);
58 Options.
store(Opts,
"MakeSmartPtrFunction", MakeSmartPtrFunctionName);
63 const LangOptions &LangOpts)
const {
64 return LangOpts.CPlusPlus11;
69 Preprocessor *ModuleExpanderPP) {
70 Inserter = std::make_unique<utils::IncludeInserter>(SM,
getLangOpts(),
72 PP->addPPCallbacks(Inserter->CreatePPCallbacks());
78 auto CanCallCtor = unless(has(ignoringImpCasts(
79 cxxConstructExpr(hasDeclaration(decl(unless(
isPublic())))))));
81 auto IsPlacement = hasAnyPlacementArg(anything());
85 ast_type_traits::TK_AsIs,
86 cxxBindTemporaryExpr(has(ignoringParenImpCasts(
90 0, cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
92 CanCallCtor, unless(IsPlacement))
93 .bind(NewExpression)),
94 unless(isInTemplateInstantiation()))
95 .bind(ConstructorCall))))),
99 traverse(ast_type_traits::TK_AsIs,
102 callee(cxxMethodDecl(hasName(
"reset"))),
103 hasArgument(0, cxxNewExpr(CanCallCtor, unless(IsPlacement))
104 .bind(NewExpression)),
105 unless(isInTemplateInstantiation()))
115 SourceManager &SM = *Result.SourceManager;
116 const auto *Construct =
117 Result.Nodes.getNodeAs<CXXConstructExpr>(ConstructorCall);
118 const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(ResetCall);
120 const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(NewExpression);
123 if (New->getType()->getPointeeType()->getContainedAutoType())
133 if (New->isArray() && !New->hasInitializer())
136 checkConstruct(SM, Result.Context, Construct,
Type, New);
138 checkReset(SM, Result.Context, Reset, New);
141 void MakeSmartPtrCheck::checkConstruct(SourceManager &SM, ASTContext *
Ctx,
142 const CXXConstructExpr *Construct,
143 const QualType *
Type,
144 const CXXNewExpr *New) {
145 SourceLocation ConstructCallStart = Construct->getExprLoc();
146 bool InMacro = ConstructCallStart.isMacroID();
148 if (InMacro && IgnoreMacros) {
152 bool Invalid =
false;
153 StringRef ExprStr = Lexer::getSourceText(
154 CharSourceRange::getCharRange(
155 ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
160 auto Diag =
diag(ConstructCallStart,
"use %0 instead")
161 << MakeSmartPtrFunctionName;
168 if (!replaceNew(Diag, New, SM,
Ctx)) {
173 size_t LAngle = ExprStr.find(
"<");
174 SourceLocation ConstructCallEnd;
175 if (LAngle == StringRef::npos) {
178 ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
179 Diag << FixItHint::CreateInsertion(
181 "<" + GetNewExprName(New, SM,
getLangOpts()) +
">");
183 ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
186 Diag << FixItHint::CreateReplacement(
187 CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
188 MakeSmartPtrFunctionName);
192 if (Construct->isListInitialization()) {
193 SourceRange BraceRange = Construct->getParenOrBraceRange();
194 Diag << FixItHint::CreateReplacement(
195 CharSourceRange::getCharRange(
196 BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
198 Diag << FixItHint::CreateReplacement(
199 CharSourceRange::getCharRange(BraceRange.getEnd(),
200 BraceRange.getEnd().getLocWithOffset(1)),
204 insertHeader(Diag, SM.getFileID(ConstructCallStart));
207 void MakeSmartPtrCheck::checkReset(SourceManager &SM, ASTContext *
Ctx,
208 const CXXMemberCallExpr *Reset,
209 const CXXNewExpr *New) {
210 const auto *Expr = cast<MemberExpr>(Reset->getCallee());
211 SourceLocation OperatorLoc = Expr->getOperatorLoc();
212 SourceLocation ResetCallStart = Reset->getExprLoc();
213 SourceLocation ExprStart = Expr->getBeginLoc();
214 SourceLocation ExprEnd =
215 Lexer::getLocForEndOfToken(Expr->getEndLoc(), 0, SM,
getLangOpts());
217 bool InMacro = ExprStart.isMacroID();
219 if (InMacro && IgnoreMacros) {
226 if (OperatorLoc.isInvalid()) {
230 auto Diag =
diag(ResetCallStart,
"use %0 instead")
231 << MakeSmartPtrFunctionName;
238 if (!replaceNew(Diag, New, SM,
Ctx)) {
242 Diag << FixItHint::CreateReplacement(
243 CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
244 (llvm::Twine(
" = ") + MakeSmartPtrFunctionName +
"<" +
249 Diag << FixItHint::CreateInsertion(ExprStart,
"*");
251 insertHeader(Diag, SM.getFileID(OperatorLoc));
254 bool MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
255 const CXXNewExpr *New, SourceManager &SM,
257 auto SkipParensParents = [&](
const Expr *
E) {
258 TraversalKindScope RAII(*
Ctx, ast_type_traits::TK_AsIs);
260 for (
const Expr *OldE =
nullptr;
E != OldE;) {
262 for (
const auto &Node :
Ctx->getParents(*
E)) {
263 if (
const Expr *
Parent = Node.get<ParenExpr>()) {
272 SourceRange NewRange = SkipParensParents(New)->getSourceRange();
273 SourceLocation NewStart = NewRange.getBegin();
274 SourceLocation NewEnd = NewRange.getEnd();
277 if (NewStart.isInvalid() || NewEnd.isInvalid())
280 std::string ArraySizeExpr;
281 if (
const auto* ArraySize = New->getArraySize().getValueOr(
nullptr)) {
282 ArraySizeExpr = Lexer::getSourceText(CharSourceRange::getTokenRange(
283 ArraySize->getSourceRange()),
293 auto HasListIntializedArgument = [](
const CXXConstructExpr *
CE) {
294 for (
const auto *Arg :
CE->arguments()) {
295 Arg = Arg->IgnoreImplicit();
297 if (isa<CXXStdInitializerListExpr>(Arg) || isa<InitListExpr>(Arg))
301 if (
const auto *CEArg = dyn_cast<CXXConstructExpr>(Arg)) {
305 if (CEArg->isElidable()) {
306 if (
const auto *TempExp = CEArg->getArg(0)) {
307 if (
const auto *UnwrappedCE =
308 dyn_cast<CXXConstructExpr>(TempExp->IgnoreImplicit()))
312 if (CEArg->isStdInitListInitialization())
318 switch (New->getInitializationStyle()) {
319 case CXXNewExpr::NoInit: {
320 if (ArraySizeExpr.empty()) {
321 Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
325 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
330 case CXXNewExpr::CallInit: {
348 if (
const auto *
CE = New->getConstructExpr()) {
349 if (HasListIntializedArgument(
CE))
352 if (ArraySizeExpr.empty()) {
353 SourceRange InitRange = New->getDirectInitRange();
354 Diag << FixItHint::CreateRemoval(
355 SourceRange(NewStart, InitRange.getBegin()));
356 Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
362 Diag << FixItHint::CreateReplacement(SourceRange(NewStart, NewEnd),
367 case CXXNewExpr::ListInit: {
369 SourceRange InitRange;
370 if (
const auto *NewConstruct = New->getConstructExpr()) {
371 if (NewConstruct->isStdInitListInitialization() ||
372 HasListIntializedArgument(NewConstruct)) {
395 InitRange = SourceRange(
396 NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
397 NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
409 if (
const CXXRecordDecl *RD = New->getType()->getPointeeCXXRecordDecl()) {
410 if (llvm::find_if(RD->ctors(), [](
const CXXConstructorDecl *Ctor) {
411 return Ctor->isCopyOrMoveConstructor() &&
412 (Ctor->isDeleted() || Ctor->getAccess() == AS_private);
413 }) != RD->ctor_end()) {
417 InitRange = SourceRange(
418 New->getAllocatedTypeSourceInfo()->getTypeLoc().getBeginLoc(),
419 New->getInitializer()->getSourceRange().getEnd());
421 Diag << FixItHint::CreateRemoval(
422 CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
423 Diag << FixItHint::CreateRemoval(
424 SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
431 void MakeSmartPtrCheck::insertHeader(DiagnosticBuilder &Diag, FileID FD) {
432 if (MakeSmartPtrFunctionHeader.empty()) {
435 Diag << Inserter->CreateIncludeInsertion(
436 FD, MakeSmartPtrFunctionHeader,
437 MakeSmartPtrFunctionHeader == StdMemoryHeader);