https://github.com/marco-milanese-sonarsource updated https://github.com/llvm/llvm-project/pull/219225
>From 59f08b836ae562a43939d8317b5855bafed5e2c0 Mon Sep 17 00:00:00 2001
From: Marco Milanese <marco.milanese at sonarsource.com>
Date: Thu, 27 Aug 2026 16:42:47 +0200
Subject: [PATCH 1/2] Add regression test
---
clang/test/Analysis/ctu/stu-workremaining.cpp | 21 +++++++++++++++++++
1 file changed, 21 insertions(+)
create mode 100644 clang/test/Analysis/ctu/stu-workremaining.cpp
diff --git a/clang/test/Analysis/ctu/stu-workremaining.cpp b/clang/test/Analysis/ctu/stu-workremaining.cpp
new file mode 100644
index 0000000000000..1524a98ec9a93
--- /dev/null
+++ b/clang/test/Analysis/ctu/stu-workremaining.cpp
@@ -0,0 +1,21 @@
+// RUN: %clang_analyze_cc1 -std=c++20 \
+// RUN: -analyzer-checker=core,alpha.deadcode.UnreachableCode \
+// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=true \
+// RUN: -analyzer-config max-nodes=10 \
+// RUN: -verify=ctu-on %s
+// ctu-on-no-diagnostics
+
+// RUN: %clang_analyze_cc1 -std=c++20 \
+// RUN: -analyzer-checker=core,alpha.deadcode.UnreachableCode \
+// RUN: -analyzer-config experimental-enable-naive-ctu-analysis=false \
+// RUN: -analyzer-config max-nodes=10 \
+// RUN: -verify=ctu-off %s
+// ctu-off-no-diagnostics
+
+#define NOP ((void)0)
+
+void entrypoint(int x) {
+ NOP; NOP; NOP; NOP; NOP;
+ NOP; NOP; NOP; NOP; NOP;
+ if (x) NOP;
+}
>From 0cad94e9c5d5918005f1d45a057b7641acd224a0 Mon Sep 17 00:00:00 2001
From: Marco Milanese <marco.milanese at sonarsource.com>
Date: Thu, 27 Aug 2026 17:47:59 +0200
Subject: [PATCH 2/2] Track whether STU finished with work remaining
---
.../clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h | 5 +++++
clang/lib/StaticAnalyzer/Core/CoreEngine.cpp | 1 +
2 files changed, 6 insertions(+)
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
index d26c0d9257b0f..a5c24dd97e3d2 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h
@@ -82,6 +82,10 @@ class CoreEngine {
/// usually because it could not reason about something.
BlocksAborted blocksAborted;
+ /// Whether the single-TU phase ran out of budget with work left over.
+ /// The CTU phase replaces \c WList, so this has to be remembered separately.
+ bool STUHadWorkRemaining = false;
+
/// The information about functions shared by the whole translation unit.
/// (This data is owned by AnalysisConsumer.)
FunctionSummariesTy *FunctionSummaries;
@@ -148,6 +152,7 @@ class CoreEngine {
bool wasBlocksExhausted() const { return !blocksExhausted.empty(); }
bool hasWorkRemaining() const { return wasBlocksExhausted() ||
WList->hasWork() ||
+ STUHadWorkRemaining ||
wasBlockAborted(); }
/// Inform the CoreEngine that a basic block was aborted because
diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
index 307c96b23b206..85fddd16057a9 100644
--- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp
@@ -161,6 +161,7 @@ bool CoreEngine::ExecuteWorkList(const StackFrame *SF, unsigned MaxSteps,
return MaxSteps - Steps;
};
const unsigned STUSteps = ProcessWList(MaxSteps);
+ STUHadWorkRemaining = WList->hasWork();
if (CTUWList) {
NumSTUSteps += STUSteps;