https://github.com/vbvictor created 
https://github.com/llvm/llvm-project/pull/206515

`ExprPointeeResolve` now resolves through the `int*` to `void*` bitcast when 
the destination pointee is non-const

Fixes https://github.com/llvm/llvm-project/issues/206429.

>From 78c3758d0eeb10213cf261cd8cff1ce0edfc3b7e Mon Sep 17 00:00:00 2001
From: Victor Baranov <[email protected]>
Date: Mon, 29 Jun 2026 18:53:12 +0300
Subject: [PATCH] [clang-tidy] Fix FP in misc-const-correctness

---
 clang-tools-extra/docs/ReleaseNotes.rst       |  3 +
 .../const-correctness-pointer-as-pointers.cpp | 32 +++++++++
 clang/lib/Analysis/ExprMutationAnalyzer.cpp   |  7 ++
 .../Analysis/ExprMutationAnalyzerTest.cpp     | 68 +++++++++++++++++++
 4 files changed, 110 insertions(+)

diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index 2a980cc089a65..dc10bc59804ea 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -571,6 +571,9 @@ Changes in existing checks
   - Fixed false positives when pointers were later passed or bound through
     ``const``-qualified pointer references.
 
+  - Fixed false positive where a pointer returned as a non-const ``void *``
+    was incorrectly diagnosed as allowing its pointee to be made ``const``.
+
 - 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-pointer-as-pointers.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-pointers.cpp
index 41df6c44f5bef..f6bfb98c548c3 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-pointers.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/misc/const-correctness-pointer-as-pointers.cpp
@@ -56,6 +56,38 @@ void *return_non_const() {
   return a;
 }
 
+void *return_as_void_pointer() {
+  int i = 0;
+  int *p_local0 = &i;
+  // CHECK-NOT: warning
+  return p_local0;
+}
+
+const void *return_as_const_void_pointer() {
+  int i = 0;
+  int *p_local0 = &i;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: pointee of variable 'p_local0' of 
type 'int *' can be declared 'const'
+  // CHECK-FIXES: int  const*p_local0 = &i;
+  return p_local0;
+}
+
+void take_void_pointer(void *);
+void pass_as_void_pointer() {
+  int i = 0;
+  int *p_local0 = &i;
+  // CHECK-NOT: warning
+  take_void_pointer(p_local0);
+}
+
+void take_const_void_pointer(const void *);
+void pass_as_const_void_pointer() {
+  int i = 0;
+  int *p_local0 = &i;
+  // CHECK-MESSAGES: [[@LINE-1]]:3: warning: pointee of variable 'p_local0' of 
type 'int *' can be declared 'const'
+  // CHECK-FIXES: int  const*p_local0 = &i;
+  take_const_void_pointer(p_local0);
+}
+
 void function_pointer_basic() {
   void (*const fp)() = nullptr;
   fp();
diff --git a/clang/lib/Analysis/ExprMutationAnalyzer.cpp 
b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
index 3709fd0af3486..66d8eacdd54e6 100644
--- a/clang/lib/Analysis/ExprMutationAnalyzer.cpp
+++ b/clang/lib/Analysis/ExprMutationAnalyzer.cpp
@@ -160,6 +160,13 @@ class ExprPointeeResolve {
         if (CastType == SubExprType)
           return resolveExpr(ICE->getSubExpr());
       }
+      if (kind == CK_BitCast) {
+        const QualType DestType = ICE->getType();
+        const QualType SubExprType = ICE->getSubExpr()->getType();
+        if (DestType->isPointerType() && SubExprType->isPointerType() &&
+            !DestType->getPointeeType().isConstQualified())
+          return resolveExpr(ICE->getSubExpr());
+      }
       return false;
     }
 
diff --git a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp 
b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
index 1545310431fd8..c19c8b0fe0b02 100644
--- a/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
+++ b/clang/unittests/Analysis/ExprMutationAnalyzerTest.cpp
@@ -1788,6 +1788,21 @@ TEST(ExprMutationAnalyzerTest, 
PointeeMutatedByInitToNonConst) {
         match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
     EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
   }
+  {
+    const std::string Code = "void f() { int* x = nullptr; void* b = x; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code =
+        "void f() { int* x = nullptr; const void* b = x; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+  }
 }
 
 TEST(ExprMutationAnalyzerTest, PointeeMutatedByAssignToNonConst) {
@@ -1806,6 +1821,21 @@ TEST(ExprMutationAnalyzerTest, 
PointeeMutatedByAssignToNonConst) {
         match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
     EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
   }
+  {
+    const std::string Code = "void f() { int* x = nullptr; void* b; b = x; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code =
+        "void f() { int* x = nullptr; const void* b; b = x; }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+  }
 }
 
 TEST(ExprMutationAnalyzerTest, PointeeMutatedByPassAsArgument) {
@@ -1874,6 +1904,22 @@ TEST(ExprMutationAnalyzerTest, 
PointeeMutatedByPassAsArgument) {
         match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
     EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
   }
+  {
+    const std::string Code =
+        "void b(void*); void f() { int* x = nullptr; b(x); }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code =
+        "void b(const void*); void f() { int* x = nullptr; b(x); }";
+    auto AST = buildASTFromCodeWithArgs(Code, {});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+  }
   {
     const std::string Code =
         "namespace std { typedef decltype(sizeof(int)) size_t; }"
@@ -2157,6 +2203,28 @@ TEST(ExprMutationAnalyzerTest, PointeeMutatedByReturn) {
         match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
     EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
   }
+  {
+    const std::string Code = R"(
+    void * f() {
+      int *const x = nullptr;
+      return x;
+    })";
+    auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_TRUE(isPointeeMutated(Results, AST.get()));
+  }
+  {
+    const std::string Code = R"(
+    const void * f() {
+      int *const x = nullptr;
+      return x;
+    })";
+    auto AST = buildASTFromCodeWithArgs(Code, {"-Wno-everything"});
+    auto Results =
+        match(withEnclosingCompound(declRefTo("x")), AST->getASTContext());
+    EXPECT_FALSE(isPointeeMutated(Results, AST.get()));
+  }
 }
 
 TEST(ExprMutationAnalyzerTest, PointeeMutatedByPointerToMemberOperator) {

_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to