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 <[email protected]> Date: Thu, 27 Aug 2026 16:42:47 +0200 Subject: [PATCH 1/5] 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 <[email protected]> Date: Thu, 27 Aug 2026 17:47:59 +0200 Subject: [PATCH 2/5] 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; >From 098a10f514f865b557e1cd5ccf9a799ec61dab4b Mon Sep 17 00:00:00 2001 From: Marco Milanese <[email protected]> Date: Fri, 28 Aug 2026 08:52:26 +0200 Subject: [PATCH 3/5] Fix formatting issue --- .../clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h index a5c24dd97e3d2..46399793306b8 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -150,10 +150,10 @@ class CoreEngine { // Functions for external checking of whether we have unfinished work. bool wasBlockAborted() const { return !blocksAborted.empty(); } bool wasBlocksExhausted() const { return !blocksExhausted.empty(); } - bool hasWorkRemaining() const { return wasBlocksExhausted() || - WList->hasWork() || - STUHadWorkRemaining || - wasBlockAborted(); } + bool hasWorkRemaining() const { + return wasBlocksExhausted() || WList->hasWork() || STUHadWorkRemaining || + wasBlockAborted(); + } /// Inform the CoreEngine that a basic block was aborted because /// it could not be completely analyzed. >From b75c75cade2164039968f091b1e191dfb58a0b66 Mon Sep 17 00:00:00 2001 From: Marco Milanese <[email protected]> Date: Fri, 28 Aug 2026 14:28:05 +0200 Subject: [PATCH 4/5] Improve regression test --- .../ctu/stu-workremaining-with-ctu-calls.cpp | 70 +++++++++++++++++++ clang/test/Analysis/ctu/stu-workremaining.cpp | 14 ++-- 2 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 clang/test/Analysis/ctu/stu-workremaining-with-ctu-calls.cpp diff --git a/clang/test/Analysis/ctu/stu-workremaining-with-ctu-calls.cpp b/clang/test/Analysis/ctu/stu-workremaining-with-ctu-calls.cpp new file mode 100644 index 0000000000000..7a288ea4bc66d --- /dev/null +++ b/clang/test/Analysis/ctu/stu-workremaining-with-ctu-calls.cpp @@ -0,0 +1,70 @@ +// RUN: rm -rf %t +// RUN: mkdir -p %t +// RUN: split-file %s %t + +// RUN: %clang_cc1 -std=c++20 -emit-pch -o %t/other.cpp.ast %t/other.cpp + +// RUN: %clang_extdef_map %t/other.cpp -- -std=c++20 >> %t/externalDefMap.tmp.txt +// On windows, absolute paths generated by extdef_map are not recognized, +// so CSA prepends the workdir path to them. Force relative paths to work +// around this issue. +// RUN: sed -e 's| .*other\.cpp| other.cpp.ast|' \ +// RUN: %t/externalDefMap.tmp.txt > %t/externalDefMap.txt + +// 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=50 \ +// RUN: -verify=ctu-off %t/main.cpp + +// 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 ctu-dir=%t \ +// RUN: -analyzer-config max-nodes=50 \ +// RUN: -analyzer-config ctu-phase1-inlining=none \ +// RUN: -verify=ctu-on %t/main.cpp + +//--- main.cpp + +#define NOP ((void)0) + +extern void other(void); + +void tp_no_cross_call(int x) { + // ctu-on-warning@+4{{This statement is never executed}} + // ctu-on-warning@+3{{self-comparison always evaluates to false}} + // ctu-off-warning@+2{{This statement is never executed}} + // ctu-off-warning@+1{{self-comparison always evaluates to false}} + if (x != x) NOP; +} + +void tp_cross_call(int x) { + other(); + // ctu-on-warning@+4{{This statement is never executed}} + // ctu-on-warning@+3{{self-comparison always evaluates to false}} + // ctu-off-warning@+2{{This statement is never executed}} + // ctu-off-warning@+1{{self-comparison always evaluates to false}} + if (x != x) NOP; +} + +void fp_no_cross_call(int x) { + NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; + NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; + NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; + NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; + if (x) NOP; +} + +void fp_cross_call(int x) { + other(); + NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; + NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; + NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; + NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; + if (x) NOP; +} + +//--- other.cpp + +void other() { return; } diff --git a/clang/test/Analysis/ctu/stu-workremaining.cpp b/clang/test/Analysis/ctu/stu-workremaining.cpp index 1524a98ec9a93..2fb1e609e3b1d 100644 --- a/clang/test/Analysis/ctu/stu-workremaining.cpp +++ b/clang/test/Analysis/ctu/stu-workremaining.cpp @@ -3,19 +3,25 @@ // 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) { +void tp(int x) { + // ctu-on-warning@+4{{This statement is never executed}} + // ctu-on-warning@+3{{self-comparison always evaluates to false}} + // ctu-off-warning@+2{{This statement is never executed}} + // ctu-off-warning@+1{{self-comparison always evaluates to false}} + if (x != x) NOP; +} + +void fp(int x) { NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; NOP; - if (x) NOP; + if (x) NOP; // no-warning: the true branch might be alive even in CTU } >From 9a581d75ecee0cc0fc60856ef4e32cc915b3268f Mon Sep 17 00:00:00 2001 From: Marco Milanese <[email protected]> Date: Tue, 1 Sep 2026 11:01:47 +0200 Subject: [PATCH 5/5] Improve naming --- .../clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h | 6 +++--- .../clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h | 2 +- .../lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp | 2 +- clang/lib/StaticAnalyzer/Core/CoreEngine.cpp | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h index 46399793306b8..67bd1504d5ffb 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h @@ -84,7 +84,7 @@ class CoreEngine { /// 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; + bool exploredAllSTUPaths = false; /// The information about functions shared by the whole translation unit. /// (This data is owned by AnalysisConsumer.) @@ -150,8 +150,8 @@ class CoreEngine { // Functions for external checking of whether we have unfinished work. bool wasBlockAborted() const { return !blocksAborted.empty(); } bool wasBlocksExhausted() const { return !blocksExhausted.empty(); } - bool hasWorkRemaining() const { - return wasBlocksExhausted() || WList->hasWork() || STUHadWorkRemaining || + bool hasExploredAllPaths() const { + return wasBlocksExhausted() || WList->hasWork() || exploredAllSTUPaths || wasBlockAborted(); } diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h index 64a2ebe5149e4..195d63b0e0936 100644 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -467,7 +467,7 @@ class ExprEngine { // Functions for external checking of whether we have unfinished work. bool wasBlocksExhausted() const { return Engine.wasBlocksExhausted(); } bool hasEmptyWorkList() const { return !Engine.getWorkList()->hasWork(); } - bool hasWorkRemaining() const { return Engine.hasWorkRemaining(); } + bool hasExploredAllPaths() const { return Engine.hasExploredAllPaths(); } const CoreEngine &getCoreEngine() const { return Engine; } diff --git a/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp index 0ab282feb67bb..57b079e40e6c4 100644 --- a/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/UnreachableCodeChecker.cpp @@ -51,7 +51,7 @@ void UnreachableCodeChecker::checkEndAnalysis(ExplodedGraph &G, ExprEngine &Eng) const { CFGBlocksSet reachable, visited; - if (Eng.hasWorkRemaining()) + if (Eng.hasExploredAllPaths()) return; const Decl *D = nullptr; diff --git a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp index 85fddd16057a9..593346b2354a1 100644 --- a/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp +++ b/clang/lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -161,7 +161,7 @@ bool CoreEngine::ExecuteWorkList(const StackFrame *SF, unsigned MaxSteps, return MaxSteps - Steps; }; const unsigned STUSteps = ProcessWList(MaxSteps); - STUHadWorkRemaining = WList->hasWork(); + exploredAllSTUPaths = WList->hasWork(); if (CTUWList) { NumSTUSteps += STUSteps; _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
