[clang] [clang-tools-extra] [clang][clang-tidy] Fixes false positive with a non-const method on a pointer (PR #188844)
Berkay Sahin via cfe-commits
cfe-commits at lists.llvm.org
Sun May 24 03:54:38 PDT 2026
https://github.com/berkaysahiin updated https://github.com/llvm/llvm-project/pull/188844
>From 939e926a0f72e6620f545ad02de7fe3122a818c1 Mon Sep 17 00:00:00 2001
From: Berkay Sahin <berkaysahindev at gmail.com>
Date: Fri, 27 Mar 2026 00:03:11 +0300
Subject: [PATCH 1/4] [clang][clang-tidy] False-positive with non-const methods
on pointer variables
---
clang-tools-extra/docs/ReleaseNotes.rst | 4 ++++
.../checkers/misc/const-correctness-parameters.cpp | 7 +++++--
.../misc/const-correctness-pointer-as-values.cpp | 11 +++++++++++
clang/lib/Analysis/ExprMutationAnalyzer.cpp | 4 +++-
4 files changed, 23 insertions(+), 3 deletions(-)
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index f8550e72dcc85..0818eac233c8b 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -289,6 +289,10 @@ Changes in existing checks
- Fixed false positive where a pointer used with placement new was
incorrectly diagnosed as allowing the pointee to be made ``const``.
+ - Fixed false positive where calling a non-const member function on a
+ pointer was incorrectly treated as mutating the pointer, when it only
+ mutates the pointee.
+
- Improved :doc:`misc-multiple-inheritance
<clang-tidy/checks/misc/multiple-inheritance>` by avoiding false positives when
virtual inheritance causes concrete bases to be counted more than once.
diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-parameters.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-parameters.cpp
index 8ff099eadb5a9..a8d90f1b582bf 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-parameters.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-parameters.cpp
@@ -40,6 +40,8 @@ void pointer_param_read_only(Bar* b) {
}
void pointer_param_mutated_pointee(Bar* b) {
+ // CHECK-MESSAGES: [[@LINE-1]]:36: warning: variable 'b' of type 'Bar *' can be declared 'const'
+ // CHECK-FIXES: void pointer_param_mutated_pointee(Bar* const b) {
b->mutating_method();
}
@@ -505,8 +507,9 @@ void struct_ptr_param(Bar** bp) {
}
void struct_ptr_param_modified(Bar** bp) {
- // CHECK-MESSAGES: [[@LINE-1]]:32: warning: variable 'bp' of type 'Bar **' can be declared 'const'
- // CHECK-FIXES: void struct_ptr_param_modified(Bar** const bp) {
+ // CHECK-MESSAGES: [[@LINE-1]]:32: warning: pointee of variable 'bp' of type 'Bar **' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-2]]:32: warning: variable 'bp' of type 'Bar **' can be declared 'const'
+ // CHECK-FIXES: void struct_ptr_param_modified(Bar* const* const bp) {
(*bp)->mutating_method();
}
diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp
index 02d32c0ec73e5..41cf3d8b4aa05 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp
@@ -72,3 +72,14 @@ void instantiate() {
// CHECK-FIXES: int *const p_local0[4] = {nullptr, nullptr, nullptr, nullptr};
EmitProtocolMethodList(p_local0);
}
+
+struct Mutating {
+ void mutating_method();
+};
+
+void pointer_member_call_not_pointer_mutation() {
+ Mutating *p = nullptr;
+ // CHECK-MESSAGES: warning: variable 'p' of type 'Mutating *' can be declared 'const'
+ // CHECK-FIXES: Mutating *const p = nullptr;
+ p->mutating_method();
+}
diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
index 5def6ba3cac5a..ad7ce91e0f966 100644
--- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp
+++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -404,7 +404,9 @@ ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) {
const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
const auto AsNonConstThis = expr(anyOf(
- cxxMemberCallExpr(on(canResolveToExpr(Exp)), unless(isConstCallee())),
+ cxxMemberCallExpr(
+ on(canResolveToExpr(Exp)),
+ unless(anyOf(isConstCallee(), thisPointerType(pointerType())))),
cxxOperatorCallExpr(callee(NonConstMethod),
hasArgument(0, canResolveToExpr(Exp))),
// In case of a templated type, calling overloaded operators is not
>From 37470bd0d95ef863d5d96af0a71ead1704d825b8 Mon Sep 17 00:00:00 2001
From: Berkay Sahin <berkaysahindev at gmail.com>
Date: Sun, 5 Apr 2026 00:00:04 +0300
Subject: [PATCH 2/4] Address PR comments
---
.../checkers/misc/const-correctness-pointer-as-values.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp
index 41cf3d8b4aa05..0875e79e22f17 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-values.cpp
@@ -79,7 +79,7 @@ struct Mutating {
void pointer_member_call_not_pointer_mutation() {
Mutating *p = nullptr;
- // CHECK-MESSAGES: warning: variable 'p' of type 'Mutating *' can be declared 'const'
+ // CHECK-MESSAGES: [[@LINE-1]]:3: warning: variable 'p' of type 'Mutating *' can be declared 'const'
// CHECK-FIXES: Mutating *const p = nullptr;
p->mutating_method();
}
>From 58ebdeac4bfc8d35cdaa0eca990957479f3c095e Mon Sep 17 00:00:00 2001
From: Berkay Sahin <berkaysahindev at gmail.com>
Date: Sun, 24 May 2026 13:18:20 +0300
Subject: [PATCH 3/4] [clang][clang-tidy] Add a comment on what we match
---
clang/lib/Analysis/ExprMutationAnalyzer.cpp | 3 +++
1 file changed, 3 insertions(+)
diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
index ad7ce91e0f966..7e06300c93395 100644
--- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp
+++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -404,9 +404,12 @@ ExprMutationAnalyzer::Analyzer::findDirectMutation(const Expr *Exp) {
const auto NonConstMethod = cxxMethodDecl(unless(isConst()));
const auto AsNonConstThis = expr(anyOf(
+ // For member calls through a pointer, the pointer variable
+ // itself is not mutated but only the pointee is mutated.
cxxMemberCallExpr(
on(canResolveToExpr(Exp)),
unless(anyOf(isConstCallee(), thisPointerType(pointerType())))),
+
cxxOperatorCallExpr(callee(NonConstMethod),
hasArgument(0, canResolveToExpr(Exp))),
// In case of a templated type, calling overloaded operators is not
>From 46a32c50c33f5364ae8ab532a8e42c1c1b18c452 Mon Sep 17 00:00:00 2001
From: Berkay Sahin <berkaysahindev at gmail.com>
Date: Sun, 24 May 2026 13:31:29 +0300
Subject: [PATCH 4/4] [clang][clang-tidy] Add tests
---
.../Analysis/ExprMutationAnalyzerTest.cpp | 16 ++++++++++++++++
1 file changed, 16 insertions(+)
diff --git a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
index c63479dc26e0b..ebd5ef9520796 100644
--- a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -280,6 +280,22 @@ TEST(ExprMutationAnalyzerTest, ConstMemberFunc) {
EXPECT_FALSE(isMutated(Results, AST.get()));
}
+TEST(ExprMutationAnalyzerTest, NonConstMemberFuncOnPointer) {
+ const auto AST = buildASTFromCode(
+ "void f() { struct Foo { void mf(); }; Foo *p; p->mf(); }");
+ const auto Results =
+ match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+ EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
+TEST(ExprMutationAnalyzerTest, ConstMemberFuncOnPointer) {
+ const auto AST = buildASTFromCode(
+ "void f() { struct Foo { void mf() const; }; Foo *p; p->mf(); }");
+ const auto Results =
+ match(withEnclosingCompound(declRefTo("p")), AST->getASTContext());
+ EXPECT_FALSE(isMutated(Results, AST.get()));
+}
+
TEST(ExprMutationAnalyzerTest, TypeDependentMemberCall) {
const auto AST = buildASTFromCodeWithArgs(
"template <class T> class vector { void push_back(T); }; "
More information about the cfe-commits
mailing list