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

>From ea23565c8b2ccf6d1e4df86cdbda970376242055 Mon Sep 17 00:00:00 2001
From: Dave MacLachlan <[email protected]>
Date: Thu, 30 Jul 2026 07:17:19 -0700
Subject: [PATCH 1/2] [clang][ast][objc] Implement getNameForDiagnostic for
 ObjCMethodDecl and ObjCPropertyDecl.

This overrides getNameForDiagnostic to provide a qualified name representation 
for Objective-C methods and properties in diagnostics. When qualified is true, 
it formats them using the standard Objective-C syntax, such as -[Class 
selector] or +[Class property]. Otherwise, it falls back to printName.
---
 clang/include/clang/AST/DeclObjC.h |   6 +
 clang/lib/AST/DeclObjC.cpp         |  45 +++++++
 clang/unittests/AST/DeclTest.cpp   | 186 ++++++++++++++++++++++++++++-
 3 files changed, 236 insertions(+), 1 deletion(-)

diff --git a/clang/include/clang/AST/DeclObjC.h 
b/clang/include/clang/AST/DeclObjC.h
index 2541edba83855..6b9aac57e6a50 100644
--- a/clang/include/clang/AST/DeclObjC.h
+++ b/clang/include/clang/AST/DeclObjC.h
@@ -247,6 +247,9 @@ class ObjCMethodDecl : public NamedDecl, public DeclContext 
{
     return 
static_cast<ObjCDeclQualifier>(ObjCMethodDeclBits.objcDeclQualifier);
   }
 
+  void getNameForDiagnostic(raw_ostream &OS, const PrintingPolicy &Policy,
+                            bool Qualified) const override;
+
   void setObjCDeclQualifier(ObjCDeclQualifier QV) {
     ObjCMethodDeclBits.objcDeclQualifier = QV;
   }
@@ -793,6 +796,9 @@ class ObjCPropertyDecl : public NamedDecl {
 
   static ObjCPropertyDecl *CreateDeserialized(ASTContext &C, GlobalDeclID ID);
 
+  void getNameForDiagnostic(raw_ostream &OS, const PrintingPolicy &Policy,
+                            bool Qualified) const override;
+
   SourceLocation getAtLoc() const { return AtLoc; }
   void setAtLoc(SourceLocation L) { AtLoc = L; }
 
diff --git a/clang/lib/AST/DeclObjC.cpp b/clang/lib/AST/DeclObjC.cpp
index 5f80bf7aa29d8..795f97ec12f66 100644
--- a/clang/lib/AST/DeclObjC.cpp
+++ b/clang/lib/AST/DeclObjC.cpp
@@ -865,6 +865,24 @@ ObjCMethodDecl 
*ObjCMethodDecl::CreateDeserialized(ASTContext &C,
                                     Selector(), QualType(), nullptr, nullptr);
 }
 
+void ObjCMethodDecl::getNameForDiagnostic(raw_ostream &OS,
+                                          const PrintingPolicy &Policy,
+                                          bool Qualified) const {
+  if (Qualified) {
+    OS << (isInstanceMethod() ? '-' : '+');
+    OS << '[';
+    if (const auto *ID = getClassInterface())
+      OS << ID->getName();
+    else if (const auto *PD = dyn_cast<ObjCProtocolDecl>(getDeclContext()))
+      OS << PD->getName();
+    else
+      OS << "<Unknown>";
+    OS << ' ' << getSelector() << ']';
+  } else {
+    printName(OS, Policy);
+  }
+}
+
 bool ObjCMethodDecl::isDirectMethod() const {
   return hasAttr<ObjCDirectAttr>() &&
          !getASTContext().getLangOpts().ObjCDisableDirectMethodsForTesting;
@@ -2365,6 +2383,33 @@ ObjCPropertyDecl 
*ObjCPropertyDecl::CreateDeserialized(ASTContext &C,
                                       QualType(), nullptr, None);
 }
 
+void ObjCPropertyDecl::getNameForDiagnostic(raw_ostream &OS,
+                                            const PrintingPolicy &Policy,
+                                            bool Qualified) const {
+  if (Qualified) {
+    OS << (isInstanceProperty() ? '-' : '+');
+    OS << '[';
+    const ObjCContainerDecl *Parent = nullptr;
+    if (const auto *MD = getGetterMethodDecl()) {
+      Parent = MD->getClassInterface();
+      if (!Parent)
+        Parent = dyn_cast<ObjCProtocolDecl>(MD->getDeclContext());
+    }
+    if (!Parent) {
+      Parent = dyn_cast<ObjCContainerDecl>(getDeclContext());
+    }
+
+    if (Parent)
+      OS << Parent->getName();
+    else
+      OS << "<Unknown>";
+
+    OS << ' ' << getName() << ']';
+  } else {
+    printName(OS, Policy);
+  }
+}
+
 QualType ObjCPropertyDecl::getUsageType(QualType objectType) const {
   return DeclType.substObjCMemberType(objectType, getDeclContext(),
                                       ObjCSubstitutionContext::Property);
diff --git a/clang/unittests/AST/DeclTest.cpp b/clang/unittests/AST/DeclTest.cpp
index b95d361896e21..4919977300fb8 100644
--- a/clang/unittests/AST/DeclTest.cpp
+++ b/clang/unittests/AST/DeclTest.cpp
@@ -13,19 +13,25 @@
 #include "clang/AST/Decl.h"
 #include "clang/AST/ASTContext.h"
 #include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclObjC.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/AST/Mangle.h"
+#include "clang/AST/TypeBase.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/ASTMatchers/ASTMatchers.h"
 #include "clang/Basic/ABI.h"
 #include "clang/Basic/Diagnostic.h"
 #include "clang/Basic/LLVM.h"
+#include "clang/Basic/Linkage.h"
 #include "clang/Basic/TargetInfo.h"
 #include "clang/Lex/Lexer.h"
 #include "clang/Tooling/Tooling.h"
-#include "llvm/IR/DataLayout.h"
+#include "llvm/Support/raw_ostream.h"
 #include "llvm/Testing/Annotations/Annotations.h"
 #include "gtest/gtest.h"
+#include <cassert>
+#include <memory>
+#include <string>
 
 using namespace clang::ast_matchers;
 using namespace clang::tooling;
@@ -725,3 +731,181 @@ TEST(Decl, NoWrittenArgsInImplicitlyInstantiatedVarSpec) {
   ASSERT_NE(VTSD, nullptr);
   EXPECT_EQ(VTSD->getTemplateArgsAsWritten(), nullptr);
 }
+
+TEST(Decl, ObjCMethodDeclNameForDiagnostic) {
+  const char *Code = R"objc(
+    @protocol MyProtocol
+    - (void)myProtocolMethod;
+    @end
+
+    @interface MyClass
+    - (void)myMethod:(int)x;
+    + (void)myClassMethod;
+    @end
+
+    @interface MyClass (MyCategory)
+    - (void)myCategoryMethod;
+    @end
+
+    @interface MyClass ()
+    - (void)myExtensionMethod;
+    @end
+  )objc";
+
+  auto AST = tooling::buildASTFromCodeWithArgs(Code, {"-x", "objective-c"});
+  ASTContext &Ctx = AST->getASTContext();
+
+  auto const *IM = selectFirst<ObjCMethodDecl>(
+      "im", match(objcMethodDecl(hasName("myMethod:")).bind("im"), Ctx));
+  ASSERT_NE(IM, nullptr);
+
+  std::string IMQualifiedName;
+  llvm::raw_string_ostream IMQualifiedOS(IMQualifiedName);
+  IM->getNameForDiagnostic(IMQualifiedOS, Ctx.getPrintingPolicy(),
+                           /*Qualified=*/true);
+  EXPECT_EQ(IMQualifiedOS.str(), "-[MyClass myMethod:]");
+
+  std::string IMUnqualifiedName;
+  llvm::raw_string_ostream IMUnqualifiedOS(IMUnqualifiedName);
+  IM->getNameForDiagnostic(IMUnqualifiedOS, Ctx.getPrintingPolicy(),
+                           /*Qualified=*/false);
+  EXPECT_EQ(IMUnqualifiedOS.str(), "myMethod:");
+
+  auto const *CM = selectFirst<ObjCMethodDecl>(
+      "cm", match(objcMethodDecl(hasName("myClassMethod")).bind("cm"), Ctx));
+  ASSERT_NE(CM, nullptr);
+
+  std::string CMQualifiedName;
+  llvm::raw_string_ostream CMQualifiedOS(CMQualifiedName);
+  CM->getNameForDiagnostic(CMQualifiedOS, Ctx.getPrintingPolicy(),
+                           /*Qualified=*/true);
+  EXPECT_EQ(CMQualifiedOS.str(), "+[MyClass myClassMethod]");
+
+  std::string CMUnqualifiedName;
+  llvm::raw_string_ostream CMUnqualifiedOS(CMUnqualifiedName);
+  CM->getNameForDiagnostic(CMUnqualifiedOS, Ctx.getPrintingPolicy(),
+                           /*Qualified=*/false);
+  EXPECT_EQ(CMUnqualifiedOS.str(), "myClassMethod");
+
+  auto const *PM = selectFirst<ObjCMethodDecl>(
+      "pm", match(objcMethodDecl(hasName("myProtocolMethod")).bind("pm"), 
Ctx));
+  ASSERT_NE(PM, nullptr);
+
+  std::string PMQualifiedName;
+  llvm::raw_string_ostream PMQualifiedOS(PMQualifiedName);
+  PM->getNameForDiagnostic(PMQualifiedOS, Ctx.getPrintingPolicy(),
+                           /*Qualified=*/true);
+  EXPECT_EQ(PMQualifiedOS.str(), "-[MyProtocol myProtocolMethod]");
+
+  auto const *CatM = selectFirst<ObjCMethodDecl>(
+      "catm",
+      match(objcMethodDecl(hasName("myCategoryMethod")).bind("catm"), Ctx));
+  ASSERT_NE(CatM, nullptr);
+
+  std::string CatMQualifiedName;
+  llvm::raw_string_ostream CatMQualifiedOS(CatMQualifiedName);
+  CatM->getNameForDiagnostic(CatMQualifiedOS, Ctx.getPrintingPolicy(),
+                             /*Qualified=*/true);
+  EXPECT_EQ(CatMQualifiedOS.str(), "-[MyClass myCategoryMethod]");
+
+  auto const *ExtM = selectFirst<ObjCMethodDecl>(
+      "extm",
+      match(objcMethodDecl(hasName("myExtensionMethod")).bind("extm"), Ctx));
+  ASSERT_NE(ExtM, nullptr);
+
+  std::string ExtMQualifiedName;
+  llvm::raw_string_ostream ExtMQualifiedOS(ExtMQualifiedName);
+  ExtM->getNameForDiagnostic(ExtMQualifiedOS, Ctx.getPrintingPolicy(),
+                             /*Qualified=*/true);
+  EXPECT_EQ(ExtMQualifiedOS.str(), "-[MyClass myExtensionMethod]");
+}
+
+TEST(Decl, ObjCPropertyDeclNameForDiagnostic) {
+  const char *Code = R"objc(
+    @protocol MyProtocol
+    @property int myProtocolProp;
+    @end
+
+    @interface MyClass
+    @property int myProp;
+    @property(class) int myClassProp;
+    @end
+
+    @interface MyClass (MyCategory)
+    @property int myCategoryProp;
+    @end
+
+    @interface MyClass ()
+    @property int extensionProp;
+    @end
+  )objc";
+
+  auto AST = tooling::buildASTFromCodeWithArgs(Code, {"-x", "objective-c"});
+  ASTContext &Ctx = AST->getASTContext();
+
+  auto const *P = selectFirst<ObjCPropertyDecl>(
+      "p", match(objcPropertyDecl(hasName("myProp")).bind("p"), Ctx));
+  ASSERT_NE(P, nullptr);
+
+  std::string PQualifiedName;
+  llvm::raw_string_ostream PQualifiedOS(PQualifiedName);
+  P->getNameForDiagnostic(PQualifiedOS, Ctx.getPrintingPolicy(),
+                          /*Qualified=*/true);
+  EXPECT_EQ(PQualifiedOS.str(), "-[MyClass myProp]");
+
+  std::string PUnqualifiedName;
+  llvm::raw_string_ostream PUnqualifiedOS(PUnqualifiedName);
+  P->getNameForDiagnostic(PUnqualifiedOS, Ctx.getPrintingPolicy(),
+                          /*Qualified=*/false);
+  EXPECT_EQ(PUnqualifiedOS.str(), "myProp");
+
+  auto const *CP = selectFirst<ObjCPropertyDecl>(
+      "cp", match(objcPropertyDecl(hasName("myClassProp")).bind("cp"), Ctx));
+  ASSERT_NE(CP, nullptr);
+
+  std::string CPQualifiedName;
+  llvm::raw_string_ostream CPQualifiedOS(CPQualifiedName);
+  CP->getNameForDiagnostic(CPQualifiedOS, Ctx.getPrintingPolicy(),
+                           /*Qualified=*/true);
+  EXPECT_EQ(CPQualifiedOS.str(), "+[MyClass myClassProp]");
+
+  std::string CPUnqualifiedName;
+  llvm::raw_string_ostream CPUnqualifiedOS(CPUnqualifiedName);
+  CP->getNameForDiagnostic(CPUnqualifiedOS, Ctx.getPrintingPolicy(),
+                           /*Qualified=*/false);
+  EXPECT_EQ(CPUnqualifiedOS.str(), "myClassProp");
+
+  auto const *PP = selectFirst<ObjCPropertyDecl>(
+      "pp", match(objcPropertyDecl(hasName("myProtocolProp")).bind("pp"), 
Ctx));
+  ASSERT_NE(PP, nullptr);
+
+  std::string PPQualifiedName;
+  llvm::raw_string_ostream PPQualifiedOS(PPQualifiedName);
+  PP->getNameForDiagnostic(PPQualifiedOS, Ctx.getPrintingPolicy(),
+                           /*Qualified=*/true);
+  EXPECT_EQ(PPQualifiedOS.str(), "-[MyProtocol myProtocolProp]");
+
+  auto const *CatP = selectFirst<ObjCPropertyDecl>(
+      "catp",
+      match(objcPropertyDecl(hasName("myCategoryProp")).bind("catp"), Ctx));
+  ASSERT_NE(CatP, nullptr);
+
+  std::string CatPQualifiedName;
+  llvm::raw_string_ostream CatPQualifiedOS(CatPQualifiedName);
+  CatP->getNameForDiagnostic(CatPQualifiedOS, Ctx.getPrintingPolicy(),
+                             /*Qualified=*/true);
+  // We expect MyClass if getter is available, or if fallback looks through
+  // categories. Let's see what happens.
+  EXPECT_EQ(CatPQualifiedOS.str(), "-[MyClass myCategoryProp]");
+
+  auto const *ExtP = selectFirst<ObjCPropertyDecl>(
+      "extp",
+      match(objcPropertyDecl(hasName("extensionProp")).bind("extp"), Ctx));
+  ASSERT_NE(ExtP, nullptr);
+
+  std::string ExtPQualifiedName;
+  llvm::raw_string_ostream ExtPQualifiedOS(ExtPQualifiedName);
+  ExtP->getNameForDiagnostic(ExtPQualifiedOS, Ctx.getPrintingPolicy(),
+                             /*Qualified=*/true);
+  EXPECT_EQ(ExtPQualifiedOS.str(), "-[MyClass extensionProp]");
+}

>From 10ad68cb625949ae5cf16903e1d70b14c1dc06f4 Mon Sep 17 00:00:00 2001
From: Dave MacLachlan <[email protected]>
Date: Thu, 30 Jul 2026 07:52:12 -0700
Subject: [PATCH 2/2] Clean up some tests with new diagnostics format.

---
 .../WebKit/unretained-call-args-member.mm        |  6 +++---
 .../Checkers/WebKit/unretained-call-args.mm      | 16 ++++++++--------
 2 files changed, 11 insertions(+), 11 deletions(-)

diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args-member.mm 
b/clang/test/Analysis/Checkers/WebKit/unretained-call-args-member.mm
index fc9e9b421ae0e..d680b2e348670 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-call-args-member.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args-member.mm
@@ -20,7 +20,7 @@
 
 void Foo::bar() {
   [m_constObj doWork]; // no-warning
-  [m_obj doWork]; // expected-warning{{Receiver 'this->m_obj' (to 
'SomeObj::doWork') is a raw pointer to RetainPtr-capable type 'SomeObj'}}
+  [m_obj doWork]; // expected-warning{{Receiver 'this->m_obj' (to '-[SomeObj 
doWork]') is a raw pointer to RetainPtr-capable type 'SomeObj'}}
 }
 
 } // namespace call_args_const_retainptr_member
@@ -56,7 +56,7 @@
 
 void Bar::baz() {
   [m_constObj doWork]; // no-warning
-  [m_obj doWork]; // expected-warning{{Receiver 'this->m_obj' (to 
'SomeObj::doWork') is a raw pointer to RetainPtr-capable type 'SomeObj'}}
+  [m_obj doWork]; // expected-warning{{Receiver 'this->m_obj' (to '-[SomeObj 
doWork]') is a raw pointer to RetainPtr-capable type 'SomeObj'}}
 }
 
 } // namespace call_args_const_retainptr_struct_member
@@ -149,7 +149,7 @@
 
 void Foo::bar() {
   [m_constObj doWork]; // no-warning
-  [m_obj doWork]; // expected-warning{{Receiver 'this->m_obj' (to 
'SomeObj::doWork') is a raw pointer to RetainPtr-capable type 'SomeObj'}}
+  [m_obj doWork]; // expected-warning{{Receiver 'this->m_obj' (to '-[SomeObj 
doWork]') is a raw pointer to RetainPtr-capable type 'SomeObj'}}
 }
 
 } // namespace call_args_const_osobjectptr_receiver
diff --git a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm 
b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
index 007cf5571f9f6..037bc1571bbef 100644
--- a/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
+++ b/clang/test/Analysis/Checkers/WebKit/unretained-call-args.mm
@@ -216,7 +216,7 @@ void foo(CFMutableArrayRef param) {
       consume_more_cf(param);
     }
   }
-  
+
   namespace os_obj {
     void foo(dispatch_queue_t param) {
       consume_more_dispatch(param);
@@ -281,7 +281,7 @@ void foo() {
 
   void foo() {
     [provide() doWork];
-    // expected-warning@-1{{Receiver 'provide()' (to 'SomeObj::doWork') is a 
raw pointer to RetainPtr-capable type 'SomeObj'}}
+    // expected-warning@-1{{Receiver 'provide()' (to '-[SomeObj doWork]') is a 
raw pointer to RetainPtr-capable type 'SomeObj'}}
     [protectedProvide().get() doWork];
 
     CFArrayAppendValue(provide_cf(), nullptr);
@@ -291,7 +291,7 @@ void foo() {
 
   void bar() {
     [downcast<OtherObj>(protectedProvide().get()) 
doMoreWork:downcast<OtherObj>(provide())];
-    // expected-warning@-1{{Function argument 'downcast<OtherObj>(provide())' 
(parameter 'other' to 'OtherObj::doMoreWork:') is a raw pointer to 
RetainPtr-capable type 'OtherObj'}}
+    // expected-warning@-1{{Function argument 'downcast<OtherObj>(provide())' 
(parameter 'other' to '-[OtherObj doMoreWork:]') is a raw pointer to 
RetainPtr-capable type 'OtherObj'}}
     [protectedProvide().get() doWork];
   };
 
@@ -541,7 +541,7 @@ bool foo(NSString *obj) {
 
 bool bar(NSObject *obj, Bar *bar, SomeObjectSingleton someObjSingleton) {
   return [obj isKindOfClass:(bar->*someObjSingleton)()];
-  // expected-warning@-1{{Function argument '(bar ->* someObjSingleton)()' 
(parameter 'aClass' to 'NSObject::isKindOfClass:') is a RetainPtr-capable type 
'Class'}}
+  // expected-warning@-1{{Function argument '(bar ->* someObjSingleton)()' 
(parameter 'aClass' to '-[NSObject isKindOfClass:]') is a RetainPtr-capable 
type 'Class'}}
 }
 
 bool baz(NSObject *obj) {
@@ -656,9 +656,9 @@ - (void)doWork:(NSString *)msg, ... {
 - (void)doWorkOnSelf {
   [self doWork:nil];
   [self doWork:@"hello", provide(), provide_cf(), provide_dispatch()];
-  // expected-warning@-1{{Function argument 'provide()' (to 
'TestObject::doWork:') is a raw pointer to RetainPtr-capable type 'SomeObj'}}
-  // expected-warning@-2{{Function argument 'provide_cf()' (to 
'TestObject::doWork:') is a RetainPtr-capable type 'CFMutableArrayRef'}}
-  // expected-warning@-3{{Function argument 'provide_dispatch()' (to 
'TestObject::doWork:') is a RetainPtr-capable type 'dispatch_queue_t'}}
+  // expected-warning@-1{{Function argument 'provide()' (to '-[TestObject 
doWork:]') is a raw pointer to RetainPtr-capable type 'SomeObj'}}
+  // expected-warning@-2{{Function argument 'provide_cf()' (to '-[TestObject 
doWork:]') is a RetainPtr-capable type 'CFMutableArrayRef'}}
+  // expected-warning@-3{{Function argument 'provide_dispatch()' (to 
'-[TestObject doWork:]') is a RetainPtr-capable type 'dispatch_queue_t'}}
   [self doWork:@"hello", RetainPtr<SomeObj> { provide() }.get(), 
RetainPtr<CFMutableArrayRef> { provide_cf() }.get(), OSObjectPtr { 
provide_dispatch() }.get()];
   [self doWork:__null];
   [self doWork:nil];
@@ -681,7 +681,7 @@ + (SomeObj *)sharedObj
 
 - (void)doWorkOnSomeObj {
     [[self getSomeObj] doWork];
-    // expected-warning@-1{{Receiver '[self getSomeObj]' (to 
'SomeObj::doWork') is a raw pointer to RetainPtr-capable type 'SomeObj'}}
+    // expected-warning@-1{{Receiver '[self getSomeObj]' (to '-[SomeObj 
doWork]') is a raw pointer to RetainPtr-capable type 'SomeObj'}}
     [[TestObject sharedObj] doWork];
 }
 

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

Reply via email to