10 #include "clang/ASTMatchers/ASTMatchFinder.h"
11 #include "clang/Lex/Lexer.h"
17 namespace readability {
20 internal::Matcher<Expr> callToGet(
const internal::Matcher<Decl> &OnClass) {
21 return cxxMemberCallExpr(
22 on(expr(anyOf(hasType(OnClass),
24 pointsTo(decl(OnClass).bind(
"ptr_to_ptr"))))))
25 .bind(
"smart_pointer")),
26 unless(callee(memberExpr(hasObjectExpression(cxxThisExpr())))),
29 returns(qualType(pointsTo(type().bind(
"getType")))))))
30 .bind(
"redundant_get");
33 internal::Matcher<Decl> knownSmartptr() {
34 return recordDecl(hasAnyName(
"::std::unique_ptr",
"::std::shared_ptr"));
37 void registerMatchersForGetArrowStart(MatchFinder *Finder,
38 MatchFinder::MatchCallback *
Callback) {
39 const auto QuacksLikeASmartptr = recordDecl(
40 recordDecl().bind(
"duck_typing"),
41 has(cxxMethodDecl(hasName(
"operator->"),
42 returns(qualType(pointsTo(type().bind(
"op->Type")))))),
43 has(cxxMethodDecl(hasName(
"operator*"), returns(qualType(references(
44 type().bind(
"op*Type")))))));
47 const auto Smartptr = anyOf(knownSmartptr(), QuacksLikeASmartptr);
51 memberExpr(expr().bind(
"memberExpr"), isArrow(),
52 hasObjectExpression(ignoringImpCasts(callToGet(Smartptr)))),
57 unaryOperator(hasOperatorName(
"*"), hasUnaryOperand(callToGet(Smartptr))),
61 const auto CallToGetAsBool = ignoringParenImpCasts(callToGet(
62 recordDecl(Smartptr, has(cxxConversionDecl(returns(booleanType()))))));
64 unaryOperator(hasOperatorName(
"!"), hasUnaryOperand(CallToGetAsBool)),
68 Finder->addMatcher(ifStmt(hasCondition(CallToGetAsBool)),
Callback);
71 Finder->addMatcher(conditionalOperator(hasCondition(CallToGetAsBool)),
75 void registerMatchersForGetEquals(MatchFinder *Finder,
76 MatchFinder::MatchCallback *
Callback) {
84 binaryOperator(hasAnyOperatorName(
"==",
"!="),
85 hasOperands(ignoringImpCasts(anyOf(
86 cxxNullPtrLiteralExpr(), gnuNullExpr(),
87 integerLiteral(equals(0)))),
88 callToGet(knownSmartptr()))),
96 void RedundantSmartptrGetCheck::storeOptions(
98 Options.store(Opts,
"IgnoreMacros", IgnoreMacros);
101 void RedundantSmartptrGetCheck::registerMatchers(MatchFinder *Finder) {
102 registerMatchersForGetArrowStart(Finder,
this);
103 registerMatchersForGetEquals(Finder,
this);
107 bool allReturnTypesMatch(
const MatchFinder::MatchResult &Result) {
108 if (Result.Nodes.getNodeAs<
Decl>(
"duck_typing") ==
nullptr)
114 const Type *OpArrowType =
115 Result.Nodes.getNodeAs<
Type>(
"op->Type")->getUnqualifiedDesugaredType();
116 const Type *OpStarType =
117 Result.Nodes.getNodeAs<
Type>(
"op*Type")->getUnqualifiedDesugaredType();
118 const Type *GetType =
119 Result.Nodes.getNodeAs<
Type>(
"getType")->getUnqualifiedDesugaredType();
120 return OpArrowType == OpStarType && OpArrowType == GetType;
124 void RedundantSmartptrGetCheck::check(
const MatchFinder::MatchResult &Result) {
125 if (!allReturnTypesMatch(Result))
128 bool IsPtrToPtr = Result.Nodes.getNodeAs<
Decl>(
"ptr_to_ptr") !=
nullptr;
129 bool IsMemberExpr = Result.Nodes.getNodeAs<Expr>(
"memberExpr") !=
nullptr;
130 const auto *GetCall = Result.Nodes.getNodeAs<Expr>(
"redundant_get");
131 if (GetCall->getBeginLoc().isMacroID() && IgnoreMacros)
134 const auto *Smartptr = Result.Nodes.getNodeAs<Expr>(
"smart_pointer");
136 if (IsPtrToPtr && IsMemberExpr) {
141 StringRef SmartptrText = Lexer::getSourceText(
142 CharSourceRange::getTokenRange(Smartptr->getSourceRange()),
143 *Result.SourceManager, getLangOpts());
145 std::string Replacement = Twine(IsPtrToPtr ?
"*" :
"", SmartptrText).str();
146 diag(GetCall->getBeginLoc(),
"redundant get() call on smart pointer")
147 << FixItHint::CreateReplacement(GetCall->getSourceRange(), Replacement);