[clang] a424861 - [clang][ssaf] CallGraph extractor should ignore objc callees for now (#202606)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Jun 10 01:03:18 PDT 2026
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() {}
More information about the cfe-commits
mailing list