Author: Benedek Kaibas Date: 2026-10-27T14:20:49+01:00 New Revision: 19757e98b55f1eac2636f1e898df1b7f050a07c7
URL: https://github.com/llvm/llvm-project/commit/19757e98b55f1eac2636f1e898df1b7f050a07c7 DIFF: https://github.com/llvm/llvm-project/commit/19757e98b55f1eac2636f1e898df1b7f050a07c7.diff LOG: [analyzer] Fix checker message order in the program state dump (#235091) Until now in `runCheckersForPrintStateJson` we iterated over the `CheckerTags` to receive the checkers' message. `CheckerTags` is a `DenseMap`. `CheckerTags` is keyed by the checker addresses and its iterator order follows the hash of the addresses. However, these addresses can differ between processes. That causes the nondeterminism of `CheckerTags` which is not a problem as long as there is only one checker present in the program state dumps. Where multiple checkers' messages appear in the program state dump iterating over a `DenseMap` causes nondeterministic order of the checkers' messages. This issue was discussed in #233889 here: https://github.com/llvm/llvm-project/pull/233889#discussion_r4327775391. Using `CHECK` instead of `CHECK-NEXT` silenced these errors in the test suite, but did not fix the problem. In this PR I have fixed the problem by iterating over `CheckerTags` and sorting the `CheckerBackend`s in an alphabetical order. Added: clang/test/Analysis/checker-order-in-dump.cpp Modified: clang/lib/StaticAnalyzer/Core/CheckerManager.cpp Removed: ################################################################################ diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp index 6da79452d7ab0..6f95e59e53aa1 100644 --- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp +++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -23,6 +23,7 @@ #include "clang/StaticAnalyzer/Core/PathSensitive/CoreEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h" #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h" +#include "llvm/ADT/STLExtras.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/ErrorHandling.h" #include "llvm/Support/FormatVariadic.h" @@ -856,51 +857,38 @@ void CheckerManager::runCheckersForPrintStateJson(raw_ostream &Out, Indent(NLOut, InnerSpace, IsDot) << "\""; // then begin the next message. ++Space; - bool HasMessage = false; - // Store the last CheckerTag. - const void *LastCT = nullptr; + std::vector<std::pair<std::string, std::string>> CheckerMsgs; for (const auto &CT : CheckerTags) { - // See whether the current checker has a message. CT.second->printState(TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/""); - - if (TempBuf.empty()) - continue; - - if (!HasMessage) { - Out << '[' << NL; - HasMessage = true; - } - - LastCT = &CT; + const std::string Msg = TempBuf.str().trim().str(); TempBuf.clear(); - } - - for (const auto &CT : CheckerTags) { - // See whether the current checker has a message. - CT.second->printState(TempOut, State, /*NL=*/NewLine.c_str(), /*Sep=*/""); - if (TempBuf.empty()) - continue; + if (!Msg.empty()) + CheckerMsgs.push_back({CT.second->getDebugTag().str(), Msg}); + } + llvm::sort(CheckerMsgs); - Indent(Out, Space, IsDot) << "{ \"checker\": \"" << CT.second->getDebugTag() - << "\", \"messages\": [" << NL; - Indent(Out, InnerSpace, IsDot) - << '\"' << TempBuf.str().trim() << '\"' << NL; - Indent(Out, Space, IsDot) << "]}"; + if (!CheckerMsgs.empty()) + Out << '[' << NL; - if (&CT != LastCT) - Out << ','; - Out << NL; - - TempBuf.clear(); - } + llvm::interleave( + CheckerMsgs, + [&](const std::pair<std::string, std::string> &CM) { + Indent(Out, Space, IsDot) + << "{ \"checker\": \"" << CM.first << "\", \"messages\": [" << NL; + Indent(Out, InnerSpace, IsDot) << '\"' << CM.second << '\"' << NL; + Indent(Out, Space, IsDot) << "]}"; + }, + [&] { Out << ',' << NL; }); // It is the last element of the 'program_state' so do not add a comma. - if (HasMessage) + if (!CheckerMsgs.empty()) { + Out << NL; Indent(Out, --Space, IsDot) << "]"; - else + } else { Out << "null"; + } Out << NL; } diff --git a/clang/test/Analysis/checker-order-in-dump.cpp b/clang/test/Analysis/checker-order-in-dump.cpp new file mode 100644 index 0000000000000..49c3c694b95c1 --- /dev/null +++ b/clang/test/Analysis/checker-order-in-dump.cpp @@ -0,0 +1,41 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,cplusplus.Move \ +// RUN: -analyzer-checker=alpha.core.LifetimeModeling,debug.ExprInspection \ +// RUN: -analyzer-config cfg-lifetime=true \ +// RUN: -verify %s 2>&1 | FileCheck %s + +#include "Inputs/system-header-simulator-cxx.h" + +void clang_analyzer_printState(); + +class A { +public: + A() = default; + A(const A&); + A(A &&); + int foo() const; +}; + +void testCheckersOrderInStateDump() { + { + A a; + A *p = &a; + A b = std::move(a); + + clang_analyzer_printState(); + (void)p; +// CHECK: "checker_messages": [ +// CHECK-NEXT: { "checker": "alpha.core.LifetimeModeling", "messages": [ +// CHECK-NEXT: "LifetimeBound bindings:", +// CHECK-NEXT: " Origin &a contains Loan a", +// CHECK-NEXT: "" +// CHECK-NEXT: ]}, +// CHECK-NEXT: { "checker": "cplusplus.Move", "messages": [ +// CHECK-NEXT: "Moved-from objects :", +// CHECK-NEXT: "a: moved", +// CHECK-NEXT: "" +// CHECK-NEXT: ]} +// CHECK-NEXT: ] + + a.foo(); // expected-warning {{Method called on moved-from object 'a'}} + } +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
