diff options
author | Shuai Wang <shuaiwang@google.com> | 2018-09-11 17:33:12 +0000 |
---|---|---|
committer | Shuai Wang <shuaiwang@google.com> | 2018-09-11 17:33:12 +0000 |
commit | ea85b52732827eb41913a1cd60ae66b56e99de82 (patch) | |
tree | e8194ea63bd5467b25dab569aca01a721bdc9a9c /clang-tools-extra/clang-tidy/utils/ExprMutationAnalyzer.cpp | |
parent | aca532f14dd3360d0025e0b9d5ae606be046cf79 (diff) | |
download | bcm5719-llvm-ea85b52732827eb41913a1cd60ae66b56e99de82.tar.gz bcm5719-llvm-ea85b52732827eb41913a1cd60ae66b56e99de82.zip |
[clang-tidy] Handle unique owning smart pointers in ExprMutationAnalyzer
Summary:
For smart pointers like std::unique_ptr which uniquely owns the
underlying object, treat the mutation of the pointee as mutation of the
smart pointer itself.
This gives better behavior for cases like this:
```
void f(std::vector<std::unique_ptr<Foo>> v) { // undesirable analyze result of `v` as not mutated.
for (auto& p : v) {
p->mutate(); // only const member function `operator->` is invoked on `p`
}
}
```
Reviewers: hokein, george.karpenkov
Subscribers: xazax.hun, a.sidorin, Szelethus, cfe-commits
Differential Revision: https://reviews.llvm.org/D50883
llvm-svn: 341967
Diffstat (limited to 'clang-tools-extra/clang-tidy/utils/ExprMutationAnalyzer.cpp')
-rw-r--r-- | clang-tools-extra/clang-tidy/utils/ExprMutationAnalyzer.cpp | 42 |
1 files changed, 40 insertions, 2 deletions
diff --git a/clang-tools-extra/clang-tidy/utils/ExprMutationAnalyzer.cpp b/clang-tools-extra/clang-tidy/utils/ExprMutationAnalyzer.cpp index f979e97a03d..1ec1f7270f3 100644 --- a/clang-tools-extra/clang-tidy/utils/ExprMutationAnalyzer.cpp +++ b/clang-tools-extra/clang-tidy/utils/ExprMutationAnalyzer.cpp @@ -51,6 +51,20 @@ const auto nonConstReferenceType = [] { return referenceType(pointee(unless(isConstQualified()))); }; +const auto nonConstPointerType = [] { + return pointerType(pointee(unless(isConstQualified()))); +}; + +const auto isMoveOnly = [] { + return cxxRecordDecl( + hasMethod(cxxConstructorDecl(isMoveConstructor(), unless(isDeleted()))), + hasMethod(cxxMethodDecl(isMoveAssignmentOperator(), unless(isDeleted()))), + unless(anyOf(hasMethod(cxxConstructorDecl(isCopyConstructor(), + unless(isDeleted()))), + hasMethod(cxxMethodDecl(isCopyAssignmentOperator(), + unless(isDeleted())))))); +}; + } // namespace const Stmt *ExprMutationAnalyzer::findMutation(const Expr *Exp) { @@ -168,6 +182,15 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { const auto AsPointerFromArrayDecay = castExpr(hasCastKind(CK_ArrayToPointerDecay), unless(hasParent(arraySubscriptExpr())), has(equalsNode(Exp))); + // Treat calling `operator->()` of move-only classes as taking address. + // These are typically smart pointers with unique ownership so we treat + // mutation of pointee as mutation of the smart pointer itself. + const auto AsOperatorArrowThis = cxxOperatorCallExpr( + hasOverloadedOperatorName("->"), + callee(cxxMethodDecl( + ofClass(isMoveOnly()), + returns(hasUnqualifiedDesugaredType(nonConstPointerType())))), + argumentCountIs(1), hasArgument(0, equalsNode(Exp))); // Used as non-const-ref argument when calling a function. // An argument is assumed to be non-const-ref when the function is unresolved. @@ -197,8 +220,8 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { const auto Matches = match(findAll(stmt(anyOf(AsAssignmentLhs, AsIncDecOperand, AsNonConstThis, AsAmpersandOperand, AsPointerFromArrayDecay, - AsNonConstRefArg, AsLambdaRefCaptureInit, - AsNonConstRefReturn)) + AsOperatorArrowThis, AsNonConstRefArg, + AsLambdaRefCaptureInit, AsNonConstRefReturn)) .bind("stmt")), Stm, Context); return selectFirst<Stmt>("stmt", Matches); @@ -250,6 +273,21 @@ const Stmt *ExprMutationAnalyzer::findRangeLoopMutation(const Expr *Exp) { } const Stmt *ExprMutationAnalyzer::findReferenceMutation(const Expr *Exp) { + // Follow non-const reference returned by `operator*()` of move-only classes. + // These are typically smart pointers with unique ownership so we treat + // mutation of pointee as mutation of the smart pointer itself. + const auto Ref = match( + findAll(cxxOperatorCallExpr( + hasOverloadedOperatorName("*"), + callee(cxxMethodDecl(ofClass(isMoveOnly()), + returns(hasUnqualifiedDesugaredType( + nonConstReferenceType())))), + argumentCountIs(1), hasArgument(0, equalsNode(Exp))) + .bind("expr")), + Stm, Context); + if (const Stmt *S = findExprMutation(Ref)) + return S; + // If 'Exp' is bound to a non-const reference, check all declRefExpr to that. const auto Refs = match( stmt(forEachDescendant( |