Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/ConstraintManager.h @@ -153,6 +153,19 @@ return nullptr; } + /// Return pair of booleans for Within and Outside statuses. + /// First bool is `true` when SymbolRef range is within the range. + /// Second bool is `true` when SymbolRef range is outside the range. + /// Return 'false, false' otherwise. + /// + /// This function acts as `assumeDual` function + /// but it does not create any new states. + virtual std::pair + isSymValWithinOrOutsideRange(ProgramStateRef state, SymbolRef sym, + llvm::APSInt lower, llvm::APSInt upper) { + return {false, false}; + } + /// Scan all symbols referenced by the constraints. If the symbol is not /// alive, remove it. virtual ProgramStateRef removeDeadBindings(ProgramStateRef state, Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -1278,6 +1278,9 @@ const llvm::APSInt *getSymVal(ProgramStateRef State, SymbolRef Sym) const override; + std::pair + isSymValWithinOrOutsideRange(ProgramStateRef State, SymbolRef Sym, + llvm::APSInt lower, llvm::APSInt upper) override; ProgramStateRef removeDeadBindings(ProgramStateRef State, SymbolReaper &SymReaper) override; @@ -1875,6 +1878,15 @@ return T ? T->getConcreteValue() : nullptr; } +std::pair RangeConstraintManager::isSymValWithinOrOutsideRange( + ProgramStateRef St, SymbolRef Sym, llvm::APSInt lower, llvm::APSInt upper) { + if (const RangeSet *T = getConstraint(St, Sym)) { + RangeSet RS = T->Intersect(getBasicVals(), F, lower, upper); + return {RS == *T, RS.isEmpty()}; + } + return {false, false}; +} + //===----------------------------------------------------------------------===// // Remove dead symbols from existing constraints //===----------------------------------------------------------------------===// Index: clang/lib/StaticAnalyzer/Core/SValBuilder.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -872,11 +872,18 @@ // Symbol to bool. if (!OriginalTy.isNull() && CastTy->isBooleanType()) { // Non-float to bool. - if (Loc::isLocType(OriginalTy) || - OriginalTy->isIntegralOrEnumerationType() || - OriginalTy->isMemberPointerType()) { + if (OriginalTy->isIntegralOrEnumerationType()) { BasicValueFactory &BVF = getBasicValueFactory(); - return makeNonLoc(SE, BO_NE, BVF.getValue(0, SE->getType()), CastTy); + const llvm::APSInt &Zero = BVF.getValue(0, OriginalTy); + bool IsZero, IsNotZero; + std::tie(IsZero, IsNotZero) = + getStateManager().getConstraintManager().isSymValWithinOrOutsideRange( + State, SE, Zero, Zero); + // Symbolic pointer, integer to bool. + if (IsZero || IsNotZero) + return makeTruthVal(IsNotZero, CastTy); + // Symbolic integer to bool. + return V; } } else { // Symbol to integer. Index: clang/test/Analysis/cast_symbolic_ints_to_bool.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/cast_symbolic_ints_to_bool.cpp @@ -0,0 +1,67 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=core,debug.ExprInspection -verify %s + +template +void clang_analyzer_dump(T); + +void test_int_to_bool(bool b, int x) { + clang_analyzer_dump(b); // expected-warning{{reg_$0<_Bool b>}} + b = x; + clang_analyzer_dump(b); // expected-warning{{reg_$1}} + if (x < 0) { + b = x; + clang_analyzer_dump(b); // expected-warning{{1 U1b}} + } else if (x > 0) { + b = x; + clang_analyzer_dump(b); // expected-warning{{1 U1b}} + } else { + b = x; + clang_analyzer_dump(b); // expected-warning{{0 U1b}} + } +} + +void test_char_to_bool(char x) { + bool b = x; + clang_analyzer_dump(b); // expected-warning{{reg_$0}} + if (x < 0) { + bool b = x; + clang_analyzer_dump(b); // expected-warning{{1 U1b}} + } else if (x > 0) { + bool b = x; + clang_analyzer_dump(b); // expected-warning{{1 U1b}} + } else { + bool b = x; + clang_analyzer_dump(b); // expected-warning{{0 U1b}} + } +} + +void test_unsigned_to_bool(bool b, unsigned x) { + clang_analyzer_dump(b); // expected-warning{{reg_$0<_Bool b>}} + b = x; + clang_analyzer_dump(b); // expected-warning{{reg_$1}} + if (x) { + b = x; + clang_analyzer_dump(b); // expected-warning{{1 U1b}} + } else { + b = x; + clang_analyzer_dump(b); // expected-warning{{0 U1b}} + } +} + +void test_unsigned_to_bool(unsigned char x) { + bool b = x; + clang_analyzer_dump(b); // expected-warning{{reg_$0}} + if (x < 42) { + b = x; + clang_analyzer_dump(b); // expected-warning{{reg_$0}} + if (x) { + b = x; + clang_analyzer_dump(b); // expected-warning{{1 U1b}} + } else { + b = x; + clang_analyzer_dump(b); // expected-warning{{0 U1b}} + } + } else { + b = x; + clang_analyzer_dump(b); // expected-warning{{1 U1b}} + } +}