[llvm] [clang-analysis]Fix false positive in mutation check when using pointer to member function (PR #66846)

Congcong Cai via llvm-commits llvm-commits at lists.llvm.org
Mon Sep 25 06:27:28 PDT 2023


https://github.com/HerrCai0907 updated https://github.com/llvm/llvm-project/pull/66846

>From 6d8e737ab1a883371a7491b676e1e202a087701f Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Tue, 19 Sep 2023 16:15:20 +0800
Subject: [PATCH 1/2] [clang-analysis]Fix false positive in mutation check when
 using pointer to member function

Fixes: #66204
---
 clang-tools-extra/docs/ReleaseNotes.rst       |  4 +++
 .../misc/const-correctness-values.cpp         | 13 ++++++++
 clang/docs/ReleaseNotes.rst                   |  2 ++
 clang/lib/Analysis/ExprMutationAnalyzer.cpp   | 22 ++++++++++++--
 .../Analysis/ExprMutationAnalyzerTest.cpp     | 30 +++++++++++++++++++
 5 files changed, 68 insertions(+), 3 deletions(-)

diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index a2cde526a8c04d9..4370059653dfc09 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -222,6 +222,10 @@ Changes in existing checks
   <clang-tidy/checks/llvm/namespace-comment>` check to provide fixes for
   ``inline`` namespaces in the same format as :program:`clang-format`.
 
+- Improved :doc:`misc-const-correctness
+  <clang-tidy/checks/misc/const-correctness>` check to avoid false positive when
+  using pointer to member function.
+
 - Improved :doc:`misc-include-cleaner
   <clang-tidy/checks/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 186e3cf5a179b24..cb6bfcc1dccba39 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 5b2a6349a5b15bf..1594e4d012a4aff 100644
--- a/clang/docs/ReleaseNotes.rst
+++ b/clang/docs/ReleaseNotes.rst
@@ -384,6 +384,8 @@ 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.
+
 .. _release-notes-sanitizers:
 
 Sanitizers
diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
index 90803830ff41949..fcd909be88c999b 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 auto *CalleeDecl = Node.getCalleeDecl();
+  const auto *VD = dyn_cast_or_null<ValueDecl>(CalleeDecl);
+  if (!VD)
+    return false;
+  const auto T = VD->getType().getCanonicalType();
+  const auto *MPT = dyn_cast<MemberPointerType>(T);
+  const auto *FPT = MPT ? cast<FunctionProtoType>(MPT->getPointeeType())
+                        : dyn_cast<FunctionProtoType>(T);
+  if (!FPT)
+    return false;
+  return FPT->isConst();
+}
+
 AST_MATCHER_P(GenericSelectionExpr, hasControllingExpr,
               ast_matchers::internal::Matcher<Expr>, 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(allOf(hasOperatorName(".*"),
+                                                    hasLHS(equalsNode(Exp))))))
                         .bind(NodeID<Expr>::value)),
             Stm, Context);
   return findExprMutation(MemberExprs);
diff --git a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
index b886259ef4d7709..96972401ebb5eda 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) {

>From a7894d680b2732430ac5ca2c2155391be710e91a Mon Sep 17 00:00:00 2001
From: Congcong Cai <congcongcai0907 at 163.com>
Date: Thu, 21 Sep 2023 14:26:27 +0800
Subject: [PATCH 2/2] not use auto

---
 clang/lib/Analysis/ExprMutationAnalyzer.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
index fcd909be88c999b..7bbd721c379b7e0 100644
--- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp
+++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -101,11 +101,11 @@ AST_MATCHER(CXXTypeidExpr, isPotentiallyEvaluated) {
 }
 
 AST_MATCHER(CXXMemberCallExpr, isConstCallee) {
-  const auto *CalleeDecl = Node.getCalleeDecl();
+  const Decl *CalleeDecl = Node.getCalleeDecl();
   const auto *VD = dyn_cast_or_null<ValueDecl>(CalleeDecl);
   if (!VD)
     return false;
-  const auto T = VD->getType().getCanonicalType();
+  const QualType T = VD->getType().getCanonicalType();
   const auto *MPT = dyn_cast<MemberPointerType>(T);
   const auto *FPT = MPT ? cast<FunctionProtoType>(MPT->getPointeeType())
                         : dyn_cast<FunctionProtoType>(T);



More information about the llvm-commits mailing list