1 //===---------- ExprMutationAnalyzer.cpp ----------------------------------===// 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 #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h" 10 #include "clang/ASTMatchers/ASTMatchFinder.h" 11 #include "llvm/ADT/STLExtras.h" 12 13 namespace clang { 14 using namespace ast_matchers; 15 16 namespace { 17 18 AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) { 19 return llvm::is_contained(Node.capture_inits(), E); 20 } 21 22 AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt, 23 ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) { 24 const DeclStmt *const Range = Node.getRangeStmt(); 25 return InnerMatcher.matches(*Range, Finder, Builder); 26 } 27 28 const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr> 29 cxxTypeidExpr; 30 31 AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) { 32 return Node.isPotentiallyEvaluated(); 33 } 34 35 const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXNoexceptExpr> 36 cxxNoexceptExpr; 37 38 const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, 39 GenericSelectionExpr> 40 genericSelectionExpr; 41 42 AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr, 43 ast_matchers::internal::Matcher<Expr>, InnerMatcher) { 44 return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder); 45 } 46 47 const auto nonConstReferenceType = [] { 48 return hasUnqualifiedDesugaredType( 49 referenceType(pointee(unless(isConstQualified())))); 50 }; 51 52 const auto nonConstPointerType = [] { 53 return hasUnqualifiedDesugaredType( 54 pointerType(pointee(unless(isConstQualified())))); 55 }; 56 57 const auto isMoveOnly = [] { 58 return cxxRecordDecl( 59 hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))), 60 hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))), 61 unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(), 62 unless(isDeleted()))), 63 hasMethod(cxxMethodDecl(isCopyAssignmentOperator(), 64 unless(isDeleted())))))); 65 }; 66 67 } // namespace 68 69 const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) { 70 const auto Memoized = Results.find(Exp); 71 if (Memoized != Results.end()) 72 return Memoized->second; 73 74 if (isUnevaluated(Exp)) 75 return Results[Exp] = nullptr; 76 77 for (const auto &Finder : {&ExprMutationAnalyzer::findDirectMutation, 78 &ExprMutationAnalyzer::findMemberMutation, 79 &ExprMutationAnalyzer::findArrayElementMutation, 80 &ExprMutationAnalyzer::findCastMutation, 81 &ExprMutationAnalyzer::findRangeLoopMutation, 82 &ExprMutationAnalyzer::findReferenceMutation, 83 &ExprMutationAnalyzer::findFunctionArgMutation}) { 84 if (const Stmt *S = (this->*Finder)(Exp)) 85 return Results[Exp] = S; 86 } 87 88 return Results[Exp] = nullptr; 89 } 90 91 bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) { 92 return selectFirst<Expr>( 93 "expr", 94 match( 95 findAll( 96 expr(equalsNode(Exp), 97 anyOf( 98 // `Exp` is part of the underlying expression of 99 // decltype/typeof if it has an ancestor of 100 // typeLoc. 101 hasAncestor(typeLoc(unless( 102 hasAncestor(unaryExprOrTypeTraitExpr())))), 103 hasAncestor(expr(anyOf( 104 // `UnaryExprOrTypeTraitExpr` is unevaluated 105 // unless it's sizeof on VLA. 106 unaryExprOrTypeTraitExpr(unless(sizeOfExpr( 107 hasArgumentOfType(variableArrayType())))), 108 // `CXXTypeidExpr` is unevaluated unless it's 109 // applied to an expression of glvalue of 110 // polymorphic class type. 111 cxxTypeidExpr( 112 unless(isPotentiallyEvaluated())), 113 // The controlling expression of 114 // `GenericSelectionExpr` is unevaluated. 115 genericSelectionExpr(hasControllingExpr( 116 hasDescendant(equalsNode(Exp)))), 117 cxxNoexceptExpr()))))) 118 .bind("expr")), 119 Stm, Context)) != nullptr; 120 } 121 122 const Stmt * 123 ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) { 124 for (const auto &Nodes : Matches) { 125 if (const Stmt *S = findMutation(Nodes.getNodeAs<Expr>("expr"))) 126 return S; 127 } 128 return nullptr; 129 } 130 131 const Stmt * 132 ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) { 133 for (const auto &DeclNodes : Matches) { 134 if (const Stmt *S = findDeclMutation(DeclNodes.getNodeAs<Decl>("decl"))) 135 return S; 136 } 137 return nullptr; 138 } 139 140 const Stmt *ExprMutationAnalyzer::findDeclMutation(const Decl *Dec) { 141 const auto Refs = match( 142 findAll(declRefExpr(to(equalsNode(Dec))).bind("expr")), Stm, Context); 143 for (const auto &RefNodes : Refs) { 144 const auto *E = RefNodes.getNodeAs<Expr>("expr"); 145 if (findMutation(E)) 146 return E; 147 } 148 return nullptr; 149 } 150 151 const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { 152 // LHS of any assignment operators. 153 const auto AsAssignmentLhs = 154 binaryOperator(isAssignmentOperator(), hasLHS(equalsNode(Exp))); 155 156 // Operand of increment/decrement operators. 157 const auto AsIncDecOperand = 158 unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")), 159 hasUnaryOperand(equalsNode(Exp))); 160 161 // Invoking non-const member function. 162 // A member function is assumed to be non-const when it is unresolved. 163 const auto NonConstMethod = cxxMethodDecl(unless(isConst())); 164 const auto AsNonConstThis = 165 expr(anyOf(cxxMemberCallExpr(callee(NonConstMethod), on(equalsNode(Exp))), 166 cxxOperatorCallExpr(callee(NonConstMethod), 167 hasArgument(0, equalsNode(Exp))), 168 callExpr(callee(expr(anyOf( 169 unresolvedMemberExpr(hasObjectExpression(equalsNode(Exp))), 170 cxxDependentScopeMemberExpr( 171 hasObjectExpression(equalsNode(Exp))))))))); 172 173 // Taking address of 'Exp'. 174 // We're assuming 'Exp' is mutated as soon as its address is taken, though in 175 // theory we can follow the pointer and see whether it escaped `Stm` or is 176 // dereferenced and then mutated. This is left for future improvements. 177 const auto AsAmpersandOperand = 178 unaryOperator(hasOperatorName("&"), 179 // A NoOp implicit cast is adding const. 180 unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))), 181 hasUnaryOperand(equalsNode(Exp))); 182 const auto AsPointerFromArrayDecay = 183 castExpr(hasCastKind(CK_ArrayToPointerDecay), 184 unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp))); 185 // Treat calling `operator->()` of move-only classes as taking address. 186 // These are typically smart pointers with unique ownership so we treat 187 // mutation of pointee as mutation of the smart pointer itself. 188 const auto AsOperatorArrowThis = 189 cxxOperatorCallExpr(hasOverloadedOperatorName("->"), 190 callee(cxxMethodDecl(ofClass(isMoveOnly()), 191 returns(nonConstPointerType()))), 192 argumentCountIs(1), hasArgument(0, equalsNode(Exp))); 193 194 // Used as non-const-ref argument when calling a function. 195 // An argument is assumed to be non-const-ref when the function is unresolved. 196 // Instantiated template functions are not handled here but in 197 // findFunctionArgMutation which has additional smarts for handling forwarding 198 // references. 199 const auto NonConstRefParam = forEachArgumentWithParam( 200 equalsNode(Exp), parmVarDecl(hasType(nonConstReferenceType()))); 201 const auto NotInstantiated = unless(hasDeclaration(isInstantiated())); 202 const auto AsNonConstRefArg = anyOf( 203 callExpr(NonConstRefParam, NotInstantiated), 204 cxxConstructExpr(NonConstRefParam, NotInstantiated), 205 callExpr(callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(), 206 cxxDependentScopeMemberExpr(), 207 hasType(templateTypeParmType())))), 208 hasAnyArgument(equalsNode(Exp))), 209 cxxUnresolvedConstructExpr(hasAnyArgument(equalsNode(Exp)))); 210 211 // Captured by a lambda by reference. 212 // If we're initializing a capture with 'Exp' directly then we're initializing 213 // a reference capture. 214 // For value captures there will be an ImplicitCastExpr <LValueToRValue>. 215 const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp)); 216 217 // Returned as non-const-ref. 218 // If we're returning 'Exp' directly then it's returned as non-const-ref. 219 // For returning by value there will be an ImplicitCastExpr <LValueToRValue>. 220 // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for 221 // adding const.) 222 const auto AsNonConstRefReturn = returnStmt(hasReturnValue(equalsNode(Exp))); 223 224 const auto Matches = 225 match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis, 226 AsAmpersandOperand, AsPointerFromArrayDecay, 227 AsOperatorArrowThis, AsNonConstRefArg, 228 AsLambdaRefCaptureInit, AsNonConstRefReturn)) 229 .bind("stmt")), 230 Stm, Context); 231 return selectFirst<Stmt>("stmt", Matches); 232 } 233 234 const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) { 235 // Check whether any member of 'Exp' is mutated. 236 const auto MemberExprs = 237 match(findAll(expr(anyOf(memberExpr(hasObjectExpression(equalsNode(Exp))), 238 cxxDependentScopeMemberExpr( 239 hasObjectExpression(equalsNode(Exp))))) 240 .bind("expr")), 241 Stm, Context); 242 return findExprMutation(MemberExprs); 243 } 244 245 const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) { 246 // Check whether any element of an array is mutated. 247 const auto SubscriptExprs = match( 248 findAll(arraySubscriptExpr(hasBase(ignoringImpCasts(equalsNode(Exp)))) 249 .bind("expr")), 250 Stm, Context); 251 return findExprMutation(SubscriptExprs); 252 } 253 254 const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) { 255 // If 'Exp' is casted to any non-const reference type, check the castExpr. 256 const auto Casts = 257 match(findAll(castExpr(hasSourceExpression(equalsNode(Exp)), 258 anyOf(explicitCastExpr(hasDestinationType( 259 nonConstReferenceType())), 260 implicitCastExpr(hasImplicitDestinationType( 261 nonConstReferenceType())))) 262 .bind("expr")), 263 Stm, Context); 264 return findExprMutation(Casts); 265 } 266 267 const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) { 268 // If range for looping over 'Exp' with a non-const reference loop variable, 269 // check all declRefExpr of the loop variable. 270 const auto LoopVars = 271 match(findAll(cxxForRangeStmt( 272 hasLoopVariable( 273 varDecl(hasType(nonConstReferenceType())).bind("decl")), 274 hasRangeInit(equalsNode(Exp)))), 275 Stm, Context); 276 return findDeclMutation(LoopVars); 277 } 278 279 const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) { 280 // Follow non-const reference returned by `operator*()` of move-only classes. 281 // These are typically smart pointers with unique ownership so we treat 282 // mutation of pointee as mutation of the smart pointer itself. 283 const auto Ref = match( 284 findAll(cxxOperatorCallExpr( 285 hasOverloadedOperatorName("*"), 286 callee(cxxMethodDecl(ofClass(isMoveOnly()), 287 returns(hasUnqualifiedDesugaredType( 288 nonConstReferenceType())))), 289 argumentCountIs(1), hasArgument(0, equalsNode(Exp))) 290 .bind("expr")), 291 Stm, Context); 292 if (const Stmt *S = findExprMutation(Ref)) 293 return S; 294 295 // If 'Exp' is bound to a non-const reference, check all declRefExpr to that. 296 const auto Refs = match( 297 stmt(forEachDescendant( 298 varDecl( 299 hasType(nonConstReferenceType()), 300 hasInitializer(anyOf(equalsNode(Exp), 301 conditionalOperator(anyOf( 302 hasTrueExpression(equalsNode(Exp)), 303 hasFalseExpression(equalsNode(Exp)))))), 304 hasParent(declStmt().bind("stmt")), 305 // Don't follow the reference in range statement, we've handled 306 // that separately. 307 unless(hasParent(declStmt(hasParent( 308 cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt")))))))) 309 .bind("decl"))), 310 Stm, Context); 311 return findDeclMutation(Refs); 312 } 313 314 const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) { 315 const auto NonConstRefParam = forEachArgumentWithParam( 316 equalsNode(Exp), 317 parmVarDecl(hasType(nonConstReferenceType())).bind("parm")); 318 const auto IsInstantiated = hasDeclaration(isInstantiated()); 319 const auto FuncDecl = hasDeclaration(functionDecl().bind("func")); 320 const auto Matches = match( 321 findAll(expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl), 322 cxxConstructExpr(NonConstRefParam, IsInstantiated, 323 FuncDecl))) 324 .bind("expr")), 325 Stm, Context); 326 for (const auto &Nodes : Matches) { 327 const auto *Exp = Nodes.getNodeAs<Expr>("expr"); 328 const auto *Func = Nodes.getNodeAs<FunctionDecl>("func"); 329 if (!Func->getBody()) 330 return Exp; 331 332 const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm"); 333 const ArrayRef<ParmVarDecl *> AllParams = 334 Func->getPrimaryTemplate()->getTemplatedDecl()->parameters(); 335 QualType ParmType = 336 AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(), 337 AllParams.size() - 1)] 338 ->getType(); 339 if (const auto *T = ParmType->getAs<PackExpansionType>()) 340 ParmType = T->getPattern(); 341 342 // If param type is forwarding reference, follow into the function 343 // definition and see whether the param is mutated inside. 344 if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) { 345 if (!RefType->getPointeeType().getQualifiers() && 346 RefType->getPointeeType()->getAs<TemplateTypeParmType>()) { 347 std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer = 348 FuncParmAnalyzer[Func]; 349 if (!Analyzer) 350 Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context)); 351 if (Analyzer->findMutation(Parm)) 352 return Exp; 353 continue; 354 } 355 } 356 // Not forwarding reference. 357 return Exp; 358 } 359 return nullptr; 360 } 361 362 FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer( 363 const FunctionDecl &Func, ASTContext &Context) 364 : BodyAnalyzer(*Func.getBody(), Context) { 365 if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) { 366 // CXXCtorInitializer might also mutate Param but they're not part of 367 // function body, check them eagerly here since they're typically trivial. 368 for (const CXXCtorInitializer *Init : Ctor->inits()) { 369 ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context); 370 for (const ParmVarDecl *Parm : Ctor->parameters()) { 371 if (Results.find(Parm) != Results.end()) 372 continue; 373 if (const Stmt *S = InitAnalyzer.findDeclMutation(Parm)) 374 Results[Parm] = S; 375 } 376 } 377 } 378 } 379 380 const Stmt * 381 FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) { 382 const auto Memoized = Results.find(Parm); 383 if (Memoized != Results.end()) 384 return Memoized->second; 385 386 if (const Stmt *S = BodyAnalyzer.findDeclMutation(Parm)) 387 return Results[Parm] = S; 388 389 return Results[Parm] = nullptr; 390 } 391 392 } // namespace clang 393