https://github.com/dmaclach updated 
https://github.com/llvm/llvm-project/pull/216158

>From 58772e7819096f7e4723922186810acb60b22081 Mon Sep 17 00:00:00 2001
From: Dave MacLachlan <[email protected]>
Date: Thu, 13 Aug 2026 12:28:09 -0700
Subject: [PATCH 1/3] [include-cleaner]Support toll-free bridged casts in
 include-cleaner.

Handle `CK_CPointerToObjCPointerCast` in `WalkAST` to report implicit 
references to the destination Objective-C interface and its protocols. This 
ensures that toll-free bridging casts from C pointers to Objective-C pointers 
are correctly tracked. Also adds unit tests for various bridged cast types.
---
 .../include-cleaner/lib/WalkAST.cpp           |  8 +-
 .../include-cleaner/unittests/WalkASTTest.cpp | 88 +++++++++++++++++++
 2 files changed, 95 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp 
b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
index 7d15f96405903..3587e2334de93 100644
--- a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
+++ b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
@@ -15,6 +15,7 @@
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Expr.h"
 #include "clang/AST/ExprCXX.h"
+#include "clang/AST/OperationKinds.h"
 #include "clang/AST/RecursiveASTVisitor.h"
 #include "clang/AST/TemplateBase.h"
 #include "clang/AST/TemplateName.h"
@@ -482,13 +483,18 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> {
   bool VisitCastExpr(CastExpr *E) {
     // Handle implicit or explicit casts between Objective-C object pointers
     // aimed towards protocol-qualification (e.g., `ClassName *` to
-    // `id<Proto>`).
+    // `id<Proto>`), as well as toll-free bridged C-pointer-to-ObjC casts.
     QualType SourceType = E->getSubExpr()->getType();
     QualType DestType = E->getType();
 
     const auto *SrcPtr = SourceType->getAs<ObjCObjectPointerType>();
     const auto *DestPtr = DestType->getAs<ObjCObjectPointerType>();
 
+    if (E->getCastKind() == CK_CPointerToObjCPointerCast) {
+      if (DestPtr && DestPtr->getInterfaceDecl())
+        report(E->getExprLoc(), DestPtr->getInterfaceDecl(), 
RefType::Implicit);
+    }
+
     // If we're casting from a known class pointer to protocol conformance.
     if (SrcPtr && DestPtr && SrcPtr->getInterfaceDecl()) {
       const ObjCInterfaceDecl *Class = SrcPtr->getInterfaceDecl();
diff --git a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp 
b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
index cf9a5a365edb6..418aafe6ec76e 100644
--- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
+++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
@@ -1164,5 +1164,93 @@ TEST(WalkAST, ObjCIvarRefExprFree) {
            {"-x", "objective-c"});
 }
 
+TEST(WalkAST, ObjCBridgedCastExprToObjC) {
+  testWalk(R"objc(
+    typedef const struct __CFString *CFStringRef;
+    @interface $explicit^NSString
+    @end
+  )objc",
+           R"objc(
+    void test(CFStringRef cf) {
+      NSString *s = (__bridge ^NSString *)cf;
+    }
+  )objc",
+           {"-x", "objective-c", "-fobjc-arc"});
+}
+
+TEST(WalkAST, ObjCBridgedCastExprToCF) {
+  testWalk(R"objc(
+    typedef const struct __CFString * $explicit^CFStringRef;
+    @interface NSString
+    @end
+  )objc",
+           R"objc(
+    void test(NSString *s) {
+      CFStringRef cf = (__bridge ^CFStringRef)s;
+    }
+  )objc",
+           {"-x", "objective-c", "-fobjc-arc"});
+}
+
+TEST(WalkAST, ObjCBridgedCastExprBridgeTransfer) {
+  testWalk(R"objc(
+    typedef const struct __CFString *CFStringRef;
+    @interface $explicit^NSString
+    @end
+  )objc",
+           R"objc(
+    void test(CFStringRef cf) {
+      NSString *s = (__bridge_transfer ^NSString *)cf;
+    }
+  )objc",
+           {"-x", "objective-c", "-fobjc-arc"});
+}
+
+TEST(WalkAST, ObjCBridgedCastExprBridgeRetained) {
+  testWalk(R"objc(
+    typedef const struct __CFString * $explicit^CFStringRef;
+    @interface NSString
+    @end
+  )objc",
+           R"objc(
+    void test(NSString *s) {
+      CFStringRef cf = (__bridge_retained ^CFStringRef)s;
+    }
+  )objc",
+           {"-x", "objective-c", "-fobjc-arc"});
+}
+
+TEST(WalkAST, ObjCTollFreeBridgeCStyleCast) {
+  testWalk(R"objc(
+    typedef const struct __attribute__((objc_bridge(NSString))) __CFString * 
CFStringRef;
+    @interface $explicit^NSString
+    @end
+  )objc",
+           R"objc(
+    void test(CFStringRef cf) {
+      NSString *s = (^NSString *)cf;
+    }
+  )objc",
+           {"-x", "objective-c"});
+}
+
+TEST(WalkAST, ObjCBridgedCastExprToProtocol) {
+  // Note this test case is handled by TraverseObjCProtocolLoc instead of
+  // VisitCastExpr.
+  // It is here for completeness.
+  testWalk(R"objc(
+    typedef const struct __CFString *CFStringRef;
+    @protocol $explicit^MyProtocol
+    - (void)doSomething;
+    @end
+  )objc",
+           R"objc(
+    void test(CFStringRef cf) {
+      id<MyProtocol> p = (__bridge id<^MyProtocol>)cf;
+    }
+  )objc",
+           {"-x", "objective-c", "-fobjc-arc"});
+}
+
 } // namespace
 } // namespace clang::include_cleaner

>From a2291482cb08b696e629f74f179e9d3063a5caa5 Mon Sep 17 00:00:00 2001
From: Dave MacLachlan <[email protected]>
Date: Mon, 24 Aug 2026 10:47:14 -0700
Subject: [PATCH 2/3] Added test that I missed copying in. Add comment to be
 clear what change implements.

---
 .../include-cleaner/lib/WalkAST.cpp              |  1 +
 .../include-cleaner/unittests/WalkASTTest.cpp    | 16 +++++++++++++++-
 2 files changed, 16 insertions(+), 1 deletion(-)

diff --git a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp 
b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
index 3587e2334de93..10a5c5fda7100 100644
--- a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
+++ b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
@@ -490,6 +490,7 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> {
     const auto *SrcPtr = SourceType->getAs<ObjCObjectPointerType>();
     const auto *DestPtr = DestType->getAs<ObjCObjectPointerType>();
 
+    // Handles non-arc CPointer to ObjCPointer casts.
     if (E->getCastKind() == CK_CPointerToObjCPointerCast) {
       if (DestPtr && DestPtr->getInterfaceDecl())
         report(E->getExprLoc(), DestPtr->getInterfaceDecl(), 
RefType::Implicit);
diff --git a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp 
b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
index 418aafe6ec76e..243664a1e7c05 100644
--- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
+++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
@@ -1222,7 +1222,8 @@ TEST(WalkAST, ObjCBridgedCastExprBridgeRetained) {
 
 TEST(WalkAST, ObjCTollFreeBridgeCStyleCast) {
   testWalk(R"objc(
-    typedef const struct __attribute__((objc_bridge(NSString))) __CFString * 
CFStringRef;
+    typedef const struct __attribute__((objc_bridge(NSString)))
+      __CFString * CFStringRef;
     @interface $explicit^NSString
     @end
   )objc",
@@ -1252,5 +1253,18 @@ TEST(WalkAST, ObjCBridgedCastExprToProtocol) {
            {"-x", "objective-c", "-fobjc-arc"});
 }
 
+TEST(WalkAST, ObjCImplicitPointerCast) {
+  testWalk(R"objc(
+    @interface $implicit^NSString
+    @end
+  )objc",
+           R"objc(
+    NSString *foo(void *p) {
+      return ^p;
+    }
+  )objc",
+           {"-x", "objective-c"});
+}
+
 } // namespace
 } // namespace clang::include_cleaner

>From cbaff9c0293209860bb5d1532928c729de9e4e20 Mon Sep 17 00:00:00 2001
From: Dave MacLachlan <[email protected]>
Date: Mon, 24 Aug 2026 12:08:49 -0700
Subject: [PATCH 3/3] - Add missing test cases. - Add missing cast from id -
 Restructure cast function to reduce indentation with some fast returns.

---
 .../include-cleaner/lib/WalkAST.cpp           | 90 +++++++++----------
 .../include-cleaner/unittests/WalkASTTest.cpp | 21 ++++-
 2 files changed, 62 insertions(+), 49 deletions(-)

diff --git a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp 
b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
index 10a5c5fda7100..56c85761132cf 100644
--- a/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
+++ b/clang-tools-extra/include-cleaner/lib/WalkAST.cpp
@@ -483,59 +483,57 @@ class ASTWalker : public RecursiveASTVisitor<ASTWalker> {
   bool VisitCastExpr(CastExpr *E) {
     // Handle implicit or explicit casts between Objective-C object pointers
     // aimed towards protocol-qualification (e.g., `ClassName *` to
-    // `id<Proto>`), as well as toll-free bridged C-pointer-to-ObjC casts.
-    QualType SourceType = E->getSubExpr()->getType();
-    QualType DestType = E->getType();
+    // `id<Proto>`), as well as C-pointer-to-ObjC and id-to-ObjC pointer casts.
+    const auto *DestPtr = E->getType()->getAs<ObjCObjectPointerType>();
 
-    const auto *SrcPtr = SourceType->getAs<ObjCObjectPointerType>();
-    const auto *DestPtr = DestType->getAs<ObjCObjectPointerType>();
+    if (!DestPtr)
+      return true;
 
-    // Handles non-arc CPointer to ObjCPointer casts.
-    if (E->getCastKind() == CK_CPointerToObjCPointerCast) {
-      if (DestPtr && DestPtr->getInterfaceDecl())
-        report(E->getExprLoc(), DestPtr->getInterfaceDecl(), 
RefType::Implicit);
-    }
+    const auto *SrcPtr =
+        E->getSubExpr()->getType()->getAs<ObjCObjectPointerType>();
+
+    // Handles non-arc CPointer to ObjCPointer and id to ObjCPointer casts.
+    if (isa<ImplicitCastExpr>(E) &&
+        (E->getCastKind() == CK_CPointerToObjCPointerCast ||
+         (SrcPtr && SrcPtr->isObjCIdType())))
+      report(E->getExprLoc(), DestPtr->getInterfaceDecl(), RefType::Implicit);
+
+    if (!SrcPtr)
+      return true;
 
     // If we're casting from a known class pointer to protocol conformance.
-    if (SrcPtr && DestPtr && SrcPtr->getInterfaceDecl()) {
-      const ObjCInterfaceDecl *Class = SrcPtr->getInterfaceDecl();
-      ASTContext &Ctx = Class->getASTContext();
-
-      // For every protocol required by the destination type.
-      for (const ObjCProtocolDecl *Proto : DestPtr->quals()) {
-        const ObjCInterfaceDecl *Current = Class;
-        // Search the inheritance hierarchy for the provider of conformance.
-        while (Current) {
-          bool ConformsDirectly = false;
-          for (const auto *PI : Current->protocols()) {
-            if (Ctx.ProtocolCompatibleWithProtocol(
-                    const_cast<ObjCProtocolDecl *>(Proto),
-                    const_cast<ObjCProtocolDecl *>(PI))) {
-              ConformsDirectly = true;
-              break;
-            }
-          }
-          // If the class itself provides the conformance directly, we don't
-          // need to keep searching Categories.
-          if (ConformsDirectly)
-            break;
+    const ObjCInterfaceDecl *Class = SrcPtr->getInterfaceDecl();
+    if (!Class)
+      return true;
 
-          // If the class doesn't declare direct conformance but conformance is
-          // injected via a visible Category attached to this class, note that
-          // the category header is required by recording an Implicit reference
-          // to it.
-          for (const auto *Cat : Current->visible_categories()) {
-            for (auto *PI : Cat->protocols()) {
-              if (Ctx.ProtocolCompatibleWithProtocol(
-                      const_cast<ObjCProtocolDecl *>(Proto),
-                      const_cast<ObjCProtocolDecl *>(PI))) {
-                report(E->getExprLoc(), const_cast<ObjCCategoryDecl *>(Cat),
-                       RefType::Implicit);
-              }
-            }
+    ASTContext &Ctx = Class->getASTContext();
+
+    // For every protocol required by the destination type.
+    for (const ObjCProtocolDecl *Proto : DestPtr->quals()) {
+      const ObjCInterfaceDecl *Current = Class;
+      // Search the inheritance hierarchy for the provider of conformance.
+      while (Current) {
+        bool ConformsDirectly = false;
+        for (auto *PI : Current->protocols()) {
+          if (Ctx.ProtocolCompatibleWithProtocol(Proto, PI)) {
+            ConformsDirectly = true;
+            break;
           }
-          Current = Current->getSuperClass();
         }
+        // If the class itself provides the conformance directly, we don't
+        // need to keep searching Categories.
+        if (ConformsDirectly)
+          break;
+
+        // If the class doesn't declare direct conformance but conformance is
+        // injected via a visible Category attached to this class, note that
+        // the category header is required by recording an Implicit reference
+        // to it.
+        for (auto *Cat : Current->visible_categories())
+          for (auto *PI : Cat->protocols())
+            if (Ctx.ProtocolCompatibleWithProtocol(Proto, PI))
+              report(E->getExprLoc(), Cat, getCategoryRefType(Cat));
+        Current = Current->getSuperClass();
       }
     }
     return true;
diff --git a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp 
b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
index 243664a1e7c05..6f2b0c1bb525f 100644
--- a/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
+++ b/clang-tools-extra/include-cleaner/unittests/WalkASTTest.cpp
@@ -1253,14 +1253,29 @@ TEST(WalkAST, ObjCBridgedCastExprToProtocol) {
            {"-x", "objective-c", "-fobjc-arc"});
 }
 
-TEST(WalkAST, ObjCImplicitPointerCast) {
+TEST(WalkAST, ObjCImplicitVoidPointerCast) {
   testWalk(R"objc(
     @interface $implicit^NSString
     @end
+    void cast(NSString *p);
   )objc",
            R"objc(
-    NSString *foo(void *p) {
-      return ^p;
+    void foo(void *p) {
+      cast(^p);
+    }
+  )objc",
+           {"-x", "objective-c"});
+}
+
+TEST(WalkAST, ObjCImplicitIdPointerCast) {
+  testWalk(R"objc(
+    @interface $implicit^NSString
+    @end
+    void cast(NSString *p);
+  )objc",
+           R"objc(
+   void foo(id p) {
+      cast(^p);
     }
   )objc",
            {"-x", "objective-c"});

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

Reply via email to