[clang] [analyzer][NFC] Reorganize bstring.cpp tests (PR #188709)

via cfe-commits cfe-commits at lists.llvm.org
Thu Mar 26 03:00:38 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-clang

Author: Endre Fülöp (gamesh411)

<details>
<summary>Changes</summary>

This change eliminates preprocessor-based suppression of test cases, and moves special cases to separate test files.

---
Full diff: https://github.com/llvm/llvm-project/pull/188709.diff


3 Files Affected:

- (added) clang/test/Analysis/bstring-oob-suppressed.cpp (+92) 
- (added) clang/test/Analysis/bstring-uninit-only.cpp (+23) 
- (modified) clang/test/Analysis/bstring.cpp (+5-95) 


``````````diff
diff --git a/clang/test/Analysis/bstring-oob-suppressed.cpp b/clang/test/Analysis/bstring-oob-suppressed.cpp
new file mode 100644
index 0000000000000..8eb9ad7a08dff
--- /dev/null
+++ b/clang/test/Analysis/bstring-oob-suppressed.cpp
@@ -0,0 +1,92 @@
+// RUN: %clang_analyze_cc1 \
+// RUN:     -analyzer-checker=core \
+// RUN:     -analyzer-checker=unix.cstring \
+// RUN:     -analyzer-checker=unix.Malloc \
+// RUN:     -analyzer-checker=debug.ExprInspection \
+// RUN:     -analyzer-config eagerly-assume=false \
+// RUN:     -analyzer-checker=alpha.unix.cstring.BufferOverlap \
+// RUN:     -analyzer-checker=unix.cstring.NotNullTerminated \
+// RUN:     -verify %s
+
+// These tests exercise memset calls that go out-of-bounds. They are separated
+// from bstring.cpp because they require OutOfBounds to be disabled: with
+// OutOfBounds enabled, the analysis sinks at the OOB memset and the subsequent
+// clang_analyzer_eval calls are not reached.
+//
+// FIXME: These tests document the analyzer's current behavior when OutOfBounds
+// is disabled and OOB memset calls don't terminate the path. Once the
+// OutOfBounds checker is stable enough to always terminate execution at OOB
+// errors (regardless of whether the checker frontend is enabled), these tests
+// should be revisited: the clang_analyzer_eval calls after the OOB memset
+// would become unreachable.
+
+#include "Inputs/system-header-simulator-cxx.h"
+
+void clang_analyzer_eval(int);
+void *memset(void *dest, int ch, std::size_t count);
+
+namespace memset_non_pod {
+class Base {
+public:
+  int b_mem;
+  Base() : b_mem(1) {}
+};
+
+class Derived : public Base {
+public:
+  int d_mem;
+  Derived() : d_mem(2) {}
+};
+
+void memset2_inheritance_field() {
+  Derived d;
+  // FIXME: OOB memset on a derived field with sizeof(Derived).
+  // Current behavior: the not-set part is treated as UNKNOWN.
+  memset(&d.d_mem, 0, sizeof(Derived));
+  clang_analyzer_eval(d.b_mem == 0); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(d.d_mem == 0); // expected-warning{{UNKNOWN}}
+}
+
+void memset3_inheritance_field() {
+  Derived d;
+  // FIXME: memset on the base field with sizeof(Derived). This doesn't
+  // actually write past the object's extent, but it's UB because the memset
+  // accesses the object through a pointer to a member, violating aliasing
+  // rules. Current behavior: the field is treated as correctly set to 0.
+  memset(&d.b_mem, 0, sizeof(Derived));
+  clang_analyzer_eval(d.b_mem == 0); // expected-warning{{TRUE}}
+  clang_analyzer_eval(d.d_mem == 0); // expected-warning{{TRUE}}
+}
+
+class BaseVirtual {
+public:
+  int b_mem;
+  virtual int get() { return 1; }
+};
+
+class DerivedVirtual : public BaseVirtual {
+public:
+  int d_mem;
+};
+
+void memset8_virtual_inheritance_field() {
+  DerivedVirtual d;
+  // FIXME: Same as memset3, but the base has a virtual function. In typical
+  // implementations &d.b_mem differs from &d because the vtable pointer
+  // precedes the first member, so this may also write past the object's
+  // extent.
+  memset(&d.b_mem, 0, sizeof(Derived));
+  clang_analyzer_eval(d.b_mem == 0); // expected-warning{{UNKNOWN}}
+  clang_analyzer_eval(d.d_mem == 0); // expected-warning{{UNKNOWN}}
+}
+} // namespace memset_non_pod
+
+void memset1_new_array() {
+  int *array = new int[10];
+  memset(array, 0, 10 * sizeof(int));
+  clang_analyzer_eval(array[2] == 0); // expected-warning{{TRUE}}
+  // FIXME: OOB memset on a heap array. The analysis continues past it.
+  memset(array + 1, 'a', 10 * sizeof(9));
+  clang_analyzer_eval(array[2] == 0); // expected-warning{{UNKNOWN}}
+  delete[] array;
+}
diff --git a/clang/test/Analysis/bstring-uninit-only.cpp b/clang/test/Analysis/bstring-uninit-only.cpp
new file mode 100644
index 0000000000000..fa78ccc2f4c26
--- /dev/null
+++ b/clang/test/Analysis/bstring-uninit-only.cpp
@@ -0,0 +1,23 @@
+// RUN: %clang_analyze_cc1 \
+// RUN:     -analyzer-checker=core \
+// RUN:     -analyzer-checker=unix.cstring \
+// RUN:     -analyzer-checker=unix.Malloc \
+// RUN:     -analyzer-checker=debug.ExprInspection \
+// RUN:     -analyzer-config eagerly-assume=false \
+// RUN:     -analyzer-checker=alpha.unix.cstring.UninitializedRead \
+// RUN:     -verify %s
+
+// This test verifies that UninitializedRead produces warnings even when
+// OutOfBounds is disabled. Previously, CheckBufferAccess would early-return
+// before reaching checkInit() when OutOfBounds was disabled, suppressing
+// UninitializedRead as a side effect.
+
+#include "Inputs/system-header-simulator-cxx.h"
+#include "Inputs/system-header-simulator-for-malloc.h"
+
+void memmove_uninit_without_outofbound() {
+  int src[4];
+  int dst[4];
+  memmove(dst, src, sizeof(src)); // expected-warning{{The first element of the 2nd argument is undefined}}
+                                  // expected-note at -1{{Other elements might also be undefined}}
+}
diff --git a/clang/test/Analysis/bstring.cpp b/clang/test/Analysis/bstring.cpp
index 732b36a92ac5a..dabd53d48cb4b 100644
--- a/clang/test/Analysis/bstring.cpp
+++ b/clang/test/Analysis/bstring.cpp
@@ -3,28 +3,14 @@
 // DEFINE:     -analyzer-checker=unix.cstring \
 // DEFINE:     -analyzer-checker=unix.Malloc \
 // DEFINE:     -analyzer-checker=debug.ExprInspection \
+// DEFINE:     -analyzer-checker=alpha.unix.cstring \
 // DEFINE:     -analyzer-config eagerly-assume=false \
 // DEFINE:     -verify %s
 
-// RUN: %{analyzer} \
-// RUN:     -analyzer-checker=alpha.unix.cstring
-
-// RUN: %{analyzer} -DUSE_BUILTINS \
-// RUN:     -analyzer-checker=alpha.unix.cstring
-
-// RUN: %{analyzer} -DVARIANT \
-// RUN:     -analyzer-checker=alpha.unix.cstring
-
-// RUN: %{analyzer} -DUSE_BUILTINS -DVARIANT \
-// RUN:     -analyzer-checker=alpha.unix.cstring
-
-// RUN: %{analyzer} -DSUPPRESS_OUT_OF_BOUND \
-// RUN:     -analyzer-checker=alpha.unix.cstring.BufferOverlap \
-// RUN:     -analyzer-checker=unix.cstring.NotNullTerminated
-
-// RUN: %{analyzer} \
-// RUN:   -DUNINIT_WITHOUT_OUTOFBOUND \
-// RUN:   -analyzer-checker=alpha.unix.cstring.UninitializedRead
+// RUN: %{analyzer}
+// RUN: %{analyzer} -DUSE_BUILTINS
+// RUN: %{analyzer} -DVARIANT
+// RUN: %{analyzer} -DUSE_BUILTINS -DVARIANT
 
 #include "Inputs/system-header-simulator-cxx.h"
 #include "Inputs/system-header-simulator-for-malloc.h"
@@ -122,33 +108,6 @@ void memset1_inheritance() {
   clang_analyzer_eval(d.d_mem == 0); // expected-warning{{TRUE}}
 }
 
-#ifdef SUPPRESS_OUT_OF_BOUND
-void memset2_inheritance_field() {
-  Derived d;
-  // FIXME: This example wrongly calls `memset` on the derived field, with the
-  // size parameter that has the size of the whole derived class. The analysis
-  // should stop at that point as this is UB.
-  // This test asserts the current behavior of treating the not set part as
-  // UNKNOWN.
-  memset(&d.d_mem, 0, sizeof(Derived));
-  clang_analyzer_eval(d.b_mem == 0); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(d.d_mem == 0); // expected-warning{{UNKNOWN}}
-}
-
-void memset3_inheritance_field() {
-  Derived d;
-  // FIXME: Here we are setting the field of the base with the size of the
-  // Derived class. By the letter of the standard this is UB, but practically
-  // this only touches memory it is supposed to with the above class
-  // definitions. If we were to be strict the analysis should stop here.
-  // This test asserts the current behavior of nevertheless treating the
-  // wrongly set field as correctly set to 0.
-  memset(&d.b_mem, 0, sizeof(Derived));
-  clang_analyzer_eval(d.b_mem == 0); // expected-warning{{TRUE}}
-  clang_analyzer_eval(d.d_mem == 0); // expected-warning{{TRUE}}
-}
-#endif
-
 void memset4_array_nonpod_object() {
   Derived array[10];
   clang_analyzer_eval(array[1].b_mem == 1); // expected-warning{{UNKNOWN}}
@@ -195,53 +154,4 @@ void memset7_placement_new() {
   clang_analyzer_eval(d->d_mem == 0); // expected-warning{{TRUE}}
 }
 
-class BaseVirtual {
-public:
-  int b_mem;
-  virtual int get() { return 1; }
-};
-
-class DerivedVirtual : public BaseVirtual {
-public:
-  int d_mem;
-};
-
-#ifdef SUPPRESS_OUT_OF_BOUND
-void memset8_virtual_inheritance_field() {
-  DerivedVirtual d;
-  // FIXME: This example wrongly calls `memset` on the derived field, with the
-  // size parameter that has the size of the whole derived class. The analysis
-  // should stop at that point as this is UB. The situation is further
-  // complicated by the fact the base base a virtual function.
-  // This test asserts the current behavior of treating the not set part as
-  // UNKNOWN.
-  memset(&d.b_mem, 0, sizeof(Derived));
-  clang_analyzer_eval(d.b_mem == 0); // expected-warning{{UNKNOWN}}
-  clang_analyzer_eval(d.d_mem == 0); // expected-warning{{UNKNOWN}}
-}
-#endif
 } // namespace memset_non_pod
-
-#ifdef SUPPRESS_OUT_OF_BOUND
-void memset1_new_array() {
-  int *array = new int[10];
-  memset(array, 0, 10 * sizeof(int));
-  clang_analyzer_eval(array[2] == 0); // expected-warning{{TRUE}}
-  // FIXME: The analyzer should stop analysis after memset. Maybe the intent of
-  // this test was to test for this as a desired behaviour, but it shouldn't be.
-  // Going out-of-bounds with memset is a fatal error, even if we decide not to
-  // report it.
-  memset(array + 1, 'a', 10 * sizeof(9));
-  clang_analyzer_eval(array[2] == 0); // expected-warning{{UNKNOWN}}
-  delete[] array;
-}
-#endif
-
-#ifdef UNINIT_WITHOUT_OUTOFBOUND
-void memmove_uninit_without_outofbound() {
-  int src[4];
-  int dst[4];
-  memmove(dst, src, sizeof(src)); // expected-warning{{The first element of the 2nd argument is undefined}}
-                                  // expected-note at -1{{Other elements might also be undefined}}
-}
-#endif

``````````

</details>


https://github.com/llvm/llvm-project/pull/188709


More information about the cfe-commits mailing list