1 1.1 joerg //===---------- ExprMutationAnalyzer.cpp ----------------------------------===// 2 1.1 joerg // 3 1.1 joerg // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 1.1 joerg // See https://llvm.org/LICENSE.txt for license information. 5 1.1 joerg // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 1.1 joerg // 7 1.1 joerg //===----------------------------------------------------------------------===// 8 1.1 joerg #include "clang/Analysis/Analyses/ExprMutationAnalyzer.h" 9 1.1.1.2 joerg #include "clang/AST/Expr.h" 10 1.1.1.2 joerg #include "clang/AST/OperationKinds.h" 11 1.1 joerg #include "clang/ASTMatchers/ASTMatchFinder.h" 12 1.1.1.2 joerg #include "clang/ASTMatchers/ASTMatchers.h" 13 1.1 joerg #include "llvm/ADT/STLExtras.h" 14 1.1 joerg 15 1.1 joerg namespace clang { 16 1.1 joerg using namespace ast_matchers; 17 1.1 joerg 18 1.1 joerg namespace { 19 1.1 joerg 20 1.1 joerg AST_MATCHER_P(LambdaExpr, hasCaptureInit, const Expr *, E) { 21 1.1 joerg return llvm::is_contained(Node.capture_inits(), E); 22 1.1 joerg } 23 1.1 joerg 24 1.1 joerg AST_MATCHER_P(CXXForRangeStmt, hasRangeStmt, 25 1.1 joerg ast_matchers::internal::Matcher<DeclStmt>, InnerMatcher) { 26 1.1 joerg const DeclStmt *const Range = Node.getRangeStmt(); 27 1.1 joerg return InnerMatcher.matches(*Range, Finder, Builder); 28 1.1 joerg } 29 1.1 joerg 30 1.1.1.2 joerg AST_MATCHER_P(Expr, maybeEvalCommaExpr, ast_matchers::internal::Matcher<Expr>, 31 1.1.1.2 joerg InnerMatcher) { 32 1.1.1.2 joerg const Expr *Result = &Node; 33 1.1 joerg while (const auto *BOComma = 34 1.1.1.2 joerg dyn_cast_or_null<BinaryOperator>(Result->IgnoreParens())) { 35 1.1 joerg if (!BOComma->isCommaOp()) 36 1.1 joerg break; 37 1.1 joerg Result = BOComma->getRHS(); 38 1.1 joerg } 39 1.1 joerg return InnerMatcher.matches(*Result, Finder, Builder); 40 1.1 joerg } 41 1.1 joerg 42 1.1.1.2 joerg AST_MATCHER_P(Expr, canResolveToExpr, ast_matchers::internal::Matcher<Expr>, 43 1.1.1.2 joerg InnerMatcher) { 44 1.1.1.2 joerg auto DerivedToBase = [](const ast_matchers::internal::Matcher<Expr> &Inner) { 45 1.1.1.2 joerg return implicitCastExpr(anyOf(hasCastKind(CK_DerivedToBase), 46 1.1.1.2 joerg hasCastKind(CK_UncheckedDerivedToBase)), 47 1.1.1.2 joerg hasSourceExpression(Inner)); 48 1.1.1.2 joerg }; 49 1.1.1.2 joerg auto IgnoreDerivedToBase = 50 1.1.1.2 joerg [&DerivedToBase](const ast_matchers::internal::Matcher<Expr> &Inner) { 51 1.1.1.2 joerg return ignoringParens(expr(anyOf(Inner, DerivedToBase(Inner)))); 52 1.1.1.2 joerg }; 53 1.1.1.2 joerg 54 1.1.1.2 joerg // The 'ConditionalOperator' matches on `<anything> ? <expr> : <expr>`. 55 1.1.1.2 joerg // This matching must be recursive because `<expr>` can be anything resolving 56 1.1.1.2 joerg // to the `InnerMatcher`, for example another conditional operator. 57 1.1.1.2 joerg // The edge-case `BaseClass &b = <cond> ? DerivedVar1 : DerivedVar2;` 58 1.1.1.2 joerg // is handled, too. The implicit cast happens outside of the conditional. 59 1.1.1.2 joerg // This is matched by `IgnoreDerivedToBase(canResolveToExpr(InnerMatcher))` 60 1.1.1.2 joerg // below. 61 1.1.1.2 joerg auto const ConditionalOperator = conditionalOperator(anyOf( 62 1.1.1.2 joerg hasTrueExpression(ignoringParens(canResolveToExpr(InnerMatcher))), 63 1.1.1.2 joerg hasFalseExpression(ignoringParens(canResolveToExpr(InnerMatcher))))); 64 1.1.1.2 joerg auto const ElvisOperator = binaryConditionalOperator(anyOf( 65 1.1.1.2 joerg hasTrueExpression(ignoringParens(canResolveToExpr(InnerMatcher))), 66 1.1.1.2 joerg hasFalseExpression(ignoringParens(canResolveToExpr(InnerMatcher))))); 67 1.1.1.2 joerg 68 1.1.1.2 joerg auto const ComplexMatcher = ignoringParens( 69 1.1.1.2 joerg expr(anyOf(IgnoreDerivedToBase(InnerMatcher), 70 1.1.1.2 joerg maybeEvalCommaExpr(IgnoreDerivedToBase(InnerMatcher)), 71 1.1.1.2 joerg IgnoreDerivedToBase(ConditionalOperator), 72 1.1.1.2 joerg IgnoreDerivedToBase(ElvisOperator)))); 73 1.1.1.2 joerg 74 1.1.1.2 joerg return ComplexMatcher.matches(Node, Finder, Builder); 75 1.1.1.2 joerg } 76 1.1.1.2 joerg 77 1.1.1.2 joerg // Similar to 'hasAnyArgument', but does not work because 'InitListExpr' does 78 1.1.1.2 joerg // not have the 'arguments()' method. 79 1.1.1.2 joerg AST_MATCHER_P(InitListExpr, hasAnyInit, ast_matchers::internal::Matcher<Expr>, 80 1.1.1.2 joerg InnerMatcher) { 81 1.1.1.2 joerg for (const Expr *Arg : Node.inits()) { 82 1.1.1.2 joerg ast_matchers::internal::BoundNodesTreeBuilder Result(*Builder); 83 1.1.1.2 joerg if (InnerMatcher.matches(*Arg, Finder, &Result)) { 84 1.1.1.2 joerg *Builder = std::move(Result); 85 1.1.1.2 joerg return true; 86 1.1.1.2 joerg } 87 1.1.1.2 joerg } 88 1.1.1.2 joerg return false; 89 1.1.1.2 joerg } 90 1.1.1.2 joerg 91 1.1 joerg const ast_matchers::internal::VariadicDynCastAllOfMatcher<Stmt, CXXTypeidExpr> 92 1.1 joerg cxxTypeidExpr; 93 1.1 joerg 94 1.1 joerg AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) { 95 1.1 joerg return Node.isPotentiallyEvaluated(); 96 1.1 joerg } 97 1.1 joerg 98 1.1 joerg AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr, 99 1.1 joerg ast_matchers::internal::Matcher<Expr>, InnerMatcher) { 100 1.1 joerg return InnerMatcher.matches(*Node.getControllingExpr(), Finder, Builder); 101 1.1 joerg } 102 1.1 joerg 103 1.1 joerg const auto nonConstReferenceType = [] { 104 1.1 joerg return hasUnqualifiedDesugaredType( 105 1.1 joerg referenceType(pointee(unless(isConstQualified())))); 106 1.1 joerg }; 107 1.1 joerg 108 1.1 joerg const auto nonConstPointerType = [] { 109 1.1 joerg return hasUnqualifiedDesugaredType( 110 1.1 joerg pointerType(pointee(unless(isConstQualified())))); 111 1.1 joerg }; 112 1.1 joerg 113 1.1 joerg const auto isMoveOnly = [] { 114 1.1 joerg return cxxRecordDecl( 115 1.1 joerg hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))), 116 1.1 joerg hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))), 117 1.1 joerg unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(), 118 1.1 joerg unless(isDeleted()))), 119 1.1 joerg hasMethod(cxxMethodDecl(isCopyAssignmentOperator(), 120 1.1 joerg unless(isDeleted())))))); 121 1.1 joerg }; 122 1.1 joerg 123 1.1 joerg template <class T> struct NodeID; 124 1.1.1.2 joerg template <> struct NodeID<Expr> { static constexpr StringRef value = "expr"; }; 125 1.1.1.2 joerg template <> struct NodeID<Decl> { static constexpr StringRef value = "decl"; }; 126 1.1.1.2 joerg constexpr StringRef NodeID<Expr>::value; 127 1.1.1.2 joerg constexpr StringRef NodeID<Decl>::value; 128 1.1 joerg 129 1.1 joerg template <class T, class F = const Stmt *(ExprMutationAnalyzer::*)(const T *)> 130 1.1 joerg const Stmt *tryEachMatch(ArrayRef<ast_matchers::BoundNodes> Matches, 131 1.1 joerg ExprMutationAnalyzer *Analyzer, F Finder) { 132 1.1 joerg const StringRef ID = NodeID<T>::value; 133 1.1 joerg for (const auto &Nodes : Matches) { 134 1.1 joerg if (const Stmt *S = (Analyzer->*Finder)(Nodes.getNodeAs<T>(ID))) 135 1.1 joerg return S; 136 1.1 joerg } 137 1.1 joerg return nullptr; 138 1.1 joerg } 139 1.1 joerg 140 1.1 joerg } // namespace 141 1.1 joerg 142 1.1 joerg const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) { 143 1.1 joerg return findMutationMemoized(Exp, 144 1.1 joerg {&ExprMutationAnalyzer::findDirectMutation, 145 1.1 joerg &ExprMutationAnalyzer::findMemberMutation, 146 1.1 joerg &ExprMutationAnalyzer::findArrayElementMutation, 147 1.1 joerg &ExprMutationAnalyzer::findCastMutation, 148 1.1 joerg &ExprMutationAnalyzer::findRangeLoopMutation, 149 1.1 joerg &ExprMutationAnalyzer::findReferenceMutation, 150 1.1 joerg &ExprMutationAnalyzer::findFunctionArgMutation}, 151 1.1 joerg Results); 152 1.1 joerg } 153 1.1 joerg 154 1.1 joerg const Stmt *ExprMutationAnalyzer::findMutation(const Decl *Dec) { 155 1.1 joerg return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findMutation); 156 1.1 joerg } 157 1.1 joerg 158 1.1 joerg const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Expr *Exp) { 159 1.1 joerg return findMutationMemoized(Exp, {/*TODO*/}, PointeeResults); 160 1.1 joerg } 161 1.1 joerg 162 1.1 joerg const Stmt *ExprMutationAnalyzer::findPointeeMutation(const Decl *Dec) { 163 1.1 joerg return tryEachDeclRef(Dec, &ExprMutationAnalyzer::findPointeeMutation); 164 1.1 joerg } 165 1.1 joerg 166 1.1 joerg const Stmt *ExprMutationAnalyzer::findMutationMemoized( 167 1.1 joerg const Expr *Exp, llvm::ArrayRef<MutationFinder> Finders, 168 1.1 joerg ResultMap &MemoizedResults) { 169 1.1 joerg const auto Memoized = MemoizedResults.find(Exp); 170 1.1 joerg if (Memoized != MemoizedResults.end()) 171 1.1 joerg return Memoized->second; 172 1.1 joerg 173 1.1 joerg if (isUnevaluated(Exp)) 174 1.1 joerg return MemoizedResults[Exp] = nullptr; 175 1.1 joerg 176 1.1 joerg for (const auto &Finder : Finders) { 177 1.1 joerg if (const Stmt *S = (this->*Finder)(Exp)) 178 1.1 joerg return MemoizedResults[Exp] = S; 179 1.1 joerg } 180 1.1 joerg 181 1.1 joerg return MemoizedResults[Exp] = nullptr; 182 1.1 joerg } 183 1.1 joerg 184 1.1 joerg const Stmt *ExprMutationAnalyzer::tryEachDeclRef(const Decl *Dec, 185 1.1 joerg MutationFinder Finder) { 186 1.1 joerg const auto Refs = 187 1.1 joerg match(findAll(declRefExpr(to(equalsNode(Dec))).bind(NodeID<Expr>::value)), 188 1.1 joerg Stm, Context); 189 1.1 joerg for (const auto &RefNodes : Refs) { 190 1.1 joerg const auto *E = RefNodes.getNodeAs<Expr>(NodeID<Expr>::value); 191 1.1 joerg if ((this->*Finder)(E)) 192 1.1 joerg return E; 193 1.1 joerg } 194 1.1 joerg return nullptr; 195 1.1 joerg } 196 1.1 joerg 197 1.1 joerg bool ExprMutationAnalyzer::isUnevaluated(const Expr *Exp) { 198 1.1 joerg return selectFirst<Expr>( 199 1.1 joerg NodeID<Expr>::value, 200 1.1 joerg match( 201 1.1 joerg findAll( 202 1.1.1.2 joerg expr(canResolveToExpr(equalsNode(Exp)), 203 1.1 joerg anyOf( 204 1.1 joerg // `Exp` is part of the underlying expression of 205 1.1 joerg // decltype/typeof if it has an ancestor of 206 1.1 joerg // typeLoc. 207 1.1 joerg hasAncestor(typeLoc(unless( 208 1.1 joerg hasAncestor(unaryExprOrTypeTraitExpr())))), 209 1.1 joerg hasAncestor(expr(anyOf( 210 1.1 joerg // `UnaryExprOrTypeTraitExpr` is unevaluated 211 1.1 joerg // unless it's sizeof on VLA. 212 1.1 joerg unaryExprOrTypeTraitExpr(unless(sizeOfExpr( 213 1.1 joerg hasArgumentOfType(variableArrayType())))), 214 1.1 joerg // `CXXTypeidExpr` is unevaluated unless it's 215 1.1 joerg // applied to an expression of glvalue of 216 1.1 joerg // polymorphic class type. 217 1.1 joerg cxxTypeidExpr( 218 1.1 joerg unless(isPotentiallyEvaluated())), 219 1.1 joerg // The controlling expression of 220 1.1 joerg // `GenericSelectionExpr` is unevaluated. 221 1.1 joerg genericSelectionExpr(hasControllingExpr( 222 1.1 joerg hasDescendant(equalsNode(Exp)))), 223 1.1 joerg cxxNoexceptExpr()))))) 224 1.1 joerg .bind(NodeID<Expr>::value)), 225 1.1 joerg Stm, Context)) != nullptr; 226 1.1 joerg } 227 1.1 joerg 228 1.1 joerg const Stmt * 229 1.1 joerg ExprMutationAnalyzer::findExprMutation(ArrayRef<BoundNodes> Matches) { 230 1.1 joerg return tryEachMatch<Expr>(Matches, this, &ExprMutationAnalyzer::findMutation); 231 1.1 joerg } 232 1.1 joerg 233 1.1 joerg const Stmt * 234 1.1 joerg ExprMutationAnalyzer::findDeclMutation(ArrayRef<BoundNodes> Matches) { 235 1.1 joerg return tryEachMatch<Decl>(Matches, this, &ExprMutationAnalyzer::findMutation); 236 1.1 joerg } 237 1.1 joerg 238 1.1 joerg const Stmt *ExprMutationAnalyzer::findExprPointeeMutation( 239 1.1 joerg ArrayRef<ast_matchers::BoundNodes> Matches) { 240 1.1 joerg return tryEachMatch<Expr>(Matches, this, 241 1.1 joerg &ExprMutationAnalyzer::findPointeeMutation); 242 1.1 joerg } 243 1.1 joerg 244 1.1 joerg const Stmt *ExprMutationAnalyzer::findDeclPointeeMutation( 245 1.1 joerg ArrayRef<ast_matchers::BoundNodes> Matches) { 246 1.1 joerg return tryEachMatch<Decl>(Matches, this, 247 1.1 joerg &ExprMutationAnalyzer::findPointeeMutation); 248 1.1 joerg } 249 1.1 joerg 250 1.1 joerg const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { 251 1.1 joerg // LHS of any assignment operators. 252 1.1.1.2 joerg const auto AsAssignmentLhs = binaryOperator( 253 1.1.1.2 joerg isAssignmentOperator(), hasLHS(canResolveToExpr(equalsNode(Exp)))); 254 1.1 joerg 255 1.1 joerg // Operand of increment/decrement operators. 256 1.1 joerg const auto AsIncDecOperand = 257 1.1 joerg unaryOperator(anyOf(hasOperatorName("++"), hasOperatorName("--")), 258 1.1.1.2 joerg hasUnaryOperand(canResolveToExpr(equalsNode(Exp)))); 259 1.1 joerg 260 1.1 joerg // Invoking non-const member function. 261 1.1 joerg // A member function is assumed to be non-const when it is unresolved. 262 1.1 joerg const auto NonConstMethod = cxxMethodDecl(unless(isConst())); 263 1.1.1.2 joerg 264 1.1.1.2 joerg const auto AsNonConstThis = expr(anyOf( 265 1.1.1.2 joerg cxxMemberCallExpr(callee(NonConstMethod), 266 1.1.1.2 joerg on(canResolveToExpr(equalsNode(Exp)))), 267 1.1.1.2 joerg cxxOperatorCallExpr(callee(NonConstMethod), 268 1.1.1.2 joerg hasArgument(0, canResolveToExpr(equalsNode(Exp)))), 269 1.1.1.2 joerg // In case of a templated type, calling overloaded operators is not 270 1.1.1.2 joerg // resolved and modelled as `binaryOperator` on a dependent type. 271 1.1.1.2 joerg // Such instances are considered a modification, because they can modify 272 1.1.1.2 joerg // in different instantiations of the template. 273 1.1.1.2 joerg binaryOperator(hasEitherOperand( 274 1.1.1.2 joerg allOf(ignoringImpCasts(canResolveToExpr(equalsNode(Exp))), 275 1.1.1.2 joerg isTypeDependent()))), 276 1.1.1.2 joerg // Within class templates and member functions the member expression might 277 1.1.1.2 joerg // not be resolved. In that case, the `callExpr` is considered to be a 278 1.1.1.2 joerg // modification. 279 1.1.1.2 joerg callExpr( 280 1.1.1.2 joerg callee(expr(anyOf(unresolvedMemberExpr(hasObjectExpression( 281 1.1.1.2 joerg canResolveToExpr(equalsNode(Exp)))), 282 1.1.1.2 joerg cxxDependentScopeMemberExpr(hasObjectExpression( 283 1.1.1.2 joerg canResolveToExpr(equalsNode(Exp)))))))), 284 1.1.1.2 joerg // Match on a call to a known method, but the call itself is type 285 1.1.1.2 joerg // dependent (e.g. `vector<T> v; v.push(T{});` in a templated function). 286 1.1.1.2 joerg callExpr(allOf(isTypeDependent(), 287 1.1.1.2 joerg callee(memberExpr(hasDeclaration(NonConstMethod), 288 1.1.1.2 joerg hasObjectExpression(canResolveToExpr( 289 1.1.1.2 joerg equalsNode(Exp))))))))); 290 1.1 joerg 291 1.1 joerg // Taking address of 'Exp'. 292 1.1 joerg // We're assuming 'Exp' is mutated as soon as its address is taken, though in 293 1.1 joerg // theory we can follow the pointer and see whether it escaped `Stm` or is 294 1.1 joerg // dereferenced and then mutated. This is left for future improvements. 295 1.1 joerg const auto AsAmpersandOperand = 296 1.1 joerg unaryOperator(hasOperatorName("&"), 297 1.1 joerg // A NoOp implicit cast is adding const. 298 1.1 joerg unless(hasParent(implicitCastExpr(hasCastKind(CK_NoOp)))), 299 1.1.1.2 joerg hasUnaryOperand(canResolveToExpr(equalsNode(Exp)))); 300 1.1 joerg const auto AsPointerFromArrayDecay = 301 1.1 joerg castExpr(hasCastKind(CK_ArrayToPointerDecay), 302 1.1 joerg unless(hasParent(arraySubscriptExpr())), 303 1.1.1.2 joerg has(canResolveToExpr(equalsNode(Exp)))); 304 1.1 joerg // Treat calling `operator->()` of move-only classes as taking address. 305 1.1 joerg // These are typically smart pointers with unique ownership so we treat 306 1.1 joerg // mutation of pointee as mutation of the smart pointer itself. 307 1.1.1.2 joerg const auto AsOperatorArrowThis = cxxOperatorCallExpr( 308 1.1.1.2 joerg hasOverloadedOperatorName("->"), 309 1.1.1.2 joerg callee( 310 1.1.1.2 joerg cxxMethodDecl(ofClass(isMoveOnly()), returns(nonConstPointerType()))), 311 1.1.1.2 joerg argumentCountIs(1), hasArgument(0, canResolveToExpr(equalsNode(Exp)))); 312 1.1 joerg 313 1.1 joerg // Used as non-const-ref argument when calling a function. 314 1.1 joerg // An argument is assumed to be non-const-ref when the function is unresolved. 315 1.1 joerg // Instantiated template functions are not handled here but in 316 1.1 joerg // findFunctionArgMutation which has additional smarts for handling forwarding 317 1.1 joerg // references. 318 1.1.1.2 joerg const auto NonConstRefParam = forEachArgumentWithParamType( 319 1.1.1.2 joerg anyOf(canResolveToExpr(equalsNode(Exp)), 320 1.1.1.2 joerg memberExpr(hasObjectExpression(canResolveToExpr(equalsNode(Exp))))), 321 1.1.1.2 joerg nonConstReferenceType()); 322 1.1 joerg const auto NotInstantiated = unless(hasDeclaration(isInstantiated())); 323 1.1.1.2 joerg const auto TypeDependentCallee = 324 1.1.1.2 joerg callee(expr(anyOf(unresolvedLookupExpr(), unresolvedMemberExpr(), 325 1.1.1.2 joerg cxxDependentScopeMemberExpr(), 326 1.1.1.2 joerg hasType(templateTypeParmType()), isTypeDependent()))); 327 1.1.1.2 joerg 328 1.1 joerg const auto AsNonConstRefArg = anyOf( 329 1.1 joerg callExpr(NonConstRefParam, NotInstantiated), 330 1.1 joerg cxxConstructExpr(NonConstRefParam, NotInstantiated), 331 1.1.1.2 joerg callExpr(TypeDependentCallee, 332 1.1.1.2 joerg hasAnyArgument(canResolveToExpr(equalsNode(Exp)))), 333 1.1.1.2 joerg cxxUnresolvedConstructExpr( 334 1.1.1.2 joerg hasAnyArgument(canResolveToExpr(equalsNode(Exp)))), 335 1.1.1.2 joerg // Previous False Positive in the following Code: 336 1.1.1.2 joerg // `template <typename T> void f() { int i = 42; new Type<T>(i); }` 337 1.1.1.2 joerg // Where the constructor of `Type` takes its argument as reference. 338 1.1.1.2 joerg // The AST does not resolve in a `cxxConstructExpr` because it is 339 1.1.1.2 joerg // type-dependent. 340 1.1.1.2 joerg parenListExpr(hasDescendant(expr(canResolveToExpr(equalsNode(Exp))))), 341 1.1.1.2 joerg // If the initializer is for a reference type, there is no cast for 342 1.1.1.2 joerg // the variable. Values are cast to RValue first. 343 1.1.1.2 joerg initListExpr(hasAnyInit(expr(canResolveToExpr(equalsNode(Exp)))))); 344 1.1 joerg 345 1.1 joerg // Captured by a lambda by reference. 346 1.1 joerg // If we're initializing a capture with 'Exp' directly then we're initializing 347 1.1 joerg // a reference capture. 348 1.1 joerg // For value captures there will be an ImplicitCastExpr <LValueToRValue>. 349 1.1 joerg const auto AsLambdaRefCaptureInit = lambdaExpr(hasCaptureInit(Exp)); 350 1.1 joerg 351 1.1 joerg // Returned as non-const-ref. 352 1.1 joerg // If we're returning 'Exp' directly then it's returned as non-const-ref. 353 1.1 joerg // For returning by value there will be an ImplicitCastExpr <LValueToRValue>. 354 1.1 joerg // For returning by const-ref there will be an ImplicitCastExpr <NoOp> (for 355 1.1 joerg // adding const.) 356 1.1.1.2 joerg const auto AsNonConstRefReturn = 357 1.1.1.2 joerg returnStmt(hasReturnValue(canResolveToExpr(equalsNode(Exp)))); 358 1.1 joerg 359 1.1.1.2 joerg // It is used as a non-const-reference for initalizing a range-for loop. 360 1.1.1.2 joerg const auto AsNonConstRefRangeInit = cxxForRangeStmt( 361 1.1.1.2 joerg hasRangeInit(declRefExpr(allOf(canResolveToExpr(equalsNode(Exp)), 362 1.1.1.2 joerg hasType(nonConstReferenceType()))))); 363 1.1.1.2 joerg 364 1.1.1.2 joerg const auto Matches = match( 365 1.1.1.2 joerg traverse(TK_AsIs, 366 1.1.1.2 joerg findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, 367 1.1.1.2 joerg AsNonConstThis, AsAmpersandOperand, 368 1.1.1.2 joerg AsPointerFromArrayDecay, AsOperatorArrowThis, 369 1.1.1.2 joerg AsNonConstRefArg, AsLambdaRefCaptureInit, 370 1.1.1.2 joerg AsNonConstRefReturn, AsNonConstRefRangeInit)) 371 1.1.1.2 joerg .bind("stmt"))), 372 1.1.1.2 joerg Stm, Context); 373 1.1 joerg return selectFirst<Stmt>("stmt", Matches); 374 1.1 joerg } 375 1.1 joerg 376 1.1 joerg const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) { 377 1.1 joerg // Check whether any member of 'Exp' is mutated. 378 1.1 joerg const auto MemberExprs = 379 1.1.1.2 joerg match(findAll(expr(anyOf(memberExpr(hasObjectExpression( 380 1.1.1.2 joerg canResolveToExpr(equalsNode(Exp)))), 381 1.1.1.2 joerg cxxDependentScopeMemberExpr(hasObjectExpression( 382 1.1.1.2 joerg canResolveToExpr(equalsNode(Exp)))))) 383 1.1 joerg .bind(NodeID<Expr>::value)), 384 1.1 joerg Stm, Context); 385 1.1 joerg return findExprMutation(MemberExprs); 386 1.1 joerg } 387 1.1 joerg 388 1.1 joerg const Stmt *ExprMutationAnalyzer::findArrayElementMutation(const Expr *Exp) { 389 1.1 joerg // Check whether any element of an array is mutated. 390 1.1.1.2 joerg const auto SubscriptExprs = 391 1.1.1.2 joerg match(findAll(arraySubscriptExpr( 392 1.1.1.2 joerg anyOf(hasBase(canResolveToExpr(equalsNode(Exp))), 393 1.1.1.2 joerg hasBase(implicitCastExpr( 394 1.1.1.2 joerg allOf(hasCastKind(CK_ArrayToPointerDecay), 395 1.1.1.2 joerg hasSourceExpression(canResolveToExpr( 396 1.1.1.2 joerg equalsNode(Exp)))))))) 397 1.1.1.2 joerg .bind(NodeID<Expr>::value)), 398 1.1.1.2 joerg Stm, Context); 399 1.1 joerg return findExprMutation(SubscriptExprs); 400 1.1 joerg } 401 1.1 joerg 402 1.1 joerg const Stmt *ExprMutationAnalyzer::findCastMutation(const Expr *Exp) { 403 1.1.1.2 joerg // If the 'Exp' is explicitly casted to a non-const reference type the 404 1.1.1.2 joerg // 'Exp' is considered to be modified. 405 1.1.1.2 joerg const auto ExplicitCast = match( 406 1.1.1.2 joerg findAll( 407 1.1.1.2 joerg stmt(castExpr(hasSourceExpression(canResolveToExpr(equalsNode(Exp))), 408 1.1.1.2 joerg explicitCastExpr( 409 1.1.1.2 joerg hasDestinationType(nonConstReferenceType())))) 410 1.1.1.2 joerg .bind("stmt")), 411 1.1.1.2 joerg Stm, Context); 412 1.1.1.2 joerg 413 1.1.1.2 joerg if (const auto *CastStmt = selectFirst<Stmt>("stmt", ExplicitCast)) 414 1.1.1.2 joerg return CastStmt; 415 1.1.1.2 joerg 416 1.1 joerg // If 'Exp' is casted to any non-const reference type, check the castExpr. 417 1.1.1.2 joerg const auto Casts = match( 418 1.1.1.2 joerg findAll( 419 1.1.1.2 joerg expr(castExpr(hasSourceExpression(canResolveToExpr(equalsNode(Exp))), 420 1.1.1.2 joerg anyOf(explicitCastExpr( 421 1.1.1.2 joerg hasDestinationType(nonConstReferenceType())), 422 1.1.1.2 joerg implicitCastExpr(hasImplicitDestinationType( 423 1.1.1.2 joerg nonConstReferenceType()))))) 424 1.1.1.2 joerg .bind(NodeID<Expr>::value)), 425 1.1.1.2 joerg Stm, Context); 426 1.1.1.2 joerg 427 1.1 joerg if (const Stmt *S = findExprMutation(Casts)) 428 1.1 joerg return S; 429 1.1 joerg // Treat std::{move,forward} as cast. 430 1.1 joerg const auto Calls = 431 1.1 joerg match(findAll(callExpr(callee(namedDecl( 432 1.1 joerg hasAnyName("::std::move", "::std::forward"))), 433 1.1.1.2 joerg hasArgument(0, canResolveToExpr(equalsNode(Exp)))) 434 1.1 joerg .bind("expr")), 435 1.1 joerg Stm, Context); 436 1.1 joerg return findExprMutation(Calls); 437 1.1 joerg } 438 1.1 joerg 439 1.1 joerg const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) { 440 1.1.1.2 joerg // Keep the ordering for the specific initialization matches to happen first, 441 1.1.1.2 joerg // because it is cheaper to match all potential modifications of the loop 442 1.1.1.2 joerg // variable. 443 1.1.1.2 joerg 444 1.1.1.2 joerg // The range variable is a reference to a builtin array. In that case the 445 1.1.1.2 joerg // array is considered modified if the loop-variable is a non-const reference. 446 1.1.1.2 joerg const auto DeclStmtToNonRefToArray = declStmt(hasSingleDecl(varDecl(hasType( 447 1.1.1.2 joerg hasUnqualifiedDesugaredType(referenceType(pointee(arrayType()))))))); 448 1.1.1.2 joerg const auto RefToArrayRefToElements = match( 449 1.1.1.2 joerg findAll(stmt(cxxForRangeStmt( 450 1.1.1.2 joerg hasLoopVariable(varDecl(hasType(nonConstReferenceType())) 451 1.1.1.2 joerg .bind(NodeID<Decl>::value)), 452 1.1.1.2 joerg hasRangeStmt(DeclStmtToNonRefToArray), 453 1.1.1.2 joerg hasRangeInit(canResolveToExpr(equalsNode(Exp))))) 454 1.1.1.2 joerg .bind("stmt")), 455 1.1.1.2 joerg Stm, Context); 456 1.1.1.2 joerg 457 1.1.1.2 joerg if (const auto *BadRangeInitFromArray = 458 1.1.1.2 joerg selectFirst<Stmt>("stmt", RefToArrayRefToElements)) 459 1.1.1.2 joerg return BadRangeInitFromArray; 460 1.1.1.2 joerg 461 1.1.1.2 joerg // Small helper to match special cases in range-for loops. 462 1.1.1.2 joerg // 463 1.1.1.2 joerg // It is possible that containers do not provide a const-overload for their 464 1.1.1.2 joerg // iterator accessors. If this is the case, the variable is used non-const 465 1.1.1.2 joerg // no matter what happens in the loop. This requires special detection as it 466 1.1.1.2 joerg // is then faster to find all mutations of the loop variable. 467 1.1.1.2 joerg // It aims at a different modification as well. 468 1.1.1.2 joerg const auto HasAnyNonConstIterator = 469 1.1.1.2 joerg anyOf(allOf(hasMethod(allOf(hasName("begin"), unless(isConst()))), 470 1.1.1.2 joerg unless(hasMethod(allOf(hasName("begin"), isConst())))), 471 1.1.1.2 joerg allOf(hasMethod(allOf(hasName("end"), unless(isConst()))), 472 1.1.1.2 joerg unless(hasMethod(allOf(hasName("end"), isConst()))))); 473 1.1.1.2 joerg 474 1.1.1.2 joerg const auto DeclStmtToNonConstIteratorContainer = declStmt( 475 1.1.1.2 joerg hasSingleDecl(varDecl(hasType(hasUnqualifiedDesugaredType(referenceType( 476 1.1.1.2 joerg pointee(hasDeclaration(cxxRecordDecl(HasAnyNonConstIterator))))))))); 477 1.1.1.2 joerg 478 1.1.1.2 joerg const auto RefToContainerBadIterators = 479 1.1.1.2 joerg match(findAll(stmt(cxxForRangeStmt(allOf( 480 1.1.1.2 joerg hasRangeStmt(DeclStmtToNonConstIteratorContainer), 481 1.1.1.2 joerg hasRangeInit(canResolveToExpr(equalsNode(Exp)))))) 482 1.1.1.2 joerg .bind("stmt")), 483 1.1.1.2 joerg Stm, Context); 484 1.1.1.2 joerg 485 1.1.1.2 joerg if (const auto *BadIteratorsContainer = 486 1.1.1.2 joerg selectFirst<Stmt>("stmt", RefToContainerBadIterators)) 487 1.1.1.2 joerg return BadIteratorsContainer; 488 1.1.1.2 joerg 489 1.1 joerg // If range for looping over 'Exp' with a non-const reference loop variable, 490 1.1 joerg // check all declRefExpr of the loop variable. 491 1.1 joerg const auto LoopVars = 492 1.1 joerg match(findAll(cxxForRangeStmt( 493 1.1 joerg hasLoopVariable(varDecl(hasType(nonConstReferenceType())) 494 1.1 joerg .bind(NodeID<Decl>::value)), 495 1.1.1.2 joerg hasRangeInit(canResolveToExpr(equalsNode(Exp))))), 496 1.1 joerg Stm, Context); 497 1.1 joerg return findDeclMutation(LoopVars); 498 1.1 joerg } 499 1.1 joerg 500 1.1 joerg const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) { 501 1.1 joerg // Follow non-const reference returned by `operator*()` of move-only classes. 502 1.1 joerg // These are typically smart pointers with unique ownership so we treat 503 1.1 joerg // mutation of pointee as mutation of the smart pointer itself. 504 1.1 joerg const auto Ref = 505 1.1 joerg match(findAll(cxxOperatorCallExpr( 506 1.1 joerg hasOverloadedOperatorName("*"), 507 1.1 joerg callee(cxxMethodDecl(ofClass(isMoveOnly()), 508 1.1 joerg returns(nonConstReferenceType()))), 509 1.1.1.2 joerg argumentCountIs(1), 510 1.1.1.2 joerg hasArgument(0, canResolveToExpr(equalsNode(Exp)))) 511 1.1 joerg .bind(NodeID<Expr>::value)), 512 1.1 joerg Stm, Context); 513 1.1 joerg if (const Stmt *S = findExprMutation(Ref)) 514 1.1 joerg return S; 515 1.1 joerg 516 1.1 joerg // If 'Exp' is bound to a non-const reference, check all declRefExpr to that. 517 1.1 joerg const auto Refs = match( 518 1.1 joerg stmt(forEachDescendant( 519 1.1 joerg varDecl( 520 1.1 joerg hasType(nonConstReferenceType()), 521 1.1.1.2 joerg hasInitializer(anyOf(canResolveToExpr(equalsNode(Exp)), 522 1.1.1.2 joerg memberExpr(hasObjectExpression( 523 1.1.1.2 joerg canResolveToExpr(equalsNode(Exp)))))), 524 1.1 joerg hasParent(declStmt().bind("stmt")), 525 1.1.1.2 joerg // Don't follow the reference in range statement, we've 526 1.1.1.2 joerg // handled that separately. 527 1.1 joerg unless(hasParent(declStmt(hasParent( 528 1.1 joerg cxxForRangeStmt(hasRangeStmt(equalsBoundNode("stmt")))))))) 529 1.1 joerg .bind(NodeID<Decl>::value))), 530 1.1 joerg Stm, Context); 531 1.1 joerg return findDeclMutation(Refs); 532 1.1 joerg } 533 1.1 joerg 534 1.1 joerg const Stmt *ExprMutationAnalyzer::findFunctionArgMutation(const Expr *Exp) { 535 1.1 joerg const auto NonConstRefParam = forEachArgumentWithParam( 536 1.1.1.2 joerg canResolveToExpr(equalsNode(Exp)), 537 1.1 joerg parmVarDecl(hasType(nonConstReferenceType())).bind("parm")); 538 1.1 joerg const auto IsInstantiated = hasDeclaration(isInstantiated()); 539 1.1 joerg const auto FuncDecl = hasDeclaration(functionDecl().bind("func")); 540 1.1 joerg const auto Matches = match( 541 1.1.1.2 joerg traverse( 542 1.1.1.2 joerg TK_AsIs, 543 1.1.1.2 joerg findAll( 544 1.1.1.2 joerg expr(anyOf(callExpr(NonConstRefParam, IsInstantiated, FuncDecl, 545 1.1 joerg unless(callee(namedDecl(hasAnyName( 546 1.1 joerg "::std::move", "::std::forward"))))), 547 1.1 joerg cxxConstructExpr(NonConstRefParam, IsInstantiated, 548 1.1 joerg FuncDecl))) 549 1.1.1.2 joerg .bind(NodeID<Expr>::value))), 550 1.1 joerg Stm, Context); 551 1.1 joerg for (const auto &Nodes : Matches) { 552 1.1 joerg const auto *Exp = Nodes.getNodeAs<Expr>(NodeID<Expr>::value); 553 1.1 joerg const auto *Func = Nodes.getNodeAs<FunctionDecl>("func"); 554 1.1 joerg if (!Func->getBody() || !Func->getPrimaryTemplate()) 555 1.1 joerg return Exp; 556 1.1 joerg 557 1.1 joerg const auto *Parm = Nodes.getNodeAs<ParmVarDecl>("parm"); 558 1.1 joerg const ArrayRef<ParmVarDecl *> AllParams = 559 1.1 joerg Func->getPrimaryTemplate()->getTemplatedDecl()->parameters(); 560 1.1 joerg QualType ParmType = 561 1.1 joerg AllParams[std::min<size_t>(Parm->getFunctionScopeIndex(), 562 1.1 joerg AllParams.size() - 1)] 563 1.1 joerg ->getType(); 564 1.1 joerg if (const auto *T = ParmType->getAs<PackExpansionType>()) 565 1.1 joerg ParmType = T->getPattern(); 566 1.1 joerg 567 1.1 joerg // If param type is forwarding reference, follow into the function 568 1.1 joerg // definition and see whether the param is mutated inside. 569 1.1 joerg if (const auto *RefType = ParmType->getAs<RValueReferenceType>()) { 570 1.1 joerg if (!RefType->getPointeeType().getQualifiers() && 571 1.1 joerg RefType->getPointeeType()->getAs<TemplateTypeParmType>()) { 572 1.1 joerg std::unique_ptr<FunctionParmMutationAnalyzer> &Analyzer = 573 1.1 joerg FuncParmAnalyzer[Func]; 574 1.1 joerg if (!Analyzer) 575 1.1 joerg Analyzer.reset(new FunctionParmMutationAnalyzer(*Func, Context)); 576 1.1 joerg if (Analyzer->findMutation(Parm)) 577 1.1 joerg return Exp; 578 1.1 joerg continue; 579 1.1 joerg } 580 1.1 joerg } 581 1.1 joerg // Not forwarding reference. 582 1.1 joerg return Exp; 583 1.1 joerg } 584 1.1 joerg return nullptr; 585 1.1 joerg } 586 1.1 joerg 587 1.1 joerg FunctionParmMutationAnalyzer::FunctionParmMutationAnalyzer( 588 1.1 joerg const FunctionDecl &Func, ASTContext &Context) 589 1.1 joerg : BodyAnalyzer(*Func.getBody(), Context) { 590 1.1 joerg if (const auto *Ctor = dyn_cast<CXXConstructorDecl>(&Func)) { 591 1.1 joerg // CXXCtorInitializer might also mutate Param but they're not part of 592 1.1 joerg // function body, check them eagerly here since they're typically trivial. 593 1.1 joerg for (const CXXCtorInitializer *Init : Ctor->inits()) { 594 1.1 joerg ExprMutationAnalyzer InitAnalyzer(*Init->getInit(), Context); 595 1.1 joerg for (const ParmVarDecl *Parm : Ctor->parameters()) { 596 1.1 joerg if (Results.find(Parm) != Results.end()) 597 1.1 joerg continue; 598 1.1 joerg if (const Stmt *S = InitAnalyzer.findMutation(Parm)) 599 1.1 joerg Results[Parm] = S; 600 1.1 joerg } 601 1.1 joerg } 602 1.1 joerg } 603 1.1 joerg } 604 1.1 joerg 605 1.1 joerg const Stmt * 606 1.1 joerg FunctionParmMutationAnalyzer::findMutation(const ParmVarDecl *Parm) { 607 1.1 joerg const auto Memoized = Results.find(Parm); 608 1.1 joerg if (Memoized != Results.end()) 609 1.1 joerg return Memoized->second; 610 1.1 joerg 611 1.1 joerg if (const Stmt *S = BodyAnalyzer.findMutation(Parm)) 612 1.1 joerg return Results[Parm] = S; 613 1.1 joerg 614 1.1 joerg return Results[Parm] = nullptr; 615 1.1 joerg } 616 1.1 joerg 617 1.1 joerg } // namespace clang 618