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

Reply via email to