[clang] 72e7990 - [Liveness][analyzer] Fix liveness of binary logical operators and their subexpressions (#231419)

via cfe-commits cfe-commits at lists.llvm.org
Fri Oct 16 05:10:18 PDT 2026


Author: Gábor Tóthvári
Date: 2026-10-16T14:10:11+02:00
New Revision: 72e7990f50005ddbc032ca22d1b77e0f9b1f10ae

URL: https://github.com/llvm/llvm-project/commit/72e7990f50005ddbc032ca22d1b77e0f9b1f10ae
DIFF: https://github.com/llvm/llvm-project/commit/72e7990f50005ddbc032ca22d1b77e0f9b1f10ae.diff

LOG: [Liveness][analyzer] Fix liveness of binary logical operators and their subexpressions (#231419)

Issue #227349 reported a clang static analyzer false negative, where a
division by zero error is not found in the following code:

```
int f(bool b) {
  bool condition = (b && b || b && b);
  int y = condition ? 0 : 1;
  return 1 / y;
}
```

It turned out, that the expressions `b` were prematurely considered dead
and, as a result, their associated values were getting purged from the
environment. This caused the path where the denominator `y` is zero to
be lost.

This patch makes sure that the children of binary logical operators have
their liveness properly handled.

Added: 
    

Modified: 
    clang/lib/Analysis/LiveVariables.cpp
    clang/test/Analysis/div-zero.cpp
    clang/test/Analysis/live-stmts.cpp

Removed: 
    


################################################################################
diff  --git a/clang/lib/Analysis/LiveVariables.cpp b/clang/lib/Analysis/LiveVariables.cpp
index c9e547ac85380..666d33ebecdea 100644
--- a/clang/lib/Analysis/LiveVariables.cpp
+++ b/clang/lib/Analysis/LiveVariables.cpp
@@ -339,6 +339,15 @@ void TransferFunctions::Visit(Stmt *S) {
       AddLiveExpr(val.liveExprs, LV.ESetFact, CO->getFalseExpr());
       return;
     }
+    case Stmt::BinaryOperatorClass: {
+      auto const *BO = cast<BinaryOperator>(S);
+      if (BO->isLogicalOp()) {
+        AddAllConditionalTerms(val.liveExprs, LV.ESetFact, BO->getLHS());
+        AddAllConditionalTerms(val.liveExprs, LV.ESetFact, BO->getRHS());
+        return;
+      }
+      break;
+    }
   }
 
   // Mark all child expressions live -- "normal" case.

diff  --git a/clang/test/Analysis/div-zero.cpp b/clang/test/Analysis/div-zero.cpp
index 51ea25e828a18..c756f24dec86b 100644
--- a/clang/test/Analysis/div-zero.cpp
+++ b/clang/test/Analysis/div-zero.cpp
@@ -12,6 +12,44 @@ int fooPR10616 (int qX ) {
 
 }
 
+namespace issue227349 {
+  int f(bool b) {
+    bool condition = (b && b || b && b);
+    int y = condition ? 0 : 1;
+    return 1 / y; // expected-warning {{Division by zero}}
+  }
+
+  int f2(bool b1, bool b2, bool b3, bool b4) {
+    bool condition = (b1 && b2 || b3 && b4);
+    int y = condition ? 0 : 1;
+    return 1 / y; // expected-warning {{Division by zero}}
+  }
+
+  int f3(bool b) {
+    bool condition = (b && b || b && b) && (b && b || b && b);
+    int y = condition ? 0 : 1;
+    return 1 / y; // expected-warning {{Division by zero}}
+  }
+
+  int f4(bool b) {
+    bool condition = (b && b && b && b);
+    int y = condition ? 0 : 1;
+    return 1 / y; // expected-warning {{Division by zero}}
+  }
+
+  int f5(bool b) {
+    bool condition = (b || b || b || b);
+    int y = condition ? 0 : 1;
+    return 1 / y; // expected-warning {{Division by zero}}
+  }
+
+  int f6(bool b1, bool b2, bool b3, bool b4) {
+    bool condition = (b1 || b2 && b3 && b4);
+    int y = condition ? 0 : 1;
+    return 1 / y; // expected-warning {{Division by zero}}
+  }
+}
+
 namespace GH148875 {
 struct A {
   int x;

diff  --git a/clang/test/Analysis/live-stmts.cpp b/clang/test/Analysis/live-stmts.cpp
index ca2ff6da8b133..136d7b2c23c50 100644
--- a/clang/test/Analysis/live-stmts.cpp
+++ b/clang/test/Analysis/live-stmts.cpp
@@ -304,3 +304,190 @@ int logicalOpInTernary(bool b) {
 // CHECK: IntegerLiteral {{.*}} 'int' 0
 // CHECK-EMPTY:
 // CHECK: IntegerLiteral {{.*}} 'int' 1
+
+int shortCircuitingBinaryOperator(bool b) {
+  bool condition = (b && b || b && b);
+  int y = condition ? 0 : 1;
+  return 1 / y;
+}
+
+// CHECK: [ B0 (live expressions at block exit) ]
+// CHECK-EMPTY:
+// CHECK-EMPTY:
+// CHECK-NEXT: [ B1 (live expressions at block exit) ]
+// CHECK-EMPTY:
+// CHECK-EMPTY:
+// CHECK-NEXT: [ B2 (live expressions at block exit) ]
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 0
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue Var {{.*}} 'condition' '_Bool'
+// CHECK-EMPTY:
+// CHECK-EMPTY:
+// CHECK-NEXT: [ B3 (live expressions at block exit) ]
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 0
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue Var {{.*}} 'condition' '_Bool'
+// CHECK-EMPTY:
+// CHECK-EMPTY:
+// CHECK-NEXT: [ B4 (live expressions at block exit) ]
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 0
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue Var {{.*}} 'condition' '_Bool'
+// CHECK-EMPTY:
+// CHECK-EMPTY:
+// CHECK-NEXT: [ B5 (live expressions at block exit) ]
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: BinaryOperator {{.*}} '_Bool' '&&'
+// CHECK-NEXT: |-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: | `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT:   `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: BinaryOperator {{.*}} '_Bool' '&&'
+// CHECK-NEXT: |-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: | `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT:   `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 0
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1
+// CHECK-EMPTY:
+// CHECK-EMPTY:
+// CHECK-NEXT: [ B6 (live expressions at block exit) ]
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: BinaryOperator {{.*}} '_Bool' '&&'
+// CHECK-NEXT: |-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: | `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT:   `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: BinaryOperator {{.*}} '_Bool' '&&'
+// CHECK-NEXT: |-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: | `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT:   `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 0
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1
+// CHECK-EMPTY:
+// CHECK-EMPTY:
+// CHECK-NEXT: [ B7 (live expressions at block exit) ]
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: BinaryOperator {{.*}} '_Bool' '&&'
+// CHECK-NEXT: |-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: | `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT:   `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: BinaryOperator {{.*}} '_Bool' '&&'
+// CHECK-NEXT: |-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: | `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT:   `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 0
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1
+// CHECK-EMPTY:
+// CHECK-EMPTY:
+// CHECK-NEXT: [ B8 (live expressions at block exit) ]
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: BinaryOperator {{.*}} '_Bool' '&&'
+// CHECK-NEXT: |-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: | `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT:   `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: BinaryOperator {{.*}} '_Bool' '&&'
+// CHECK-NEXT: |-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: | `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT:   `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 0
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1
+// CHECK-EMPTY:
+// CHECK-EMPTY:
+// CHECK-NEXT: [ B9 (live expressions at block exit) ]
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: BinaryOperator {{.*}} '_Bool' '&&'
+// CHECK-NEXT: |-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT: | `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-NEXT: `-ImplicitCastExpr {{.*}} '_Bool' <LValueToRValue>
+// CHECK-NEXT:   `-DeclRefExpr {{.*}} '_Bool' lvalue ParmVar {{.*}} 'b' '_Bool'
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 0
+// CHECK-EMPTY:
+// CHECK-NEXT: IntegerLiteral {{.*}} 'int' 1


        


More information about the cfe-commits mailing list