[clang] [clang][analyzer] Model function addresses in constant initializers (PR #217608)
Radovan Božić via cfe-commits
cfe-commits at lists.llvm.org
Thu Aug 20 05:58:09 PDT 2026
https://github.com/bozicrHT created https://github.com/llvm/llvm-project/pull/217608
`SValBuilder::getConstantVal()` does not currently handle direct function addresses, as a result, a const function pointer initialized with a function is loaded into `UnknownVal`, preventing the analyzer from resolving and inlining calls through that pointer.
Fixes #216983
>From 9ef09e0c2a6f72c2ed0ff683c28e610d5fc8ed79 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Radovan=20Bo=C5=BEi=C4=87?= <radovan.bozic at htecgroup.com>
Date: Thu, 20 Aug 2026 14:50:13 +0200
Subject: [PATCH] [clang][analyzer] Model function addresses in constant
initializers
Teach `SValBuilder::getConstantVal()` to represent direct function
addresses using the existing `FunctionCodeRegion` SVal.
This allows the analyzer to resolve calls through constant function
pointers and preserve caller constraints.
---
clang/lib/StaticAnalyzer/Core/SValBuilder.cpp | 6 ++
.../test/Analysis/constant-function-pointer.c | 80 +++++++++++++++++++
2 files changed, 86 insertions(+)
create mode 100644 clang/test/Analysis/constant-function-pointer.c
diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index 57c97b2852445..54b4d9cd4dfed 100644
--- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -326,6 +326,12 @@ loc::MemRegionVal SValBuilder::getCXXThis(const CXXRecordDecl *D,
std::optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
E = E->IgnoreParens();
+ if (E->getType()->isFunctionPointerType()) {
+ if (const auto *FD =
+ dyn_cast_or_null<FunctionDecl>(E->getReferencedDeclOfCallee()))
+ return getFunctionPointer(FD);
+ }
+
switch (E->getStmtClass()) {
// Handle expressions that we treat differently from the AST's constant
// evaluator.
diff --git a/clang/test/Analysis/constant-function-pointer.c b/clang/test/Analysis/constant-function-pointer.c
new file mode 100644
index 0000000000000..dcd12eb1122e9
--- /dev/null
+++ b/clang/test/Analysis/constant-function-pointer.c
@@ -0,0 +1,80 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection \
+// RUN: -analyze-function=test_function_pointer_forms -verify=forms %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection \
+// RUN: -analyze-function=test_mutable_pointer -verify=mutable %s
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection \
+// RUN: -analyze-function=test_no_false_positive -verify=fp %s
+
+void clang_analyzer_checkInlined(int);
+void clang_analyzer_eval(int);
+
+typedef void (*callback)(unsigned);
+
+static void decay_target(unsigned value) {
+ clang_analyzer_checkInlined(value == 1); // forms-warning{{TRUE}}
+}
+
+static callback const decayCallback = decay_target;
+
+static void address_target(unsigned value) {
+ clang_analyzer_checkInlined(value == 2); // forms-warning{{TRUE}}
+}
+
+static callback const addressCallback = &address_target;
+
+static void cast_target(unsigned value) {
+ clang_analyzer_checkInlined(value == 3); // forms-warning{{TRUE}}
+}
+
+static callback const castCallback = (callback)cast_target;
+
+void test_function_pointer_forms(void) {
+ clang_analyzer_eval(decayCallback == decay_target); // forms-warning{{TRUE}}
+ decay_target(1);
+
+ clang_analyzer_eval(addressCallback == address_target); // forms-warning{{TRUE}}
+ address_target(2);
+
+ clang_analyzer_eval(castCallback == cast_target); // forms-warning{{TRUE}}
+ castCallback(3);
+}
+
+static callback mutableCallback = decay_target;
+
+void test_mutable_pointer(void) {
+ clang_analyzer_eval(mutableCallback == decay_target); // mutable-warning{{UNKNOWN}}
+}
+
+struct St {
+ int f;
+};
+
+static struct St sts[4];
+
+static void helper(unsigned id) {
+ struct St *s = 0;
+
+ if (id < 1)
+ return;
+ if (id < 5)
+ s = &sts[id-1];
+ else if (id == 5)
+ return;
+
+ s->f = 60;
+}
+
+static void notify(unsigned id) {
+ clang_analyzer_checkInlined(id >= 1 && id <= 4); // fp-warning{{TRUE}}
+ helper(id);
+}
+
+static callback const callbackFn = notify;
+
+void test_no_false_positive(unsigned id) {
+ if (id < 1 || id > 4)
+ return;
+
+ clang_analyzer_eval(callbackFn == notify); // fp-warning{{TRUE}}
+ callbackFn(id);
+}
More information about the cfe-commits
mailing list