brintos

brintos / llvm-project-archived public Read only

0
0
Text · 7.7 KiB · a458ae3 Raw
189 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "RedundantSmartptrGetCheck.h"10#include "clang/ASTMatchers/ASTMatchFinder.h"11#include "clang/Lex/Lexer.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::readability {16 17static internal::Matcher<Expr>18callToGet(const internal::Matcher<Decl> &OnClass) {19  return expr(20             anyOf(cxxMemberCallExpr(21                       on(expr(anyOf(hasType(OnClass),22                                     hasType(qualType(pointsTo(23                                         decl(OnClass).bind("ptr_to_ptr"))))))24                              .bind("smart_pointer")),25                       unless(callee(26                           memberExpr(hasObjectExpression(cxxThisExpr())))),27                       callee(cxxMethodDecl(hasName("get"),28                                            returns(qualType(pointsTo(29                                                type().bind("getType"))))))),30                   cxxDependentScopeMemberExpr(31                       hasMemberName("get"),32                       hasObjectExpression(33                           expr(hasType(qualType(hasCanonicalType(34                                    templateSpecializationType(hasDeclaration(35                                        classTemplateDecl(has(cxxRecordDecl(36                                            OnClass,37                                            hasMethod(cxxMethodDecl(38                                                hasName("get"),39                                                returns(qualType(40                                                    pointsTo(type().bind(41                                                        "getType")))))))))))))))42                               .bind("smart_pointer")))))43      .bind("redundant_get");44}45 46static internal::Matcher<Decl> knownSmartptr() {47  return recordDecl(hasAnyName("::std::unique_ptr", "::std::shared_ptr"));48}49 50static void51registerMatchersForGetArrowStart(MatchFinder *Finder,52                                 MatchFinder::MatchCallback *Callback) {53  const auto MatchesOpArrow =54      allOf(hasName("operator->"),55            returns(qualType(pointsTo(type().bind("op->Type")))));56  const auto MatchesOpStar =57      allOf(hasName("operator*"),58            returns(qualType(references(type().bind("op*Type")))));59  const auto HasRelevantOps =60      allOf(anyOf(hasMethod(MatchesOpArrow),61                  has(functionTemplateDecl(has(functionDecl(MatchesOpArrow))))),62            anyOf(hasMethod(MatchesOpStar),63                  has(functionTemplateDecl(has(functionDecl(MatchesOpStar))))));64 65  const auto QuacksLikeASmartptr =66      cxxRecordDecl(cxxRecordDecl().bind("duck_typing"), HasRelevantOps);67 68  // Make sure we are not missing the known standard types.69  const auto SmartptrAny = anyOf(knownSmartptr(), QuacksLikeASmartptr);70  const auto SmartptrWithDeref = anyOf(71      cxxRecordDecl(knownSmartptr(), HasRelevantOps), QuacksLikeASmartptr);72 73  // Catch 'ptr.get()->Foo()'74  Finder->addMatcher(75      memberExpr(expr().bind("memberExpr"), isArrow(),76                 hasObjectExpression(callToGet(SmartptrWithDeref))),77      Callback);78 79  // Catch '*ptr.get()' or '*ptr->get()'80  Finder->addMatcher(81      unaryOperator(hasOperatorName("*"),82                    hasUnaryOperand(callToGet(SmartptrWithDeref))),83      Callback);84 85  // Catch '!ptr.get()'86  const auto CallToGetAsBool = callToGet(87      recordDecl(SmartptrAny, has(cxxConversionDecl(returns(booleanType())))));88  Finder->addMatcher(89      unaryOperator(hasOperatorName("!"), hasUnaryOperand(CallToGetAsBool)),90      Callback);91 92  // Catch 'if(ptr.get())'93  Finder->addMatcher(ifStmt(hasCondition(CallToGetAsBool)), Callback);94 95  // Catch 'ptr.get() ? X : Y'96  Finder->addMatcher(conditionalOperator(hasCondition(CallToGetAsBool)),97                     Callback);98 99  Finder->addMatcher(cxxDependentScopeMemberExpr(hasObjectExpression(100                         callExpr(has(callToGet(SmartptrAny))))),101                     Callback);102}103 104static void registerMatchersForGetEquals(MatchFinder *Finder,105                                         MatchFinder::MatchCallback *Callback) {106  // This one is harder to do with duck typing.107  // The operator==/!= that we are looking for might be member or non-member,108  // might be on global namespace or found by ADL, might be a template, etc.109  // For now, lets keep it to the known standard types.110 111  // Matches against nullptr.112  Finder->addMatcher(113      binaryOperator(hasAnyOperatorName("==", "!="),114                     hasOperands(anyOf(cxxNullPtrLiteralExpr(), gnuNullExpr(),115                                       integerLiteral(equals(0))),116                                 callToGet(knownSmartptr()))),117      Callback);118 119  // FIXME: Match and fix if (l.get() == r.get()).120}121 122void RedundantSmartptrGetCheck::storeOptions(123    ClangTidyOptions::OptionMap &Opts) {124  Options.store(Opts, "IgnoreMacros", IgnoreMacros);125}126 127void RedundantSmartptrGetCheck::registerMatchers(MatchFinder *Finder) {128  registerMatchersForGetArrowStart(Finder, this);129  registerMatchersForGetEquals(Finder, this);130}131 132static bool allReturnTypesMatch(const MatchFinder::MatchResult &Result) {133  if (Result.Nodes.getNodeAs<Decl>("duck_typing") == nullptr)134    return true;135  // Verify that the types match.136  // We can't do this on the matcher because the type nodes can be different,137  // even though they represent the same type. This difference comes from how138  // the type is referenced (eg. through a typedef, a type trait, etc).139  const Type *OpArrowType =140      Result.Nodes.getNodeAs<Type>("op->Type")->getUnqualifiedDesugaredType();141  const Type *OpStarType =142      Result.Nodes.getNodeAs<Type>("op*Type")->getUnqualifiedDesugaredType();143  const Type *GetType =144      Result.Nodes.getNodeAs<Type>("getType")->getUnqualifiedDesugaredType();145  return OpArrowType == OpStarType && OpArrowType == GetType;146}147 148void RedundantSmartptrGetCheck::check(const MatchFinder::MatchResult &Result) {149  if (!allReturnTypesMatch(Result))150    return;151 152  const bool IsPtrToPtr = Result.Nodes.getNodeAs<Decl>("ptr_to_ptr") != nullptr;153  const bool IsMemberExpr =154      Result.Nodes.getNodeAs<Expr>("memberExpr") != nullptr;155  const auto *GetCall = Result.Nodes.getNodeAs<Expr>("redundant_get");156  if (GetCall->getBeginLoc().isMacroID() && IgnoreMacros)157    return;158 159  const auto *Smartptr = Result.Nodes.getNodeAs<Expr>("smart_pointer");160 161  if (IsPtrToPtr && IsMemberExpr) {162    // Ignore this case (eg. Foo->get()->DoSomething());163    return;164  }165 166  auto SR = GetCall->getSourceRange();167  // CXXDependentScopeMemberExpr source range does not include parens168  // Extend the source range of the get call to account for them.169  if (isa<CXXDependentScopeMemberExpr>(GetCall))170    SR.setEnd(Lexer::getLocForEndOfToken(SR.getEnd(), 0, *Result.SourceManager,171                                         getLangOpts())172                  .getLocWithOffset(1));173 174  StringRef SmartptrText = Lexer::getSourceText(175      CharSourceRange::getTokenRange(Smartptr->getSourceRange()),176      *Result.SourceManager, getLangOpts());177  // Check if the last two characters are "->" and remove them178  if (SmartptrText.ends_with("->")) {179    SmartptrText = SmartptrText.drop_back(2);180  }181  // Replace foo->get() with *foo, and foo.get() with foo.182  const std::string Replacement =183      Twine(IsPtrToPtr ? "*" : "", SmartptrText).str();184  diag(GetCall->getBeginLoc(), "redundant get() call on smart pointer")185      << FixItHint::CreateReplacement(SR, Replacement);186}187 188} // namespace clang::tidy::readability189