Index: lib/StaticAnalyzer/Core/ExprEngineC.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -627,6 +627,21 @@ void ExprEngine::VisitLogicalExpr(const BinaryOperator* B, ExplodedNode *Pred, ExplodedNodeSet &Dst) { + // This method acts upon CFG elements for logical operators && and || + // and attaches the value (true or false) to them as expressions. + // It doesn't produce any state splits. + // If we made it that far, we're past the point when we modeled the short + // circuit. It means that we should have precise knowledge about whether + // we've short-circuited. If we did, we already know the value we need to + // bind. If we didn't, the value of the RHS (casted to the boolean type) + // is the answer. + // Currently this method tries to figure out whether we've short-circuited + // by looking at the ExplodedGraph. This method is imperfect because there + // could inevitably have been merges that would have resulted in multiple + // potential path traversal histories. We bail out when we fail. + // Due to this ambiguity, a more reliable solution would have been to + // track the short circuit operation history path-sensitively until + // we evaluate the respective logical operator. assert(B->getOpcode() == BO_LAnd || B->getOpcode() == BO_LOr); @@ -648,10 +663,20 @@ ProgramPoint P = N->getLocation(); assert(P.getAs()|| P.getAs()); (void) P; - assert(N->pred_size() == 1); + if (N->pred_size() != 1) { + // We failed to track back where we came from. + Bldr.generateNode(B, Pred, state); + return; + } N = *N->pred_begin(); } - assert(N->pred_size() == 1); + + if (N->pred_size() != 1) { + // We failed to track back where we came from. + Bldr.generateNode(B, Pred, state); + return; + } + N = *N->pred_begin(); BlockEdge BE = N->getLocation().castAs(); SVal X; Index: test/Analysis/logical-ops.c =================================================================== --- test/Analysis/logical-ops.c +++ test/Analysis/logical-ops.c @@ -1,4 +1,5 @@ -// RUN: %clang_analyze_cc1 -Wno-pointer-bool-conversion -analyzer-checker=core,debug.ExprInspection -verify -analyzer-config eagerly-assume=false %s +// RUN: %clang_analyze_cc1 -w -analyzer-checker=core,debug.ExprInspection\ +// RUN: -analyzer-config eagerly-assume=false -verify %s void clang_analyzer_eval(int); @@ -33,7 +34,21 @@ return x >= start && x < end; } -int undef(void) {} // expected-warning{{control reaches end of non-void function}} +int undef(void) {} void useUndef(void) { 0 || undef(); } void testPointer(void) { (void) (1 && testPointer && 0); } + +char *global_ap, *global_bp, *global_cp; +void ambiguous_backtrack_1() { + for (;;) { + (global_bp - global_ap ? global_cp[global_bp - global_ap] : 0) || 1; + global_bp++; + } +} + +int global_a, global_b; +void ambiguous_backtrack_2(int x) { + global_a = x >= 2 ? 1 : x; + global_b == x && 9 || 2; +}