11 #include "clang/Lex/Lexer.h"
13 using namespace clang::ast_matchers;
19 const char MakeSmartPtrCheck::PointerType[] =
"pointerType";
20 const char MakeSmartPtrCheck::ConstructorCall[] =
"constructorCall";
21 const char MakeSmartPtrCheck::ResetCall[] =
"resetCall";
22 const char MakeSmartPtrCheck::NewExpression[] =
"newExpression";
25 std::string makeSmartPtrFunctionName)
27 makeSmartPtrFunctionName(std::move(makeSmartPtrFunctionName)) {}
35 auto CanCallCtor = unless(has(ignoringImpCasts(
36 cxxConstructExpr(hasDeclaration(decl(unless(isPublic())))))));
39 cxxBindTemporaryExpr(has(ignoringParenImpCasts(
43 cxxNewExpr(hasType(pointsTo(qualType(hasCanonicalType(
53 callee(cxxMethodDecl(hasName(
"reset"))),
64 SourceManager &
SM = *Result.SourceManager;
65 const auto *Construct =
67 const auto *Reset = Result.Nodes.getNodeAs<CXXMemberCallExpr>(
ResetCall);
68 const auto *Type = Result.Nodes.getNodeAs<QualType>(
PointerType);
69 const auto *New = Result.Nodes.getNodeAs<CXXNewExpr>(
NewExpression);
71 if (New->getNumPlacementArgs() != 0)
75 checkConstruct(SM, Construct, Type, New);
77 checkReset(SM, Reset, New);
80 void MakeSmartPtrCheck::checkConstruct(SourceManager &
SM,
81 const CXXConstructExpr *Construct,
83 const CXXNewExpr *New) {
84 SourceLocation ConstructCallStart = Construct->getExprLoc();
87 StringRef ExprStr = Lexer::getSourceText(
88 CharSourceRange::getCharRange(
89 ConstructCallStart, Construct->getParenOrBraceRange().getBegin()),
90 SM, LangOptions(), &Invalid);
94 auto Diag =
diag(ConstructCallStart,
"use %0 instead")
95 << makeSmartPtrFunctionName;
98 size_t LAngle = ExprStr.find(
"<");
99 SourceLocation ConstructCallEnd;
100 if (LAngle == StringRef::npos) {
103 ConstructCallEnd = ConstructCallStart.getLocWithOffset(ExprStr.size());
104 Diag << FixItHint::CreateInsertion(
105 ConstructCallEnd,
"<" + Type->getAsString(
getLangOpts()) +
">");
107 ConstructCallEnd = ConstructCallStart.getLocWithOffset(LAngle);
110 Diag << FixItHint::CreateReplacement(
111 CharSourceRange::getCharRange(ConstructCallStart, ConstructCallEnd),
112 makeSmartPtrFunctionName);
116 if (Construct->isListInitialization()) {
117 SourceRange BraceRange = Construct->getParenOrBraceRange();
118 Diag << FixItHint::CreateReplacement(
119 CharSourceRange::getCharRange(
120 BraceRange.getBegin(), BraceRange.getBegin().getLocWithOffset(1)),
122 Diag << FixItHint::CreateReplacement(
123 CharSourceRange::getCharRange(BraceRange.getEnd(),
124 BraceRange.getEnd().getLocWithOffset(1)),
128 replaceNew(Diag, New);
131 void MakeSmartPtrCheck::checkReset(SourceManager &SM,
132 const CXXMemberCallExpr *Reset,
133 const CXXNewExpr *New) {
134 const auto *Expr = cast<MemberExpr>(Reset->getCallee());
135 SourceLocation OperatorLoc = Expr->getOperatorLoc();
136 SourceLocation ResetCallStart = Reset->getExprLoc();
137 SourceLocation ExprStart = Expr->getLocStart();
138 SourceLocation ExprEnd =
139 Lexer::getLocForEndOfToken(Expr->getLocEnd(), 0,
SM,
getLangOpts());
141 auto Diag =
diag(ResetCallStart,
"use %0 instead")
142 << makeSmartPtrFunctionName;
144 Diag << FixItHint::CreateReplacement(
145 CharSourceRange::getCharRange(OperatorLoc, ExprEnd),
146 (llvm::Twine(
" = ") + makeSmartPtrFunctionName +
"<" +
147 New->getAllocatedType().getAsString(
getLangOpts()) +
">")
151 Diag << FixItHint::CreateInsertion(ExprStart,
"*");
153 replaceNew(Diag, New);
156 void MakeSmartPtrCheck::replaceNew(DiagnosticBuilder &Diag,
157 const CXXNewExpr *New) {
158 SourceLocation NewStart = New->getSourceRange().getBegin();
159 SourceLocation NewEnd = New->getSourceRange().getEnd();
160 switch (New->getInitializationStyle()) {
161 case CXXNewExpr::NoInit: {
162 Diag << FixItHint::CreateRemoval(SourceRange(NewStart, NewEnd));
165 case CXXNewExpr::CallInit: {
166 SourceRange InitRange = New->getDirectInitRange();
167 Diag << FixItHint::CreateRemoval(
168 SourceRange(NewStart, InitRange.getBegin()));
169 Diag << FixItHint::CreateRemoval(SourceRange(InitRange.getEnd(), NewEnd));
172 case CXXNewExpr::ListInit: {
174 SourceRange InitRange;
175 if (
const auto *NewConstruct = New->getConstructExpr()) {
183 InitRange = SourceRange(
184 NewConstruct->getParenOrBraceRange().getBegin().getLocWithOffset(1),
185 NewConstruct->getParenOrBraceRange().getEnd().getLocWithOffset(-1));
191 InitRange = SourceRange(
192 New->getAllocatedTypeSourceInfo()->getTypeLoc().getLocStart(),
193 New->getInitializer()->getSourceRange().getEnd());
195 Diag << FixItHint::CreateRemoval(
196 CharSourceRange::getCharRange(NewStart, InitRange.getBegin()));
197 Diag << FixItHint::CreateRemoval(
198 SourceRange(InitRange.getEnd().getLocWithOffset(1), NewEnd));
LangOptions getLangOpts() const
Returns the language options from the context.
std::unique_ptr< ast_matchers::MatchFinder > Finder
void registerMatchers(ast_matchers::MatchFinder *Finder) final
Override this to register AST matchers with Finder.
Base class for all clang-tidy checks.
virtual SmartPtrTypeMatcher getSmartPointerTypeMatcher() const =0
Returns matcher that match with different smart pointer types.
static const char ConstructorCall[]
void check(const ast_matchers::MatchFinder::MatchResult &Result) final
ClangTidyChecks that register ASTMatchers should do the actual work in here.
static const char PointerType[]
ClangTidyContext & Context
Every ClangTidyCheck reports errors through a DiagnosticsEngine provided by this context.
static const char NewExpression[]
static const char ResetCall[]
DiagnosticBuilder diag(SourceLocation Loc, StringRef Description, DiagnosticIDs::Level Level=DiagnosticIDs::Warning)
Add a diagnostic with the check's name.