Skip to content

Conversation

@guillem-bartrina-sonarsource
Copy link
Contributor

The first thing SValBuilder::evalBinOpNN does is simplify both operators (here), why wouldn't we simplify the RHS in SValBuilder::evalBinOpLN?

When the LHS is an Element, the right side is simplified incidentally when calling evalBinOpNN (here).

Without this patch, the “base_complex” test case is UNKNOWN instead of TRUE.

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:static analyzer labels Oct 1, 2025
@guillem-bartrina-sonarsource guillem-bartrina-sonarsource changed the title [analyzer] SValBuilder::evalBinOpLN: try simplifying the RHS first [analyzer] SValBuilder::evalBinOpLN: try simplifying the RHS first Oct 1, 2025
@llvmbot
Copy link
Member

llvmbot commented Oct 1, 2025

@llvm/pr-subscribers-clang

@llvm/pr-subscribers-clang-static-analyzer-1

Author: None (guillem-bartrina-sonarsource)

Changes

The first thing SValBuilder::evalBinOpNN does is simplify both operators (here), why wouldn't we simplify the RHS in SValBuilder::evalBinOpLN?

When the LHS is an Element, the right side is simplified incidentally when calling evalBinOpNN (here).

Without this patch, the “base_complex” test case is UNKNOWN instead of TRUE.


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

2 Files Affected:

  • (modified) clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp (+6)
  • (added) clang/test/Analysis/loc-folding.cpp (+60)
diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index 84a9c43d3572e..975172a2b1f79 100644
--- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -1111,6 +1111,12 @@ SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
   assert(!BinaryOperator::isComparisonOp(op) &&
          "arguments to comparison ops must be of the same type");
 
+  // Constraints may have changed since the creation of a bound SVal. Check if
+  // the value can be simplified based on those new constraints.
+  SVal simplifiedRhs = simplifySVal(state, rhs);
+  if (auto simplifiedRhsAsNonLoc = simplifiedRhs.getAs<NonLoc>())
+    rhs = *simplifiedRhsAsNonLoc;
+
   // Special case: rhs is a zero constant.
   if (rhs.isZeroConstant())
     return lhs;
diff --git a/clang/test/Analysis/loc-folding.cpp b/clang/test/Analysis/loc-folding.cpp
new file mode 100644
index 0000000000000..ec70143b972ef
--- /dev/null
+++ b/clang/test/Analysis/loc-folding.cpp
@@ -0,0 +1,60 @@
+// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify -Wno-tautological-compare -analyzer-config eagerly-assume=false %s
+
+void clang_analyzer_eval(bool);
+
+void element_constant() {
+  char arr[10];
+  clang_analyzer_eval(arr + 1 > arr); // expected-warning{{TRUE}}
+}
+
+void element_known() {
+  char arr[10];
+  int off = 1;
+  clang_analyzer_eval(arr + off > arr); // expected-warning{{TRUE}}
+}
+
+void element_constrained(int off) {
+  char arr[10];
+  if (off == 1) {
+    clang_analyzer_eval(arr + off > arr); // expected-warning{{TRUE}}
+  }
+}
+
+void element_unknown(int off) {
+  char arr[10];
+  clang_analyzer_eval(arr + off > arr); // expected-warning{{UNKNOWN}}
+}
+
+void element_complex(int off) {
+  char arr[10];
+  int comp = off * 2;
+  if (off == 1) {
+    clang_analyzer_eval(arr + comp); // expected-warning{{TRUE}}
+  }
+}
+
+void base_constant(int *arr) {
+  clang_analyzer_eval(arr + 1 > arr); // expected-warning{{TRUE}}
+}
+
+void base_known(int *arr) {
+  int off = 1;
+  clang_analyzer_eval(arr + off > arr); // expected-warning{{TRUE}}
+}
+
+void base_constrained(int *arr, int off) {
+  if (off == 1) {
+    clang_analyzer_eval(arr + off > arr); // expected-warning{{TRUE}}
+  }
+}
+
+void base_unknown(int *arr, int off) {
+  clang_analyzer_eval(arr + off > arr); // expected-warning{{UNKNOWN}}
+}
+
+void base_complex(int *arr, int off) {
+  int comp = off * 2;
+  if (off == 1) {
+    clang_analyzer_eval(arr + comp > arr); // expected-warning{{TRUE}}
+  }
+}

Copy link
Contributor

@steakhal steakhal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. I wish we could obey the rules I outlined before. But that looks out of reach for now.

@guillem-bartrina-sonarsource
Copy link
Contributor Author

Thank you. Feel free to merge it, since I don't have the rights to do so.

@steakhal steakhal merged commit d49aa40 into llvm:main Oct 14, 2025
9 checks passed
akadutta pushed a commit to akadutta/llvm-project that referenced this pull request Oct 14, 2025
…lvm#161537)

The first thing `SValBuilder::evalBinOpNN` does is simplify both
operators
([here](https://github.com/llvm/llvm-project/blob/main/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp#L430-L437)),
why wouldn't we simplify the RHS in `SValBuilder::evalBinOpLN`?

When the LHS is an `Element`, the right side is simplified incidentally
when calling `evalBinOpNN`
([here](https://github.com/llvm/llvm-project/blob/main/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp#L1169-L1170)).

Without this patch, the “base_complex” test case is **UNKNOWN** instead of **TRUE**.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:static analyzer clang Clang issues not falling into any other category

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants