diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 466c9b08c5788..8fc28c0903418 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -246,6 +246,10 @@ Changes in existing checks customizable namespace. This further allows for testing the libc when the system-libc is also LLVM's libc. +- Improved :doc:`misc-const-correctness + ` check to avoid false positive when + using pointer to member function. + - Improved :doc:`misc-include-cleaner ` check by adding option `DeduplicateFindings` to output one finding per symbol occurrence. diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp index 186e3cf5a179b..cb6bfcc1dccba 100644 --- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-values.cpp @@ -976,3 +976,16 @@ void auto_usage_variants() { auto &auto_td1 = auto_td0; auto *auto_td2 = &auto_td0; } + +using PointerToMemberFunction = int (Value::*)(); +void member_pointer(Value &x, PointerToMemberFunction m) { + Value &member_pointer_tmp = x; + (member_pointer_tmp.*m)(); +} + +using PointerToConstMemberFunction = int (Value::*)() const; +void member_pointer_const(Value &x, PointerToConstMemberFunction m) { + Value &member_pointer_tmp = x; + // CHECK-MESSAGES:[[@LINE-1]]:3: warning: variable 'member_pointer_tmp' of type 'Value &' can be declared 'const' + (member_pointer_tmp.*m)(); +} diff --git a/clang/docs/ReleaseNotes.rst b/clang/docs/ReleaseNotes.rst index 688454d6b562e..8a136aae5489a 100644 --- a/clang/docs/ReleaseNotes.rst +++ b/clang/docs/ReleaseNotes.rst @@ -502,6 +502,9 @@ Static Analyzer bitwise shift operators produce undefined behavior (because some operand is negative or too large). +- Fix false positive in mutation check when using pointer to member function. + (`#66204: `_). + - The ``alpha.security.taint.TaintPropagation`` checker no longer propagates taint on ``strlen`` and ``strnlen`` calls, unless these are marked explicitly propagators in the user-provided taint configuration file. diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp index 90803830ff419..f2e1beb025423 100644 --- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp +++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp @@ -100,6 +100,20 @@ AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) { return Node.isPotentiallyEvaluated(); } +AST_MATCHER(CXXMemberCallExpr, isConstCallee) { + const Decl *CalleeDecl = Node.getCalleeDecl(); + const auto *VD = dyn_cast_or_null(CalleeDecl); + if (!VD) + return false; + const QualType T = VD->getType().getCanonicalType(); + const auto *MPT = dyn_cast(T); + const auto *FPT = MPT ? cast(MPT->getPointeeType()) + : dyn_cast(T); + if (!FPT) + return false; + return FPT->isConst(); +} + AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr, ast_matchers::internal::Matcher, InnerMatcher) { if (Node.isTypePredicate()) @@ -274,8 +288,8 @@ const Stmt *ExprMutationAnalyzer::findDirectMutation(const Expr *Exp) { const auto NonConstMethod = cxxMethodDecl(unless(isConst())); const auto AsNonConstThis = expr(anyOf( - cxxMemberCallExpr(callee(NonConstMethod), - on(canResolveToExpr(equalsNode(Exp)))), + cxxMemberCallExpr(on(canResolveToExpr(equalsNode(Exp))), + unless(isConstCallee())), cxxOperatorCallExpr(callee(NonConstMethod), hasArgument(0, canResolveToExpr(equalsNode(Exp)))), // In case of a templated type, calling overloaded operators is not @@ -391,7 +405,9 @@ const Stmt *ExprMutationAnalyzer::findMemberMutation(const Expr *Exp) { match(findAll(expr(anyOf(memberExpr(hasObjectExpression( canResolveToExpr(equalsNode(Exp)))), cxxDependentScopeMemberExpr(hasObjectExpression( - canResolveToExpr(equalsNode(Exp)))))) + canResolveToExpr(equalsNode(Exp)))), + binaryOperator(hasOperatorName(".*"), + hasLHS(equalsNode(Exp))))) .bind(NodeID::value)), Stm, Context); return findExprMutation(MemberExprs); diff --git a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp index b886259ef4d77..c0a398394093c 100644 --- a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp +++ b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp @@ -284,6 +284,36 @@ TEST(ExprMutationAnalyzerTest, TypeDependentMemberCall) { EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("x.push_back(T())")); } +TEST(ExprMutationAnalyzerTest, MemberPointerMemberCall) { + { + const auto AST = + buildASTFromCode("struct X {};" + "using T = int (X::*)();" + "void f(X &x, T m) { X &ref = x; (ref.*m)(); }"); + const auto Results = + match(withEnclosingCompound(declRefTo("ref")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(ref .* m)()")); + } + { + const auto AST = + buildASTFromCode("struct X {};" + "using T = int (X::*)();" + "void f(X &x, T const m) { X &ref = x; (ref.*m)(); }"); + const auto Results = + match(withEnclosingCompound(declRefTo("ref")), AST->getASTContext()); + EXPECT_THAT(mutatedBy(Results, AST.get()), ElementsAre("(ref .* m)()")); + } + { + const auto AST = + buildASTFromCode("struct X {};" + "using T = int (X::*)() const;" + "void f(X &x, T m) { X &ref = x; (ref.*m)(); }"); + const auto Results = + match(withEnclosingCompound(declRefTo("ref")), AST->getASTContext()); + EXPECT_FALSE(isMutated(Results, AST.get())); + } +} + // Section: overloaded operators TEST(ExprMutationAnalyzerTest, NonConstOperator) {