[clang] c63b6d6 - [analyzer] Order UninitializedObject notes deterministically (#214208)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Aug 5 08:48:25 PDT 2026
Author: Balázs Benics
Date: 2026-08-05T15:48:19Z
New Revision: c63b6d6d1709de8975d937d4965cb0d32ff1477a
URL: https://github.com/llvm/llvm-project/commit/c63b6d6d1709de8975d937d4965cb0d32ff1477a
DIFF: https://github.com/llvm/llvm-project/commit/c63b6d6d1709de8975d937d4965cb0d32ff1477a.diff
LOG: [analyzer] Order UninitializedObject notes deterministically (#214208)
UninitFieldMap is keyed by FieldRegion pointers, so iterating it to emit
the notes ordered them by where those regions happened to be allocated.
The order therefore varied between runs: on cxx-uninitialized-object.cpp
the two notes of the report at line 363 swapped in roughly 3 of 12 runs.
- Emit the notes in source order instead, tie-broken by the note message.
- `-verify` matches notes by line, text and count and ignores their order,
so the new test pins the order down with FileCheck.
Assisted-By: claude
Added:
clang/test/Analysis/cxx-uninitialized-object-note-order.cpp
Modified:
clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
Removed:
################################################################################
diff --git a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
index 4d54f11efe158..2137b649f7541 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UninitializedObject/UninitializedObjectChecker.cpp
@@ -201,11 +201,30 @@ void UninitializedObjectChecker::checkEndFunction(
BT_uninitField, WarningOS.str(), Node, LocUsedForUniqueing,
Node->getStackFrame()->getDecl());
- for (const auto &Pair : UninitFields) {
- Report->addNote(Pair.second,
- PathDiagnosticLocation::create(Pair.first->getDecl(),
- Context.getSourceManager()));
+ using NoteTy = std::pair<PathDiagnosticLocation, StringRef>;
+ SmallVector<NoteTy> Notes;
+ const auto &SM = Context.getSourceManager();
+ for (const auto &[FieldRegion, NoteMsg] : UninitFields) {
+ auto FieldLoc = PathDiagnosticLocation::create(FieldRegion->getDecl(), SM);
+ Notes.emplace_back(FieldLoc, NoteMsg);
}
+
+ // Make the order deterministic.
+ llvm::sort(Notes, [](const NoteTy &LHS, const NoteTy &RHS) {
+ FullSourceLoc L = LHS.first.asLocation();
+ FullSourceLoc R = RHS.first.asLocation();
+ if (L != R)
+ return L.isBeforeInTranslationUnitThan(R);
+ // Comparing the field locs might not be enough so we might need a tie
+ // breaker.
+ // See the `cxx-uninitialized-object-note-order.cpp:fTwoInstances` test
+ // demonstrating this.
+ return LHS.second < RHS.second;
+ });
+
+ for (const auto &[Loc, NoteMsg] : Notes)
+ Report->addNote(NoteMsg, Loc);
+
Context.emitReport(std::move(Report));
}
diff --git a/clang/test/Analysis/cxx-uninitialized-object-note-order.cpp b/clang/test/Analysis/cxx-uninitialized-object-note-order.cpp
new file mode 100644
index 0000000000000..6221d224db912
--- /dev/null
+++ b/clang/test/Analysis/cxx-uninitialized-object-note-order.cpp
@@ -0,0 +1,80 @@
+// DEFINE: %{run} = %clang_analyze_cc1 \
+// DEFINE: -analyzer-checker=core,optin.cplusplus.UninitializedObject \
+// DEFINE: -analyzer-output=text -fno-caret-diagnostics %s
+
+// RUN: %{run} -verify
+// RUN: %{run} 2>&1 | FileCheck %s
+
+// ATTENTION:
+// We use FileCheck to ensure that the relative order of the notes is stable.
+// These notes used to be emitted in a non-deterministic order, which is not checked by `-verify`.
+
+struct MultipleSiblings {
+ int a; // expected-note {{uninitialized field 'this->a'}}
+ int b; // expected-note {{uninitialized field 'this->b'}}
+ int c; // expected-note {{uninitialized field 'this->c'}}
+ int d;
+ MultipleSiblings() { d = 0; }
+ // expected-warning at -1 {{3 uninitialized fields}}
+ // expected-note at -2 {{3 uninitialized fields}}
+};
+
+void fMultipleSiblings() {
+ MultipleSiblings s; // expected-note {{Calling default constructor for 'MultipleSiblings'}}
+}
+
+// CHECK-LABEL: warning: 3 uninitialized fields at the end of the constructor call
+// CHECK-NEXT: note: uninitialized field 'this->a'
+// CHECK-NEXT: note: uninitialized field 'this->b'
+// CHECK-NEXT: note: uninitialized field 'this->c'
+
+struct Inner {
+ int x;
+ // expected-note at -1 {{uninitialized field 'this->i.x'}}
+ // expected-note at -2 {{uninitialized field 'this->first.x'}}
+ // expected-note at -3 {{uninitialized field 'this->second.x'}}
+ int y;
+ // expected-note at -1 {{uninitialized field 'this->i.y'}}
+ // expected-note at -2 {{uninitialized field 'this->first.y'}}
+ // expected-note at -3 {{uninitialized field 'this->second.y'}}
+};
+
+struct Nested {
+ Inner i;
+ int z;
+ Nested() { z = 0; }
+ // expected-warning at -1 {{2 uninitialized fields}}
+ // expected-note at -2 {{2 uninitialized fields}}
+};
+
+void fNested() {
+ Nested n; // expected-note {{Calling default constructor for 'Nested'}}
+}
+
+// CHECK-LABEL: warning: 2 uninitialized fields at the end of the constructor call
+// CHECK-NEXT: note: uninitialized field 'this->i.x'
+// CHECK-NEXT: note: uninitialized field 'this->i.y'
+
+struct TwoInstances {
+ Inner first;
+ Inner second;
+ int z;
+ TwoInstances() { z = 0; }
+ // expected-warning at -1 {{4 uninitialized fields}}
+ // expected-note at -2 {{4 uninitialized fields}}
+};
+
+void fTwoInstances() {
+ TwoInstances t; // expected-note {{Calling default constructor for 'TwoInstances'}}
+}
+
+// 'first' and 'second' have the same type, so all four notes point at the two members of Inner.
+// Ordering by source location alone does not separate them.
+// Because of this, we sort the notes by the message as well as a tie breaker.
+
+// CHECK-LABEL: warning: 4 uninitialized fields at the end of the constructor call
+// CHECK-NEXT: note: uninitialized field 'this->first.x'
+// CHECK-NEXT: note: uninitialized field 'this->second.x'
+// CHECK-NEXT: note: uninitialized field 'this->first.y'
+// CHECK-NEXT: note: uninitialized field 'this->second.y'
+
More information about the cfe-commits
mailing list