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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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); }; " _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
