Author: Balázs Benics
Date: 2026-06-10T09:03:13+01:00
New Revision: a424861be0f82ca8e05dd0ae3fb433805a1fec20

URL: 
https://github.com/llvm/llvm-project/commit/a424861be0f82ca8e05dd0ae3fb433805a1fec20
DIFF: 
https://github.com/llvm/llvm-project/commit/a424861be0f82ca8e05dd0ae3fb433805a1fec20.diff

LOG: [clang][ssaf] CallGraph extractor should ignore objc callees for now 
(#202606)

Ignoring them is better than crashing/asserting on nullptr derefs.

Fixes: rdar://179104950

Added: 
    

Modified: 
    clang/include/clang/Analysis/CallGraph.h
    
clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
    
clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp

Removed: 
    


################################################################################
diff  --git a/clang/include/clang/Analysis/CallGraph.h 
b/clang/include/clang/Analysis/CallGraph.h
index 76d1bd1dc9f34..f80790902e6b1 100644
--- a/clang/include/clang/Analysis/CallGraph.h
+++ b/clang/include/clang/Analysis/CallGraph.h
@@ -194,7 +194,8 @@ class CallGraphNode {
   Decl *getDecl() const { return FD; }
 
   FunctionDecl *getDefinition() const {
-    return getDecl()->getAsFunction()->getDefinition();
+    FunctionDecl *FD = getDecl()->getAsFunction();
+    return FD ? FD->getDefinition() : nullptr;
   }
 
   void print(raw_ostream &os) const;

diff  --git 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
index 6e0b0ee8fe834..6dfb66a283674 100644
--- 
a/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
+++ 
b/clang/lib/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractor.cpp
@@ -72,9 +72,13 @@ void CallGraphExtractor::handleCallGraphNode(const 
ASTContext &Ctx,
     // never null.
     assert(CalleeDecl);
 
-    // FIXME: `clang::CallGraph` does not consider ObjCMessageExprs as calls.
-    // Consequently, they don't appear as a Callee.
-    assert(!isa<ObjCMethodDecl>(CalleeDecl));
+    // `clang::CallGraph` resolves ObjCMessageExprs (including property
+    // dot-syntax) to their ObjCMethodDecls and adds them as callees — see
+    // `CGBuilder::VisitObjCMessageExpr` in clang/lib/Analysis/CallGraph.cpp.
+    // ObjC dispatch is dynamic, so recording these as direct callees would be
+    // misleading; skip them until we model ObjC properly.
+    if (isa<ObjCMethodDecl>(CalleeDecl))
+      continue;
 
     // FIXME: `clang::CallGraph` does not create entries for primary templates.
     assert(!CalleeDecl->isTemplated());

diff  --git 
a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp
 
b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp
index 39b3cf604857d..183fd7155cfba 100644
--- 
a/clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp
+++ 
b/clang/unittests/ScalableStaticAnalysisFramework/Analyses/CallGraph/CallGraphExtractorTest.cpp
@@ -424,6 +424,48 @@ TEST_F(CallGraphExtractorTest, ObjCMessageExprs) {
       hasSummaryThat(HasNoDirectCallees(), HasNoVirtualCallees()));
 }
 
+TEST_F(CallGraphExtractorTest, ObjCMessageToLocallyImplementedMethod) {
+  // When the receiver class is implemented in the same TU, `clang::CallGraph`
+  // resolves the ObjCMessageExpr (via `lookupPrivateMethod`) to an
+  // ObjCMethodDecl and adds it as a callee. The extractor must not abort on
+  // these — ObjC dispatch is dynamic and we don't model it as a direct callee.
+  runExtractor(R"cpp(
+    @interface NSObject @end
+    @interface Inner : NSObject
+    - (int) val;
+    @end
+    @implementation Inner
+    - (int) val { return 42; }
+    @end
+    int caller(Inner *x) { return [x val]; }
+  )cpp",
+               {"-x", "objective-c"});
+
+  ASSERT_THAT_EXPECTED(
+      findSummary("caller"),
+      hasSummaryThat(HasNoDirectCallees(), HasNoVirtualCallees()));
+}
+
+TEST_F(CallGraphExtractorTest, ObjCPropertyDotSyntax) {
+  // Property dot-syntax (`x.val`) desugars to an ObjCMessageExpr to the
+  // synthesized getter; same dynamic dispatch as bracket syntax. Cover it
+  // explicitly because real-world hits often come through dot-syntax.
+  runExtractor(R"cpp(
+    @interface NSObject @end
+    @interface Inner : NSObject
+    @property (readonly) int val;
+    @end
+    @implementation Inner
+    @end
+    int caller(Inner *x) { return x.val; }
+  )cpp",
+               {"-x", "objective-c"});
+
+  ASSERT_THAT_EXPECTED(
+      findSummary("caller"),
+      hasSummaryThat(HasNoDirectCallees(), HasNoVirtualCallees()));
+}
+
 TEST_F(CallGraphExtractorTest, DefinitionLocation) {
   runExtractor(R"cpp(
     void callee_with_def() {}


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

Reply via email to