Index: include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -789,12 +789,6 @@ return UnknownVal(); } -inline SVal ProgramState::getSVal(const Stmt *Ex, - const LocationContext *LCtx) const{ - return Env.getSVal(EnvironmentEntry(Ex, LCtx), - *getStateManager().svalBuilder); -} - inline SVal ProgramState::getSValAsScalarOrLoc(const Stmt *S, const LocationContext *LCtx) const { Index: lib/StaticAnalyzer/Core/BasicValueFactory.cpp =================================================================== --- lib/StaticAnalyzer/Core/BasicValueFactory.cpp +++ lib/StaticAnalyzer/Core/BasicValueFactory.cpp @@ -281,7 +281,11 @@ case BO_NE: return &getTruthValue( V1 != V2 ); - // Note: LAnd, LOr, Comma are handled specially by higher-level logic. + case BO_LAnd: + return &getTruthValue(V1.getBoolValue() && V2.getBoolValue()); + + case BO_LOr: + return &getTruthValue(V1.getBoolValue() || V2.getBoolValue()); case BO_And: return &getValue( V1 & V2 ); @@ -291,6 +295,8 @@ case BO_Xor: return &getValue( V1 ^ V2 ); + + // Note: Comma is handled specially by higher-level logic. } } Index: lib/StaticAnalyzer/Core/BugReporterVisitors.cpp =================================================================== --- lib/StaticAnalyzer/Core/BugReporterVisitors.cpp +++ lib/StaticAnalyzer/Core/BugReporterVisitors.cpp @@ -1788,12 +1788,6 @@ ConditionBRVisitor::VisitNodeImpl(const ExplodedNode *N, BugReporterContext &BRC, BugReport &BR) { ProgramPoint progPoint = N->getLocation(); - ProgramStateRef CurrentState = N->getState(); - ProgramStateRef PreviousState = N->getFirstPred()->getState(); - - // If the constraint information does not changed there is no assumption. - if (BRC.getStateManager().haveEqualConstraints(CurrentState, PreviousState)) - return nullptr; // If an assumption was made on a branch, it should be caught // here by looking at the state transition. @@ -1881,6 +1875,18 @@ ConditionBRVisitor::VisitTrueTest(const Expr *Cond, bool tookTrue, BugReporterContext &BRC, BugReport &R, const ExplodedNode *N) { + ProgramStateRef CurrentState = N->getState(); + ProgramStateRef PreviousState = N->getFirstPred()->getState(); + const LocationContext *LCtx = N->getLocationContext(); + + // If the constraint information is changed between the current and the + // previous program state we assuming the newly seen constraint information. + // If we cannot evaluate the condition (and the constraints are the same) + // the analyzer has no information about the value and just assuming it. + if (BRC.getStateManager().haveEqualConstraints(CurrentState, PreviousState) && + CurrentState->getSVal(Cond, LCtx).isValid()) + return nullptr; + // These will be modified in code below, but we need to preserve the original // values in case we want to throw the generic message. const Expr *CondTmp = Cond; @@ -1916,7 +1922,6 @@ // Condition too complex to explain? Just say something so that the user // knew we've made some path decision at this point. - const LocationContext *LCtx = N->getLocationContext(); PathDiagnosticLocation Loc(Cond, BRC.getSourceManager(), LCtx); if (!Loc.isValid() || !Loc.asLocation().isValid()) return nullptr; Index: lib/StaticAnalyzer/Core/ProgramState.cpp =================================================================== --- lib/StaticAnalyzer/Core/ProgramState.cpp +++ lib/StaticAnalyzer/Core/ProgramState.cpp @@ -258,6 +258,37 @@ return UnknownVal(); } +SVal ProgramState::getSVal(const Stmt *S, const LocationContext *LCtx) const { + auto &SVB = *getStateManager().svalBuilder; + + // Handle short-circuit operators. + if (const auto *E = dyn_cast(S)) { + if (const auto *BO = dyn_cast(E->IgnoreParenCasts())) { + BinaryOperator::Opcode Op = BO->getOpcode(); + if (Op == BO_LAnd || Op == BO_LOr) { + const Expr *LhsExpr = BO->getLHS()->IgnoreParenCasts(); + const Expr *RhsExpr = BO->getRHS()->IgnoreParenCasts(); + SVal Lhs = getSVal(LhsExpr, LCtx); + SVal Rhs = getSVal(RhsExpr, LCtx); + + if (Lhs.isValid() && Rhs.isUnknownOrUndef()) + if (auto LhsCI = Lhs.getAs()) + return SVB.makeTruthVal(LhsCI.getValue().getValue().getBoolValue()); + + if (Rhs.isValid() && Lhs.isUnknownOrUndef()) + if (auto RhsCI = Rhs.getAs()) + return SVB.makeTruthVal(RhsCI.getValue().getValue().getBoolValue()); + + SVal V = SVB.evalBinOp(this, Op, Lhs, Rhs, BO->getType()); + if (V.isValid()) + return V; + } + } + } + + return Env.getSVal(EnvironmentEntry(S, LCtx), SVB); +} + SVal ProgramState::getSVal(Loc location, QualType T) const { SVal V = getRawSVal(location, T); Index: test/Analysis/diagnostics/macros.cpp =================================================================== --- test/Analysis/diagnostics/macros.cpp +++ test/Analysis/diagnostics/macros.cpp @@ -30,7 +30,8 @@ // There are no path notes on the comparison to float types. void testDoubleMacro(double d) { - if (d == DBL_MAX) { // expected-note {{Taking true branch}} + if (d == DBL_MAX) { // expected-note {{Assuming 'd' is equal to DBL_MAX}} + // expected-note@-1 {{Taking true branch}} char *p = NULL; // expected-note {{'p' initialized to a null pointer value}} *p = 7; // expected-warning {{Dereference of null pointer (loaded from variable 'p')}} Index: test/Analysis/uninit-vals.m =================================================================== --- test/Analysis/uninit-vals.m +++ test/Analysis/uninit-vals.m @@ -164,7 +164,8 @@ // expected-note@-1{{TRUE}} testObj->origin = makePoint(0.0, 0.0); - if (testObj->size > 0) { ; } // expected-note{{Taking false branch}} + if (testObj->size > 0) { ; } // expected-note{{Assuming the condition is false}} + // expected-note@-1{{Taking false branch}} // FIXME: Assigning to 'testObj->origin' kills the default binding for the // whole region, meaning that we've forgotten that testObj->size should also @@ -218,10 +219,14 @@ // expected-note@-1{{TRUE}} testObj->origin = makeIntPoint(1, 2); - if (testObj->size > 0) { ; } // expected-note{{Taking false branch}} + if (testObj->size > 0) { ; } // expected-note{{Assuming the condition is false}} // expected-note@-1{{Taking false branch}} - // expected-note@-2{{Taking false branch}} + // expected-note@-2{{Assuming the condition is false}} // expected-note@-3{{Taking false branch}} + // expected-note@-4{{Assuming the condition is false}} + // expected-note@-5{{Taking false branch}} + // expected-note@-6{{Assuming the condition is false}} + // expected-note@-7{{Taking false branch}} // FIXME: Assigning to 'testObj->origin' kills the default binding for the // whole region, meaning that we've forgotten that testObj->size should also