1 //===--- DeclRefExprUtils.cpp - clang-tidy---------------------------------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 10 #include "DeclRefExprUtils.h" 11 #include "Matchers.h" 12 #include "clang/AST/ASTContext.h" 13 #include "clang/AST/DeclCXX.h" 14 #include "clang/ASTMatchers/ASTMatchFinder.h" 15 16 namespace clang { 17 namespace tidy { 18 namespace utils { 19 namespace decl_ref_expr { 20 21 using namespace ::clang::ast_matchers; 22 using llvm::SmallPtrSet; 23 24 namespace { 25 26 template <typename S> bool isSetDifferenceEmpty(const S &S1, const S &S2) { 27 for (const auto &E : S1) 28 if (S2.count(E) == 0) 29 return false; 30 return true; 31 } 32 33 // Extracts all Nodes keyed by ID from Matches and inserts them into Nodes. 34 template <typename Node> 35 void extractNodesByIdTo(ArrayRef<BoundNodes> Matches, StringRef ID, 36 SmallPtrSet<const Node *, 16> &Nodes) { 37 for (const auto &Match : Matches) 38 Nodes.insert(Match.getNodeAs<Node>(ID)); 39 } 40 41 } // namespace 42 43 // Finds all DeclRefExprs where a const method is called on VarDecl or VarDecl 44 // is the a const reference or value argument to a CallExpr or CXXConstructExpr. 45 SmallPtrSet<const DeclRefExpr *, 16> 46 constReferenceDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, 47 ASTContext &Context) { 48 auto DeclRefToVar = 49 declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef"); 50 auto ConstMethodCallee = callee(cxxMethodDecl(isConst())); 51 // Match method call expressions where the variable is referenced as the this 52 // implicit object argument and opertor call expression for member operators 53 // where the variable is the 0-th argument. 54 auto Matches = match( 55 findAll(expr(anyOf(cxxMemberCallExpr(ConstMethodCallee, on(DeclRefToVar)), 56 cxxOperatorCallExpr(ConstMethodCallee, 57 hasArgument(0, DeclRefToVar))))), 58 Stmt, Context); 59 SmallPtrSet<const DeclRefExpr *, 16> DeclRefs; 60 extractNodesByIdTo(Matches, "declRef", DeclRefs); 61 auto ConstReferenceOrValue = 62 qualType(anyOf(referenceType(pointee(qualType(isConstQualified()))), 63 unless(anyOf(referenceType(), pointerType())))); 64 auto UsedAsConstRefOrValueArg = forEachArgumentWithParam( 65 DeclRefToVar, parmVarDecl(hasType(ConstReferenceOrValue))); 66 Matches = match(findAll(callExpr(UsedAsConstRefOrValueArg)), Stmt, Context); 67 extractNodesByIdTo(Matches, "declRef", DeclRefs); 68 Matches = 69 match(findAll(cxxConstructExpr(UsedAsConstRefOrValueArg)), Stmt, Context); 70 extractNodesByIdTo(Matches, "declRef", DeclRefs); 71 return DeclRefs; 72 } 73 74 // Finds all DeclRefExprs where a const method is called on VarDecl or VarDecl 75 // is the a const reference or value argument to a CallExpr or CXXConstructExpr. 76 SmallPtrSet<const DeclRefExpr *, 16> 77 constReferenceDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl, 78 ASTContext &Context) { 79 auto DeclRefToVar = 80 declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef"); 81 auto ConstMethodCallee = callee(cxxMethodDecl(isConst())); 82 // Match method call expressions where the variable is referenced as the this 83 // implicit object argument and opertor call expression for member operators 84 // where the variable is the 0-th argument. 85 auto Matches = 86 match(decl(forEachDescendant(expr( 87 anyOf(cxxMemberCallExpr(ConstMethodCallee, on(DeclRefToVar)), 88 cxxOperatorCallExpr(ConstMethodCallee, 89 hasArgument(0, DeclRefToVar)))))), 90 Decl, Context); 91 SmallPtrSet<const DeclRefExpr *, 16> DeclRefs; 92 extractNodesByIdTo(Matches, "declRef", DeclRefs); 93 auto ConstReferenceOrValue = 94 qualType(anyOf(referenceType(pointee(qualType(isConstQualified()))), 95 unless(anyOf(referenceType(), pointerType())))); 96 auto UsedAsConstRefOrValueArg = forEachArgumentWithParam( 97 DeclRefToVar, parmVarDecl(hasType(ConstReferenceOrValue))); 98 Matches = match(decl(forEachDescendant(callExpr(UsedAsConstRefOrValueArg))), 99 Decl, Context); 100 extractNodesByIdTo(Matches, "declRef", DeclRefs); 101 Matches = 102 match(decl(forEachDescendant(cxxConstructExpr(UsedAsConstRefOrValueArg))), 103 Decl, Context); 104 extractNodesByIdTo(Matches, "declRef", DeclRefs); 105 return DeclRefs; 106 } 107 108 bool isOnlyUsedAsConst(const VarDecl &Var, const Stmt &Stmt, 109 ASTContext &Context) { 110 // Collect all DeclRefExprs to the loop variable and all CallExprs and 111 // CXXConstructExprs where the loop variable is used as argument to a const 112 // reference parameter. 113 // If the difference is empty it is safe for the loop variable to be a const 114 // reference. 115 auto AllDeclRefs = allDeclRefExprs(Var, Stmt, Context); 116 auto ConstReferenceDeclRefs = constReferenceDeclRefExprs(Var, Stmt, Context); 117 return isSetDifferenceEmpty(AllDeclRefs, ConstReferenceDeclRefs); 118 } 119 120 SmallPtrSet<const DeclRefExpr *, 16> 121 allDeclRefExprs(const VarDecl &VarDecl, const Stmt &Stmt, ASTContext &Context) { 122 auto Matches = match( 123 findAll(declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef")), 124 Stmt, Context); 125 SmallPtrSet<const DeclRefExpr *, 16> DeclRefs; 126 extractNodesByIdTo(Matches, "declRef", DeclRefs); 127 return DeclRefs; 128 } 129 130 SmallPtrSet<const DeclRefExpr *, 16> 131 allDeclRefExprs(const VarDecl &VarDecl, const Decl &Decl, ASTContext &Context) { 132 auto Matches = match( 133 decl(forEachDescendant( 134 declRefExpr(to(varDecl(equalsNode(&VarDecl)))).bind("declRef"))), 135 Decl, Context); 136 SmallPtrSet<const DeclRefExpr *, 16> DeclRefs; 137 extractNodesByIdTo(Matches, "declRef", DeclRefs); 138 return DeclRefs; 139 } 140 141 bool isCopyConstructorArgument(const DeclRefExpr &DeclRef, const Decl &Decl, 142 ASTContext &Context) { 143 auto UsedAsConstRefArg = forEachArgumentWithParam( 144 declRefExpr(equalsNode(&DeclRef)), 145 parmVarDecl(hasType(matchers::isReferenceToConst()))); 146 auto Matches = match( 147 decl(hasDescendant( 148 cxxConstructExpr(UsedAsConstRefArg, hasDeclaration(cxxConstructorDecl( 149 isCopyConstructor()))) 150 .bind("constructExpr"))), 151 Decl, Context); 152 return !Matches.empty(); 153 } 154 155 bool isCopyAssignmentArgument(const DeclRefExpr &DeclRef, const Decl &Decl, 156 ASTContext &Context) { 157 auto UsedAsConstRefArg = forEachArgumentWithParam( 158 declRefExpr(equalsNode(&DeclRef)), 159 parmVarDecl(hasType(matchers::isReferenceToConst()))); 160 auto Matches = match( 161 decl(hasDescendant( 162 cxxOperatorCallExpr(UsedAsConstRefArg, hasOverloadedOperatorName("="), 163 callee(cxxMethodDecl(isCopyAssignmentOperator()))) 164 .bind("operatorCallExpr"))), 165 Decl, Context); 166 return !Matches.empty(); 167 } 168 169 } // namespace decl_ref_expr 170 } // namespace utils 171 } // namespace tidy 172 } // namespace clang 173