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