summaryrefslogtreecommitdiffstats
path: root/clang-tools-extra/clang-tidy/utils/ExprMutationAnalyzer.cpp
diff options
context:
space:
mode:
authorShuai Wang <shuaiwang@google.com>2018-09-11 17:33:12 +0000
committerShuai Wang <shuaiwang@google.com>2018-09-11 17:33:12 +0000
commitea85b52732827eb41913a1cd60ae66b56e99de82 (patch)
treee8194ea63bd5467b25dab569aca01a721bdc9a9c /clang-tools-extra/clang-tidy/utils/ExprMutationAnalyzer.cpp
parentaca532f14dd3360d0025e0b9d5ae606be046cf79 (diff)
downloadbcm5719-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.cpp42
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(
OpenPOWER on IntegriCloud