Index: clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h =================================================================== --- clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h +++ clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h @@ -135,8 +135,9 @@ " (" + Visit(S->getRHS()) + ")"; } - // TODO: SymbolCast doesn't appear in practice. - // Add the relevant code once it does. + std::string VisitSymbolCast(const SymbolCast *S) { + return "(" + S->getType().getAsString() + ")" + Visit(S->getOperand()); + } std::string VisitSymbolicRegion(const SymbolicRegion *R) { // Explain 'this' object here. Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h =================================================================== --- clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h +++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h @@ -118,10 +118,6 @@ SVal evalCast(SVal V, QualType CastTy, QualType OriginalTy); - // Handles casts of type CK_IntegralCast. - SVal evalIntegralCast(ProgramStateRef state, SVal val, QualType castTy, - QualType originalType); - virtual SVal evalMinus(NonLoc val) = 0; virtual SVal evalComplement(NonLoc val) = 0; Index: clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/ExprInspectionChecker.cpp @@ -418,6 +418,13 @@ ProgramStateRef State = C.getState(); + // Unwrap symbolic expression to skip argument casts on function call. + // This is useful when there is no way for overloading function in C + // but we need to pass different types of arguments and + // implicit cast occures. + while (isa(Sym)) + Sym = cast(Sym)->getOperand(); + C.addTransition(C.getState()->set(Sym, E)); } Index: clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -431,7 +431,7 @@ case CK_IntegralCast: { // Delegate to SValBuilder to process. SVal V = state->getSVal(Ex, LCtx); - V = svalBuilder.evalIntegralCast(state, V, T, ExTy); + V = svalBuilder.evalCast(V, T, ExTy); state = state->BindExpr(CastE, LCtx, V); Bldr.generateNode(CastE, Pred, state); continue; Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -766,6 +766,7 @@ namespace { class EquivalenceClass; +class NominalTypeList; } // end anonymous namespace REGISTER_MAP_WITH_PROGRAMSTATE(ClassMap, SymbolRef, EquivalenceClass) @@ -826,14 +827,16 @@ LLVM_NODISCARD static inline ProgramStateRef markDisequal(BasicValueFactory &BV, RangeSet::Factory &F, - ProgramStateRef State, SymbolRef First, SymbolRef Second); + ProgramStateRef State, NominalTypeList &NTL, SymbolRef First, + SymbolRef Second); LLVM_NODISCARD static inline ProgramStateRef markDisequal(BasicValueFactory &BV, RangeSet::Factory &F, - ProgramStateRef State, EquivalenceClass First, - EquivalenceClass Second); + ProgramStateRef State, NominalTypeList &NTL, + EquivalenceClass First, EquivalenceClass Second); LLVM_NODISCARD inline ProgramStateRef markDisequal(BasicValueFactory &BV, RangeSet::Factory &F, - ProgramStateRef State, EquivalenceClass Other) const; + ProgramStateRef State, NominalTypeList &NTL, + EquivalenceClass Other) const; LLVM_NODISCARD static inline ClassSet getDisequalClasses(ProgramStateRef State, SymbolRef Sym); LLVM_NODISCARD inline ClassSet @@ -893,8 +896,8 @@ static inline bool addToDisequalityInfo(DisequalityMapTy &Info, ConstraintRangeTy &Constraints, BasicValueFactory &BV, RangeSet::Factory &F, - ProgramStateRef State, EquivalenceClass First, - EquivalenceClass Second); + ProgramStateRef State, NominalTypeList &NTL, + EquivalenceClass First, EquivalenceClass Second); /// This is a unique identifier of the class. uintptr_t ID; @@ -1081,6 +1084,35 @@ // Symbolic reasoning logic //===----------------------------------------------------------------------===// +class NominalTypeList { + CanQualType Types[4]; + +public: + using Iterator = CanQualType *; + + NominalTypeList(ASTContext &C) + : Types{C.Char8Ty, C.Char16Ty, C.Char32Ty, C.LongLongTy} {} + Iterator findByWidth(uint32_t Width) { + int index = 4; + switch (Width) { + case 8: + index = 0; + break; + case 16: + index = 1; + break; + case 32: + index = 2; + break; + case 64: + index = 3; + }; + return Types + index; + } + Iterator begin() { return std::begin(Types); } + Iterator end() { return std::end(Types); } +}; + /// A little component aggregating all of the reasoning we have about /// the ranges of symbolic expressions. /// @@ -1091,11 +1123,60 @@ public: template static RangeSet inferRange(BasicValueFactory &BV, RangeSet::Factory &F, - ProgramStateRef State, SourceType Origin) { - SymbolicRangeInferrer Inferrer(BV, F, State); + ProgramStateRef State, NominalTypeList &NTL, + SourceType Origin) { + SymbolicRangeInferrer Inferrer(BV, F, State, NTL); return Inferrer.infer(Origin); } + RangeSet VisitSymbolCast(const SymbolCast *Sym) { + // Unwrap symbol to get an underlying symbol. + // Store every next type except the inner(original) one. + SmallVector Types; + uint32_t MinBitWidth = UINT32_MAX; + SymbolRef RootSym = Sym; + ASTContext &C = ValueFactory.getContext(); + do { + // We only handle integral cast, when all the types are integrals. + // Otherwise, pass the expression to VisitSymExpr. + QualType T = RootSym->getType(); + if (!T->isIntegralOrEnumerationType()) + return VisitSymExpr(Sym); + + MinBitWidth = std::min(MinBitWidth, C.getIntWidth(T)); + Types.push_back(T); + RootSym = cast(RootSym)->getOperand(); + } while (isa(RootSym)); + // Now RootSym is the root symbol. + + QualType RootTy = RootSym->getType(); + const uint32_t RootBitWidth = C.getIntWidth(RootTy); + + // Check if we had any truncated ranges of the root symbol, + // which are more precise for reasoning about other bigger truncations. + const RangeSet *RS = nullptr; + auto It = NominalTypes.findByWidth(MinBitWidth); + auto E = NominalTypes.findByWidth(RootBitWidth); + for (; !RS && It < E; ++It) { + SymbolRef S = + State->getSymbolManager().getCastSymbol(RootSym, RootTy, *It); + RS = getConstraint(State, S); + } + // If we didn't find any truncated ranges, look for the original range. + if (!RS) + RS = getConstraint(State, RootSym); + + // If there's no existing range, create it based on type. + RangeSet OriginalRS = RS ? *RS : infer(RootTy); + + // Cast original range to the types from inner to outer one by one. + auto TypesReversedRange = llvm::make_range(Types.rbegin(), Types.rend()); + for (const QualType T : TypesReversedRange) + OriginalRS = RangeFactory.castTo(OriginalRS, T); + + return OriginalRS; + } + RangeSet VisitSymExpr(SymbolRef Sym) { // If we got to this function, the actual type of the symbolic // expression is not supported for advanced inference. @@ -1118,8 +1199,8 @@ private: SymbolicRangeInferrer(BasicValueFactory &BV, RangeSet::Factory &F, - ProgramStateRef S) - : ValueFactory(BV), RangeFactory(F), State(S) {} + ProgramStateRef S, NominalTypeList &NTL) + : ValueFactory(BV), RangeFactory(F), State(S), NominalTypes(NTL) {} /// Infer range information from the given integer constant. /// @@ -1464,6 +1545,7 @@ BasicValueFactory &ValueFactory; RangeSet::Factory &RangeFactory; ProgramStateRef State; + NominalTypeList &NominalTypes; }; //===----------------------------------------------------------------------===// @@ -1637,7 +1719,8 @@ class RangeConstraintManager : public RangedConstraintManager { public: RangeConstraintManager(ExprEngine *EE, SValBuilder &SVB) - : RangedConstraintManager(EE, SVB), F(getBasicVals()) {} + : RangedConstraintManager(EE, SVB), F(getBasicVals()), + NTL(SVB.getContext()) {} //===------------------------------------------------------------------===// // Implementation for interface from ConstraintManager. @@ -1703,6 +1786,10 @@ private: RangeSet::Factory F; + NominalTypeList NTL; + + std::tuple + handleSymbolCast(ProgramStateRef State, SymbolRef Sym, RangeSet R); RangeSet getRange(ProgramStateRef State, SymbolRef Sym); RangeSet getRange(ProgramStateRef State, EquivalenceClass Class); @@ -1767,7 +1854,8 @@ ProgramStateRef trackDisequality(ProgramStateRef State, SymbolRef LHS, SymbolRef RHS) { - return EquivalenceClass::markDisequal(getBasicVals(), F, State, LHS, RHS); + return EquivalenceClass::markDisequal(getBasicVals(), F, State, NTL, LHS, + RHS); } ProgramStateRef trackEquality(ProgramStateRef State, SymbolRef LHS, @@ -2035,25 +2123,23 @@ return isTrivial(State) && Reaper.isDead(getRepresentativeSymbol()); } -inline ProgramStateRef EquivalenceClass::markDisequal(BasicValueFactory &VF, - RangeSet::Factory &RF, - ProgramStateRef State, - SymbolRef First, - SymbolRef Second) { - return markDisequal(VF, RF, State, find(State, First), find(State, Second)); +inline ProgramStateRef +EquivalenceClass::markDisequal(BasicValueFactory &VF, RangeSet::Factory &RF, + ProgramStateRef State, NominalTypeList &NTL, + SymbolRef First, SymbolRef Second) { + return markDisequal(VF, RF, State, NTL, find(State, First), + find(State, Second)); } -inline ProgramStateRef EquivalenceClass::markDisequal(BasicValueFactory &VF, - RangeSet::Factory &RF, - ProgramStateRef State, - EquivalenceClass First, - EquivalenceClass Second) { - return First.markDisequal(VF, RF, State, Second); +inline ProgramStateRef EquivalenceClass::markDisequal( + BasicValueFactory &VF, RangeSet::Factory &RF, ProgramStateRef State, + NominalTypeList &NTL, EquivalenceClass First, EquivalenceClass Second) { + return First.markDisequal(VF, RF, State, NTL, Second); } inline ProgramStateRef EquivalenceClass::markDisequal(BasicValueFactory &VF, RangeSet::Factory &RF, - ProgramStateRef State, + ProgramStateRef State, NominalTypeList &NTL, EquivalenceClass Other) const { // If we know that two classes are equal, we can only produce an infeasible // state. @@ -2066,10 +2152,10 @@ // Disequality is a symmetric relation, so if we mark A as disequal to B, // we should also mark B as disequalt to A. - if (!addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, *this, - Other) || - !addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, Other, - *this)) + if (!addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, NTL, + *this, Other) || + !addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, NTL, + Other, *this)) return nullptr; assert(areFeasible(Constraints) && "Constraint manager shouldn't produce " @@ -2084,7 +2170,7 @@ inline bool EquivalenceClass::addToDisequalityInfo( DisequalityMapTy &Info, ConstraintRangeTy &Constraints, BasicValueFactory &VF, RangeSet::Factory &RF, ProgramStateRef State, - EquivalenceClass First, EquivalenceClass Second) { + NominalTypeList &NTL, EquivalenceClass First, EquivalenceClass Second) { // 1. Get all of the required factories. DisequalityMapTy::Factory &F = State->get_context(); @@ -2107,7 +2193,7 @@ if (const llvm::APSInt *Point = SecondConstraint->getConcreteValue()) { RangeSet FirstConstraint = SymbolicRangeInferrer::inferRange( - VF, RF, State, First.getRepresentativeSymbol()); + VF, RF, State, NTL, First.getRepresentativeSymbol()); FirstConstraint = RF.deletePoint(FirstConstraint, *Point); @@ -2423,12 +2509,13 @@ RangeSet RangeConstraintManager::getRange(ProgramStateRef State, SymbolRef Sym) { - return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, Sym); + return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, NTL, Sym); } RangeSet RangeConstraintManager::getRange(ProgramStateRef State, EquivalenceClass Class) { - return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, Class); + return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, NTL, + Class); } //===------------------------------------------------------------------------=== @@ -2443,6 +2530,67 @@ // As an example, the range [UINT_MAX-1, 3) contains five values: UINT_MAX-1, // UINT_MAX, 0, 1, and 2. +std::tuple +RangeConstraintManager::handleSymbolCast(ProgramStateRef State, SymbolRef Sym, + RangeSet R) { + QualType T = Sym->getType(); + if (!T->isIntegralOrEnumerationType() || R.isEmpty()) + return {State, Sym, R}; + + BasicValueFactory &BVF = getBasicVals(); + ASTContext &C = BVF.getContext(); + SymbolRef RootSym = Sym; + if (isa(Sym)) { + uint32_t MinBitWidth = UINT32_MAX; + do { + // We only handle integral cast, when all the types are integrals. + T = RootSym->getType(); + if (!T->isIntegralOrEnumerationType()) + return {State, Sym, R}; + MinBitWidth = std::min(MinBitWidth, C.getIntWidth(T)); + RootSym = cast(RootSym)->getOperand(); + } while (isa(RootSym)); + + QualType RootTy = RootSym->getType(); + uint32_t RootBitWidth = C.getIntWidth(RootSym->getType()); + const bool isTruncated = (MinBitWidth < RootBitWidth); + if (isTruncated) { + // Trancation occurred. High bits lost. We can't reason about ranges of + // the original(root) operand in this case. Just add a cast symbol to the + // constraint set. + // Create a new SymbolCast with a signed type and the least met size. + // E.g. (int)(uchar)x -> (char8)x + CanQualType Ty = *NTL.findByWidth(MinBitWidth); + R = F.castTo(R, Ty); + Sym = getSymbolManager().getCastSymbol(RootSym, RootTy, Ty); + } else { + // Promotion or conversion occurred. No bit lost. + // Get a range to the original(root) type and add to the constraint set. + R = F.castTo(R, RootTy); + Sym = RootSym; + } + } + + T = Sym->getType(); + for (auto It = NTL.findByWidth(C.getIntWidth(T)) - 1; It >= NTL.begin(); + --It) { + SymbolRef S = State->getSymbolManager().getCastSymbol( + RootSym, RootSym->getType(), *It); + if (const RangeSet *RS = getConstraint(State, S)) { + RangeSet TruncR = F.castTo(R, *It); + TruncR = F.intersect(*RS, TruncR); + if (TruncR.isEmpty()) { + // This seems to be an infisible branch. Return an empty set. + R = TruncR; + break; + } + State = setConstraint(State, S, TruncR); + } + } + + return {State, Sym, R}; +} + ProgramStateRef RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym, const llvm::APSInt &Int, @@ -2457,6 +2605,7 @@ RangeSet New = getRange(St, Sym); New = F.deletePoint(New, Point); + std::tie(St, Sym, New) = handleSymbolCast(St, Sym, New); return trackNE(New, St, Sym, Int, Adjustment); } @@ -2474,6 +2623,7 @@ RangeSet New = getRange(St, Sym); New = F.intersect(New, AdjInt); + std::tie(St, Sym, New) = handleSymbolCast(St, Sym, New); return trackEQ(New, St, Sym, Int, Adjustment); } @@ -2511,6 +2661,8 @@ const llvm::APSInt &Int, const llvm::APSInt &Adjustment) { RangeSet New = getSymLTRange(St, Sym, Int, Adjustment); + + std::tie(St, Sym, New) = handleSymbolCast(St, Sym, New); return trackNE(New, St, Sym, Int, Adjustment); } @@ -2548,6 +2700,8 @@ const llvm::APSInt &Int, const llvm::APSInt &Adjustment) { RangeSet New = getSymGTRange(St, Sym, Int, Adjustment); + + std::tie(St, Sym, New) = handleSymbolCast(St, Sym, New); return trackNE(New, St, Sym, Int, Adjustment); } @@ -2585,7 +2739,13 @@ const llvm::APSInt &Int, const llvm::APSInt &Adjustment) { RangeSet New = getSymGERange(St, Sym, Int, Adjustment); - return New.isEmpty() ? nullptr : setConstraint(St, Sym, New); + + if (New.isEmpty()) + return nullptr; + + std::tie(St, Sym, New) = handleSymbolCast(St, Sym, New); + + return setConstraint(St, Sym, New); } RangeSet @@ -2629,7 +2789,13 @@ const llvm::APSInt &Int, const llvm::APSInt &Adjustment) { RangeSet New = getSymLERange(St, Sym, Int, Adjustment); - return New.isEmpty() ? nullptr : setConstraint(St, Sym, New); + + if (New.isEmpty()) + return nullptr; + + std::tie(St, Sym, New) = handleSymbolCast(St, Sym, New); + + return setConstraint(St, Sym, New); } ProgramStateRef RangeConstraintManager::assumeSymWithinInclusiveRange( Index: clang/lib/StaticAnalyzer/Core/SValBuilder.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/SValBuilder.cpp +++ clang/lib/StaticAnalyzer/Core/SValBuilder.cpp @@ -500,44 +500,6 @@ return true; } -// Handles casts of type CK_IntegralCast. -// At the moment, this function will redirect to evalCast, except when the range -// of the original value is known to be greater than the max of the target type. -SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val, - QualType castTy, QualType originalTy) { - // No truncations if target type is big enough. - if (getContext().getTypeSize(castTy) >= getContext().getTypeSize(originalTy)) - return evalCast(val, castTy, originalTy); - - SymbolRef se = val.getAsSymbol(); - if (!se) // Let evalCast handle non symbolic expressions. - return evalCast(val, castTy, originalTy); - - // Find the maximum value of the target type. - APSIntType ToType(getContext().getTypeSize(castTy), - castTy->isUnsignedIntegerType()); - llvm::APSInt ToTypeMax = ToType.getMaxValue(); - NonLoc ToTypeMaxVal = - makeIntVal(ToTypeMax.isUnsigned() ? ToTypeMax.getZExtValue() - : ToTypeMax.getSExtValue(), - castTy) - .castAs(); - // Check the range of the symbol being casted against the maximum value of the - // target type. - NonLoc FromVal = val.castAs(); - QualType CmpTy = getConditionType(); - NonLoc CompVal = - evalBinOpNN(state, BO_LE, FromVal, ToTypeMaxVal, CmpTy).castAs(); - ProgramStateRef IsNotTruncated, IsTruncated; - std::tie(IsNotTruncated, IsTruncated) = state->assume(CompVal); - if (!IsNotTruncated && IsTruncated) { - // Symbol is truncated so we evaluate it as a cast. - NonLoc CastVal = makeNonLoc(se, originalTy, castTy); - return CastVal; - } - return evalCast(val, castTy, originalTy); -} - //===----------------------------------------------------------------------===// // Cast methods. // `evalCast` is the main method @@ -939,15 +901,63 @@ } else { // Symbol to integer, float. QualType T = Context.getCanonicalType(SE->getType()); - // If types are the same or both are integers, ignore the cast. - // FIXME: Remove this hack when we support symbolic truncation/extension. - // HACK: If both castTy and T are integers, ignore the cast. This is - // not a permanent solution. Eventually we want to precisely handle - // extension/truncation of symbolic integers. This prevents us from losing - // precision when we assign 'x = y' and 'y' is symbolic and x and y are - // different integer types. - if (haveSameType(T, CastTy)) - return V; + // If castTy and T are different integers, return a cast of the given + // symbol. This helps RangeConstraintManager to recognize the cast and + // reason about new ranges. See symbolic truncation/extension/conversions + // handles in SymbolicRangeInferrer::VisitSymbolCast. + if (T->isIntegralOrEnumerationType() && + CastTy->isIntegralOrEnumerationType()) { + // If types are the same, ignore the cast. + if (T == CastTy) + return V; + // Simplify SymbolCast if no truncation occurred. This reduce unnecessary + // nested types between the root operand and the outermost type. + // E.g. (short)(int)(long)(char x) equivalent to (short)(char x). + if (isa(SE)) { + ASTContext &Ctx = getContext(); + SymbolRef OperandSym = cast(SE)->getOperand(); + QualType OperandTy = OperandSym->getType(); + uint32_t OperandBitWidth = Ctx.getIntWidth(OperandTy); + uint32_t SymBitWidth = Ctx.getIntWidth(T); + uint32_t CastBitWidth = Ctx.getIntWidth(CastTy); + const bool isTruncated = (OperandBitWidth > SymBitWidth); + if (isTruncated) { + const bool isMoreTruncated = (SymBitWidth >= CastBitWidth); + if (isMoreTruncated) { + // Cast type bit width is less than the current: + // - (char)(short)(int x) -> (char)int x. + SE = OperandSym; + T = CastTy; + } + } else { + // Original and cast types are equal: + // - (ushort)(int)(ushort x) -> ushort x; + // - (char)(uint)(char x) -> char x. + if (OperandTy == CastTy) + return nonloc::SymbolVal(OperandSym); + // Current and cast type bit widths or signs are equal: + // - (signed)(unsigned)(short x) -> (signed)short x; + // - (unsigned)(signed)(short x) -> (unsigned)short x; + // - (unsigned long long)(unsigned int)(short x) -> + // (unsigned long long)short x; + // - (long long)(int)(short x) -> (long long)short x; + if ((SymBitWidth == CastBitWidth) || + (T->isSignedIntegerOrEnumerationType() == + CastTy->isSignedIntegerOrEnumerationType())) { + SE = OperandSym; + T = OperandTy; + } + // Current and cast type signs are different: + // - (signed long long)(unsigned int)(short x) -> + // (signed long long)(unsigned int)short x; + // - (unsigned long long)(signed int)(short x) -> + // (unsigned long long)(signed int)short x; + // Do nothing extra. + // This prevent wrong `sext` and `zext` actions. + } + } + return makeNonLoc(SE, T, CastTy); + } if (!Loc::isLocType(CastTy)) if (!IsUnknownOriginalType || !CastTy->isFloatingType() || T->isFloatingType()) Index: clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp +++ clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp @@ -536,8 +536,13 @@ // We only handle LHS as simple symbols or SymIntExprs. SymbolRef Sym = lhs.castAs().getSymbol(); + // Unwrap SymbolCast trying to find SymIntExpr inside. + SymbolRef S = Sym; + while (isa(S)) + S = cast(S)->getOperand(); + // LHS is a symbolic expression. - if (const SymIntExpr *symIntExpr = dyn_cast(Sym)) { + if (const SymIntExpr *symIntExpr = dyn_cast(S)) { // Is this a logical not? (!x is represented as x == 0.) if (op == BO_EQ && rhs.isZeroConstant()) { Index: clang/test/Analysis/bool-assignment.c =================================================================== --- clang/test/Analysis/bool-assignment.c +++ clang/test/Analysis/bool-assignment.c @@ -39,14 +39,14 @@ void test_BOOL_initialization(int y) { BOOL constant = 2; // expected-warning {{Assignment of a non-Boolean value}} if (y < 0) { - BOOL x = y; // expected-warning {{Assignment of a non-Boolean value}} + BOOL x = y; // no-warning return; } if (y > 200 && y < 250) { #ifdef ANALYZER_CM_Z3 BOOL x = y; // expected-warning {{Assignment of a non-Boolean value}} #else - BOOL x = y; // no-warning + BOOL x = y; // expected-warning {{Assignment of a non-Boolean value}} #endif return; } @@ -64,11 +64,11 @@ void test_BOOL_assignment(int y) { BOOL x = 0; // no-warning if (y < 0) { - x = y; // expected-warning {{Assignment of a non-Boolean value}} + x = y; // no-warning return; } if (y > 1) { - x = y; // expected-warning {{Assignment of a non-Boolean value}} + x = y; // no-warning return; } x = y; // no-warning @@ -82,11 +82,11 @@ void test_Boolean_initialization(int y) { Boolean constant = 2; // expected-warning {{Assignment of a non-Boolean value}} if (y < 0) { - Boolean x = y; // expected-warning {{Assignment of a non-Boolean value}} + Boolean x = y; // no-warning return; } if (y > 1) { - Boolean x = y; // expected-warning {{Assignment of a non-Boolean value}} + Boolean x = y; // no-warning return; } Boolean x = y; // no-warning @@ -95,11 +95,11 @@ void test_Boolean_assignment(int y) { Boolean x = 0; // no-warning if (y < 0) { - x = y; // expected-warning {{Assignment of a non-Boolean value}} + x = y; // no-warning return; } if (y > 1) { - x = y; // expected-warning {{Assignment of a non-Boolean value}} + x = y; // no-warning return; } x = y; // no-warning Index: clang/test/Analysis/constant-folding.c =================================================================== --- clang/test/Analysis/constant-folding.c +++ clang/test/Analysis/constant-folding.c @@ -238,9 +238,7 @@ if (a <= 10) { // Result is unsigned. This means that 'c' is casted to unsigned. - // We don't want to reason about ranges changing boundaries with - // conversions. - clang_analyzer_eval((a % c) < 30); // expected-warning{{UNKNOWN}} + clang_analyzer_eval((a % c) < 30); // expected-warning{{TRUE}} } } Index: clang/test/Analysis/constraint_manager_negate_difference.c =================================================================== --- clang/test/Analysis/constraint_manager_negate_difference.c +++ clang/test/Analysis/constraint_manager_negate_difference.c @@ -7,8 +7,8 @@ #define UINT_MIN (0U) #define UINT_MAX (~UINT_MIN) #define UINT_MID (UINT_MAX / 2 + 1) -#define INT_MAX (UINT_MAX & (UINT_MAX >> 1)) -#define INT_MIN (UINT_MAX & ~(UINT_MAX >> 1)) +#define INT_MAX (int)(UINT_MAX & (UINT_MAX >> 1)) +#define INT_MIN (int)(UINT_MAX & ~(UINT_MAX >> 1)) extern void __assert_fail (__const char *__assertion, __const char *__file, unsigned int __line, __const char *__function) Index: clang/test/Analysis/explain-svals.cpp =================================================================== --- clang/test/Analysis/explain-svals.cpp +++ clang/test/Analysis/explain-svals.cpp @@ -46,15 +46,15 @@ void test_2(char *ptr, int ext) { clang_analyzer_explain((void *) "asdf"); // expected-warning-re{{{{^pointer to element of type 'char' with index 0 of string literal "asdf"$}}}} - clang_analyzer_explain(strlen(ptr)); // expected-warning-re{{{{^metadata of type 'unsigned long' tied to pointee of argument 'ptr'$}}}} + clang_analyzer_explain(strlen(ptr)); // expected-warning-re{{{{^\(int\)metadata of type 'unsigned long' tied to pointee of argument 'ptr'$}}}} clang_analyzer_explain(conjure()); // expected-warning-re{{{{^symbol of type 'int' conjured at statement 'conjure\(\)'$}}}} clang_analyzer_explain(glob); // expected-warning-re{{{{^value derived from \(symbol of type 'int' conjured at statement 'conjure\(\)'\) for global variable 'glob'$}}}} clang_analyzer_explain(glob_ptr); // expected-warning-re{{{{^value derived from \(symbol of type 'int' conjured at statement 'conjure\(\)'\) for global variable 'glob_ptr'$}}}} - clang_analyzer_explain(clang_analyzer_getExtent(ptr)); // expected-warning-re{{{{^extent of pointee of argument 'ptr'$}}}} + clang_analyzer_explain(clang_analyzer_getExtent(ptr)); // expected-warning-re{{{{^\(int\)extent of pointee of argument 'ptr'$}}}} int *x = new int[ext]; clang_analyzer_explain(x); // expected-warning-re{{{{^pointer to element of type 'int' with index 0 of heap segment that starts at symbol of type 'int \*' conjured at statement 'new int \[ext\]'$}}}} // Sic! What gets computed is the extent of the element-region. - clang_analyzer_explain(clang_analyzer_getExtent(x)); // expected-warning-re{{{{^\(argument 'ext'\) \* 4$}}}} + clang_analyzer_explain(clang_analyzer_getExtent(x)); // expected-warning-re{{{{^\(int\)\(\(unsigned long\)argument 'ext'\) \* 4$}}}} delete[] x; } @@ -91,7 +91,7 @@ public: void test_5(int i) { clang_analyzer_explain(this); // expected-warning-re{{{{^pointer to 'this' object$}}}} - clang_analyzer_explain(&x[i]); // expected-warning-re{{{{^pointer to element of type 'int' with index 'argument 'i'' of field 'x' of 'this' object$}}}} + clang_analyzer_explain(&x[i]); // expected-warning-re{{{{^pointer to element of type 'int' with index '\(long long\)argument 'i'' of field 'x' of 'this' object$}}}} clang_analyzer_explain(__builtin_alloca(i)); // expected-warning-re{{{{^pointer to region allocated by '__builtin_alloca\(i\)'$}}}} } }; Index: clang/test/Analysis/expr-inspection.cpp =================================================================== --- clang/test/Analysis/expr-inspection.cpp +++ clang/test/Analysis/expr-inspection.cpp @@ -2,7 +2,8 @@ // Self-tests for the debug.ExprInspection checker. -void clang_analyzer_denote(int x, const char *str); +template +void clang_analyzer_denote(T x, const char *str); void clang_analyzer_express(int x); // Invalid declarations to test sanity checks. @@ -19,7 +20,7 @@ clang_analyzer_denote(x, "$x"); clang_analyzer_denote(y, "$y"); - clang_analyzer_express(x + y); // expected-warning{{$x + $y}} + clang_analyzer_express(x + y); // expected-warning{{(unsigned int)$x + $y}} clang_analyzer_denote(1, "$z"); // expected-warning{{Not a symbol}} clang_analyzer_express(1); // expected-warning{{Not a symbol}} Index: clang/test/Analysis/malloc.c =================================================================== --- clang/test/Analysis/malloc.c +++ clang/test/Analysis/malloc.c @@ -1893,6 +1893,6 @@ // expected-warning-re@-1 {{{{^conj_\$[[:digit:]]+{int, LC1, S[[:digit:]]+, #1}}}}}} int *p = (int *)malloc(x); clang_analyzer_dumpExtent(p); - // expected-warning-re@-1 {{{{^conj_\$[[:digit:]]+{int, LC1, S[[:digit:]]+, #1}}}}}} + // expected-warning-re@-1 {{{{^\(unsigned long long\) \(conj_\$[[:digit:]]+{int, LC1, S[[:digit:]]+, #1}\)}}}}} free(p); } Index: clang/test/Analysis/std-c-library-functions.c =================================================================== --- clang/test/Analysis/std-c-library-functions.c +++ clang/test/Analysis/std-c-library-functions.c @@ -214,8 +214,11 @@ int isprint(int); void test_isgraph_isprint(int x) { char y = x; - if (isgraph(y)) + if (isgraph(y)) { + // expected-warning@+1{{FALSE}} clang_analyzer_eval(isprint(x)); // expected-warning{{TRUE}} + clang_analyzer_eval(isprint((char)x)); // expected-warning{{TRUE}} + } } int isdigit(int); @@ -234,7 +237,7 @@ void test_isspace(int x) { if (!isascii(x)) return; - char y = x; + int y = x; if (y == ' ') clang_analyzer_eval(isspace(x)); // expected-warning{{TRUE}} } Index: clang/test/Analysis/svalbuilder-rearrange-comparisons.c =================================================================== --- clang/test/Analysis/svalbuilder-rearrange-comparisons.c +++ clang/test/Analysis/svalbuilder-rearrange-comparisons.c @@ -515,7 +515,7 @@ clang_analyzer_denote(x, "$x"); clang_analyzer_denote(y, "$y"); clang_analyzer_express(y); // expected-warning {{$y}} - clang_analyzer_express(x == y); // expected-warning {{$x - $y == 0}} + clang_analyzer_express(x == y); // expected-warning {{(unsigned int)$x == (unsigned int)$y}} } void compare_different_symbol_plus_left_int_equal_unsigned() { @@ -524,7 +524,7 @@ clang_analyzer_denote(y, "$y"); clang_analyzer_express(x); // expected-warning {{$x + 1}} clang_analyzer_express(y); // expected-warning {{$y}} - clang_analyzer_express(x == y); // expected-warning {{$y - $x == 1}} + clang_analyzer_express(x == y); // expected-warning {{(unsigned int)$x + 1 == (unsigned int)$y}} } void compare_different_symbol_minus_left_int_equal_unsigned() { @@ -533,7 +533,7 @@ clang_analyzer_denote(y, "$y"); clang_analyzer_express(x); // expected-warning {{$x - 1}} clang_analyzer_express(y); // expected-warning {{$y}} - clang_analyzer_express(x == y); // expected-warning {{$x - $y == 1}} + clang_analyzer_express(x == y); // expected-warning {{(unsigned int)$x - 1 == (unsigned int)$y}} } void compare_different_symbol_plus_right_int_equal_unsigned() { @@ -541,7 +541,7 @@ clang_analyzer_denote(x, "$x"); clang_analyzer_denote(y - 2, "$y"); clang_analyzer_express(y); // expected-warning {{$y + 2}} - clang_analyzer_express(x == y); // expected-warning {{$x - $y == 2}} + clang_analyzer_express(x == y); // expected-warning {{(unsigned int)$x == (unsigned int)$y + 2}} } void compare_different_symbol_minus_right_int_equal_unsigned() { @@ -549,7 +549,7 @@ clang_analyzer_denote(x, "$x"); clang_analyzer_denote(y + 2, "$y"); clang_analyzer_express(y); // expected-warning {{$y - 2}} - clang_analyzer_express(x == y); // expected-warning {{$y - $x == 2}} + clang_analyzer_express(x == y); // expected-warning {{(unsigned int)$x == (unsigned int)$y - 2}} } void compare_different_symbol_plus_left_plus_right_int_equal_unsigned() { @@ -558,7 +558,7 @@ clang_analyzer_denote(y - 1, "$y"); clang_analyzer_express(x); // expected-warning {{$x + 2}} clang_analyzer_express(y); // expected-warning {{$y + 1}} - clang_analyzer_express(x == y); // expected-warning {{$y - $x == 1}} + clang_analyzer_express(x == y); // expected-warning {{(unsigned int)$x + 2 == (unsigned int)$y + 1}} } void compare_different_symbol_plus_left_minus_right_int_equal_unsigned() { @@ -567,7 +567,7 @@ clang_analyzer_denote(y + 1, "$y"); clang_analyzer_express(x); // expected-warning {{$x + 2}} clang_analyzer_express(y); // expected-warning {{$y - 1}} - clang_analyzer_express(x == y); // expected-warning {{$y - $x == 3}} + clang_analyzer_express(x == y); // expected-warning {{(unsigned int)$x + 2 == (unsigned int)$y - 1}} } void compare_different_symbol_minus_left_plus_right_int_equal_unsigned() { @@ -576,7 +576,7 @@ clang_analyzer_denote(y - 1, "$y"); clang_analyzer_express(x); // expected-warning {{$x - 2}} clang_analyzer_express(y); // expected-warning {{$y + 1}} - clang_analyzer_express(x == y); // expected-warning {{$x - $y == 3}} + clang_analyzer_express(x == y); // expected-warning {{(unsigned int)$x - 2 == (unsigned int)$y + 1}} } void compare_different_symbol_minus_left_minus_right_int_equal_unsigned() { @@ -585,7 +585,7 @@ clang_analyzer_denote(y + 1, "$y"); clang_analyzer_express(x); // expected-warning {{$x - 2}} clang_analyzer_express(y); // expected-warning {{$y - 1}} - clang_analyzer_express(x == y); // expected-warning {{$x - $y == 1}} + clang_analyzer_express(x == y); // expected-warning {{(unsigned int)$x - 2 == (unsigned int)$y - 1}} } void compare_same_symbol_equal_unsigned() { @@ -601,7 +601,7 @@ ++x; clang_analyzer_express(x); // expected-warning {{$x + 1}} clang_analyzer_express(y); // expected-warning {{$x}} - clang_analyzer_express(x == y); // expected-warning {{$x + 1U == $x}} + clang_analyzer_express(x == y); // expected-warning {{(unsigned int)$x + 1U == (unsigned int)$x}} } void compare_same_symbol_minus_left_int_equal_unsigned() { @@ -610,21 +610,21 @@ --x; clang_analyzer_express(x); // expected-warning {{$x - 1}} clang_analyzer_express(y); // expected-warning {{$x}} - clang_analyzer_express(x == y); // expected-warning {{$x - 1U == $x}} + clang_analyzer_express(x == y); // expected-warning {{(unsigned int)$x - 1U == (unsigned int)$x}} } void compare_same_symbol_plus_right_int_equal_unsigned() { unsigned x = f(), y = x + 1; clang_analyzer_denote(x, "$x"); clang_analyzer_express(y); // expected-warning {{$x + 1}} - clang_analyzer_express(x == y); // expected-warning {{$x == $x + 1U}} + clang_analyzer_express(x == y); // expected-warning {{(unsigned int)$x == (unsigned int)$x + 1U}} } void compare_same_symbol_minus_right_int_equal_unsigned() { unsigned x = f(), y = x - 1; clang_analyzer_denote(x, "$x"); clang_analyzer_express(y); // expected-warning {{$x - 1}} - clang_analyzer_express(x == y); // expected-warning {{$x == $x - 1U}} + clang_analyzer_express(x == y); // expected-warning {{(unsigned int)$x == (unsigned int)$x - 1U}} } void compare_same_symbol_plus_left_plus_right_int_equal_unsigned() { @@ -642,7 +642,7 @@ ++x; clang_analyzer_express(x); // expected-warning {{$x + 1}} clang_analyzer_express(y); // expected-warning {{$x - 1}} - clang_analyzer_express(x == y); // expected-warning {{$x + 1U == $x - 1U}} + clang_analyzer_express(x == y); // expected-warning {{(unsigned int)$x + 1U == (unsigned int)$x - 1U}} } void compare_same_symbol_minus_left_plus_right_int_equal_unsigned() { @@ -651,7 +651,7 @@ --x; clang_analyzer_express(x); // expected-warning {{$x - 1}} clang_analyzer_express(y); // expected-warning {{$x + 1}} - clang_analyzer_express(x == y); // expected-warning {{$x - 1U == $x + 1U}} + clang_analyzer_express(x == y); // expected-warning {{(unsigned int)$x - 1U == (unsigned int)$x + 1U}} } void compare_same_symbol_minus_left_minus_right_int_equal_unsigned() { @@ -668,7 +668,7 @@ clang_analyzer_denote(x, "$x"); clang_analyzer_denote(y, "$y"); clang_analyzer_express(y); // expected-warning {{$y}} - clang_analyzer_express(x <= y); // expected-warning {{$x - $y <= 0}} + clang_analyzer_express(x <= y); // expected-warning {{(unsigned int)$x <= (unsigned int)$y}} } void compare_different_symbol_plus_left_int_less_or_equal_unsigned() { @@ -677,7 +677,7 @@ clang_analyzer_denote(y, "$y"); clang_analyzer_express(x); // expected-warning {{$x + 1}} clang_analyzer_express(y); // expected-warning {{$y}} - clang_analyzer_express(x <= y); // expected-warning {{$y - $x >= 1}} + clang_analyzer_express(x <= y); // expected-warning {{(unsigned int)$x + 1 <= (unsigned int)$y}} } void compare_different_symbol_minus_left_int_less_or_equal_unsigned() { @@ -686,7 +686,7 @@ clang_analyzer_denote(y, "$y"); clang_analyzer_express(x); // expected-warning {{$x - 1}} clang_analyzer_express(y); // expected-warning {{$y}} - clang_analyzer_express(x <= y); // expected-warning {{$x - $y <= 1}} + clang_analyzer_express(x <= y); // expected-warning {{(unsigned int)$x - 1 <= (unsigned int)$y}} } void compare_different_symbol_plus_right_int_less_or_equal_unsigned() { @@ -694,7 +694,7 @@ clang_analyzer_denote(x, "$x"); clang_analyzer_denote(y - 2, "$y"); clang_analyzer_express(y); // expected-warning {{$y + 2}} - clang_analyzer_express(x <= y); // expected-warning {{$x - $y <= 2}} + clang_analyzer_express(x <= y); // expected-warning {{(unsigned int)$x <= (unsigned int)$y + 2}} } void compare_different_symbol_minus_right_int_less_or_equal_unsigned() { @@ -702,7 +702,7 @@ clang_analyzer_denote(x, "$x"); clang_analyzer_denote(y + 2, "$y"); clang_analyzer_express(y); // expected-warning {{$y - 2}} - clang_analyzer_express(x <= y); // expected-warning {{$y - $x >= 2}} + clang_analyzer_express(x <= y); // expected-warning {{(unsigned int)$x <= (unsigned int)$y - 2}} } void compare_different_symbol_plus_left_plus_right_int_less_or_equal_unsigned() { @@ -711,7 +711,7 @@ clang_analyzer_denote(y - 1, "$y"); clang_analyzer_express(x); // expected-warning {{$x + 2}} clang_analyzer_express(y); // expected-warning {{$y + 1}} - clang_analyzer_express(x <= y); // expected-warning {{$y - $x >= 1}} + clang_analyzer_express(x <= y); // expected-warning {{(unsigned int)$x + 2 <= (unsigned int)$y + 1}} } void compare_different_symbol_plus_left_minus_right_int_less_or_equal_unsigned() { @@ -720,7 +720,7 @@ clang_analyzer_denote(y + 1, "$y"); clang_analyzer_express(x); // expected-warning {{$x + 2}} clang_analyzer_express(y); // expected-warning {{$y - 1}} - clang_analyzer_express(x <= y); // expected-warning {{$y - $x >= 3}} + clang_analyzer_express(x <= y); // expected-warning {{(unsigned int)$x + 2 <= (unsigned int)$y - 1}} } void compare_different_symbol_minus_left_plus_right_int_less_or_equal_unsigned() { @@ -729,7 +729,7 @@ clang_analyzer_denote(y - 1, "$y"); clang_analyzer_express(x); // expected-warning {{$x - 2}} clang_analyzer_express(y); // expected-warning {{$y + 1}} - clang_analyzer_express(x <= y); // expected-warning {{$x - $y <= 3}} + clang_analyzer_express(x <= y); // expected-warning {{(unsigned int)$x - 2 <= (unsigned int)$y + 1}} } void compare_different_symbol_minus_left_minus_right_int_less_or_equal_unsigned() { @@ -738,7 +738,7 @@ clang_analyzer_denote(y + 1, "$y"); clang_analyzer_express(x); // expected-warning {{$x - 2}} clang_analyzer_express(y); // expected-warning {{$y - 1}} - clang_analyzer_express(x <= y); // expected-warning {{$x - $y <= 1}} + clang_analyzer_express(x <= y); // expected-warning {{(unsigned int)$x - 2 <= (unsigned int)$y - 1}} } void compare_same_symbol_less_or_equal_unsigned() { @@ -754,7 +754,7 @@ ++x; clang_analyzer_express(x); // expected-warning {{$x + 1}} clang_analyzer_express(y); // expected-warning {{$x}} - clang_analyzer_express(x <= y); // expected-warning {{$x + 1U <= $x}} + clang_analyzer_express(x <= y); // expected-warning {{(unsigned int)$x + 1U <= (unsigned int)$x}} } void compare_same_symbol_minus_left_int_less_or_equal_unsigned() { @@ -763,21 +763,21 @@ --x; clang_analyzer_express(x); // expected-warning {{$x - 1}} clang_analyzer_express(y); // expected-warning {{$x}} - clang_analyzer_express(x <= y); // expected-warning {{$x - 1U <= $x}} + clang_analyzer_express(x <= y); // expected-warning {{(unsigned int)$x - 1U <= (unsigned int)$x}} } void compare_same_symbol_plus_right_int_less_or_equal_unsigned() { unsigned x = f(), y = x + 1; clang_analyzer_denote(x, "$x"); clang_analyzer_express(y); // expected-warning {{$x + 1}} - clang_analyzer_express(x <= y); // expected-warning {{$x <= $x + 1U}} + clang_analyzer_express(x <= y); // expected-warning {{(unsigned int)$x <= (unsigned int)$x + 1U}} } void compare_same_symbol_minus_right_int_less_or_equal_unsigned() { unsigned x = f(), y = x - 1; clang_analyzer_denote(x, "$x"); clang_analyzer_express(y); // expected-warning {{$x - 1}} - clang_analyzer_express(x <= y); // expected-warning {{$x <= $x - 1U}} + clang_analyzer_express(x <= y); // expected-warning {{(unsigned int)$x <= (unsigned int)$x - 1U}} } void compare_same_symbol_plus_left_plus_right_int_less_or_equal_unsigned() { @@ -795,7 +795,7 @@ ++x; clang_analyzer_express(x); // expected-warning {{$x + 1}} clang_analyzer_express(y); // expected-warning {{$x - 1}} - clang_analyzer_express(x <= y); // expected-warning {{$x + 1U <= $x - 1U}} + clang_analyzer_express(x <= y); // expected-warning {{(unsigned int)$x + 1U <= (unsigned int)$x - 1U}} } void compare_same_symbol_minus_left_plus_right_int_less_or_equal_unsigned() { @@ -804,7 +804,7 @@ --x; clang_analyzer_express(x); // expected-warning {{$x - 1}} clang_analyzer_express(y); // expected-warning {{$x + 1}} - clang_analyzer_express(x <= y); // expected-warning {{$x - 1U <= $x + 1U}} + clang_analyzer_express(x <= y); // expected-warning {{(unsigned int)$x - 1U <= (unsigned int)$x + 1U}} } void compare_same_symbol_minus_left_minus_right_int_less_or_equal_unsigned() { @@ -821,7 +821,7 @@ clang_analyzer_denote(x, "$x"); clang_analyzer_denote(y, "$y"); clang_analyzer_express(y); // expected-warning {{$y}} - clang_analyzer_express(x < y); // expected-warning {{$x - $y < 0}} + clang_analyzer_express(x < y); // expected-warning {{(unsigned int)$x < (unsigned int)$y}} } void compare_different_symbol_plus_left_int_less_unsigned() { @@ -830,7 +830,7 @@ clang_analyzer_denote(y, "$y"); clang_analyzer_express(x); // expected-warning {{$x + 1}} clang_analyzer_express(y); // expected-warning {{$y}} - clang_analyzer_express(x < y); // expected-warning {{$y - $x > 1}} + clang_analyzer_express(x < y); // expected-warning {{(unsigned int)$x + 1 < (unsigned int)$y}} } void compare_different_symbol_minus_left_int_less_unsigned() { @@ -839,7 +839,7 @@ clang_analyzer_denote(y, "$y"); clang_analyzer_express(x); // expected-warning {{$x - 1}} clang_analyzer_express(y); // expected-warning {{$y}} - clang_analyzer_express(x < y); // expected-warning {{$x - $y < 1}} + clang_analyzer_express(x < y); // expected-warning {{(unsigned int)$x - 1 < (unsigned int)$y}} } void compare_different_symbol_plus_right_int_less_unsigned() { @@ -847,7 +847,7 @@ clang_analyzer_denote(x, "$x"); clang_analyzer_denote(y - 2, "$y"); clang_analyzer_express(y); // expected-warning {{$y + 2}} - clang_analyzer_express(x < y); // expected-warning {{$x - $y < 2}} + clang_analyzer_express(x < y); // expected-warning {{(unsigned int)$x < (unsigned int)$y + 2}} } void compare_different_symbol_minus_right_int_less_unsigned() { @@ -855,7 +855,7 @@ clang_analyzer_denote(x, "$x"); clang_analyzer_denote(y + 2, "$y"); clang_analyzer_express(y); // expected-warning {{$y - 2}} - clang_analyzer_express(x < y); // expected-warning {{$y - $x > 2}} + clang_analyzer_express(x < y); // expected-warning {{(unsigned int)$x < (unsigned int)$y - 2}} } void compare_different_symbol_plus_left_plus_right_int_less_unsigned() { @@ -864,7 +864,7 @@ clang_analyzer_denote(y - 1, "$y"); clang_analyzer_express(x); // expected-warning {{$x + 2}} clang_analyzer_express(y); // expected-warning {{$y + 1}} - clang_analyzer_express(x < y); // expected-warning {{$y - $x > 1}} + clang_analyzer_express(x < y); // expected-warning {{(unsigned int)$x + 2 < (unsigned int)$y + 1}} } void compare_different_symbol_plus_left_minus_right_int_less_unsigned() { @@ -873,7 +873,7 @@ clang_analyzer_denote(y + 1, "$y"); clang_analyzer_express(x); // expected-warning {{$x + 2}} clang_analyzer_express(y); // expected-warning {{$y - 1}} - clang_analyzer_express(x < y); // expected-warning {{$y - $x > 3}} + clang_analyzer_express(x < y); // expected-warning {{(unsigned int)$x + 2 < (unsigned int)$y - 1}} } void compare_different_symbol_minus_left_plus_right_int_less_unsigned() { @@ -882,7 +882,7 @@ clang_analyzer_denote(y - 1, "$y"); clang_analyzer_express(x); // expected-warning {{$x - 2}} clang_analyzer_express(y); // expected-warning {{$y + 1}} - clang_analyzer_express(x < y); // expected-warning {{$x - $y < 3}} + clang_analyzer_express(x < y); // expected-warning {{(unsigned int)$x - 2 < (unsigned int)$y + 1}} } void compare_different_symbol_minus_left_minus_right_int_less_unsigned() { @@ -891,7 +891,7 @@ clang_analyzer_denote(y + 1, "$y"); clang_analyzer_express(x); // expected-warning {{$x - 2}} clang_analyzer_express(y); // expected-warning {{$y - 1}} - clang_analyzer_express(x < y); // expected-warning {{$x - $y < 1}} + clang_analyzer_express(x < y); // expected-warning {{(unsigned int)$x - 2 < (unsigned int)$y - 1}} } void compare_same_symbol_less_unsigned() { @@ -907,7 +907,7 @@ ++x; clang_analyzer_express(x); // expected-warning {{$x + 1}} clang_analyzer_express(y); // expected-warning {{$x}} - clang_analyzer_express(x < y); // expected-warning {{$x + 1U < $x}} + clang_analyzer_express(x < y); // expected-warning {{(unsigned int)$x + 1U < (unsigned int)$x}} } void compare_same_symbol_minus_left_int_less_unsigned() { @@ -916,21 +916,21 @@ --x; clang_analyzer_express(x); // expected-warning {{$x - 1}} clang_analyzer_express(y); // expected-warning {{$x}} - clang_analyzer_express(x < y); // expected-warning {{$x - 1U < $x}} + clang_analyzer_express(x < y); // expected-warning {{(unsigned int)$x - 1U < (unsigned int)$x}} } void compare_same_symbol_plus_right_int_less_unsigned() { unsigned x = f(), y = x + 1; clang_analyzer_denote(x, "$x"); clang_analyzer_express(y); // expected-warning {{$x + 1}} - clang_analyzer_express(x < y); // expected-warning {{$x < $x + 1U}} + clang_analyzer_express(x < y); // expected-warning {{(unsigned int)$x < (unsigned int)$x + 1U}} } void compare_same_symbol_minus_right_int_less_unsigned() { unsigned x = f(), y = x - 1; clang_analyzer_denote(x, "$x"); clang_analyzer_express(y); // expected-warning {{$x - 1}} - clang_analyzer_express(x < y); // expected-warning {{$x < $x - 1U}} + clang_analyzer_express(x < y); // expected-warning {{(unsigned int)$x < (unsigned int)$x - 1U}} } void compare_same_symbol_plus_left_plus_right_int_less_unsigned() { @@ -948,7 +948,7 @@ ++x; clang_analyzer_express(x); // expected-warning {{$x + 1}} clang_analyzer_express(y); // expected-warning {{$x - 1}} - clang_analyzer_express(x < y); // expected-warning {{$x + 1U < $x - 1U}} + clang_analyzer_express(x < y); // expected-warning {{(unsigned int)$x + 1U < (unsigned int)$x - 1U}} } void compare_same_symbol_minus_left_plus_right_int_less_unsigned() { @@ -957,7 +957,7 @@ --x; clang_analyzer_express(x); // expected-warning {{$x - 1}} clang_analyzer_express(y); // expected-warning {{$x + 1}} - clang_analyzer_express(x < y); // expected-warning {{$x - 1U < $x + 1U}} + clang_analyzer_express(x < y); // expected-warning {{(unsigned int)$x - 1U < (unsigned int)$x + 1U}} } void compare_same_symbol_minus_left_minus_right_int_less_unsigned() { Index: clang/test/Analysis/symbol-integral-cast.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/symbol-integral-cast.cpp @@ -0,0 +1,353 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection -analyzer-config eagerly-assume=false -verify %s + +template +void clang_analyzer_eval(T); +void clang_analyzer_warnIfReached(); + +typedef short int16_t; +typedef int int32_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; + +void test1(int x) { + // Even if two lower bytes of `x` equal to zero, it doesn't mean that + // the entire `x` is zero. We are not able to know the exact value of x. + // It can be one of 65536 possible values like [0, 65536, 131072, ...] + // and so on. To avoid huge range sets we still assume `x` in the range + // [INT_MIN, INT_MAX]. + if (!(short)x) { + if (!x) + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + } +} + +void test2(int x) { + // If two lower bytes of `x` equal to zero, and we know x to be 65537, + // which is not truncated to short as zero. Thus the branch is infisible. + short s = x; + if (!s) { + if (x == 65537) + clang_analyzer_warnIfReached(); // no-warning + else + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + } +} + +void test3(int x, short s) { + s = x; + if ((short)x > -10 && s < 10) { + if (x > 0 && x < 10) { + // If the range of the whole variable was constrained then reason again + // about truncated bytes to make the ranges more precise. + clang_analyzer_eval((short)x <= 0); // expected-warning {{FALSE}} + } + } +} + +void test4(unsigned x) { + if ((char)x > 8) { + // Constraint the range of the lowest byte of `x` to [9, CHAR_MAX]. + // The original range of `x` still remains [0, UINT_MAX]. + clang_analyzer_eval((char)x < 42); // expected-warning {{UNKNOWN}} + if (x < 42) { + // Constraint the original range to [0, 42] and update (re-constraint) + // the range of the lowest byte of 'x' to [9, 42]. + clang_analyzer_eval((char)x < 42); // expected-warning {{TRUE}} + } + } +} + +void test5(unsigned x) { + if ((char)x > -10 && (char)x < 10) { + if ((short)x == 8) { + // If the range of higher bytes(short) was constrained then reason again + // about smaller truncated ranges(char) to make it more precise. + clang_analyzer_eval((char)x == 8); // expected-warning {{TRUE}} + clang_analyzer_eval((short)x == 8); // expected-warning {{TRUE}} + // We still assume full version of `x` in the range [INT_MIN, INT_MAX]. + clang_analyzer_eval(x == 8); // expected-warning {{UNKNOWN}} + } + } +} + +void test6(int x) { + // Even if two lower bytes of `x` less than zero, it doesn't mean that `x` + // can't be greater than zero. Thence we don't change the native range of + // `x` and this branch is feasible. + if (x > 0) + if ((short)x < 0) + clang_analyzer_eval(x > 0); // expected-warning {{TRUE}} +} + +void test7(int x) { + // The range of two lower bytes of `x` [1, SHORT_MAX] is enough to cover + // all possible values of char [CHAR_MIN, CHAR_MAX]. So the lowest byte + // can be lower than zero. + if ((short)x > 0) { + if ((char)x < 0) + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + } +} + +void test8(int x) { + // Promotion from `signed int` to `signed long long` also reasoning about the + // original range, because we know the fact that even after promotion it + // remains in the range [INT_MIN, INT_MAX]. + if ((long long)x < 0) + clang_analyzer_eval(x < 0); // expected-warning {{TRUE}} +} + +void test9(signed int x) { + // Any cast `signed` to `unsigned` produces an unsigned range, which is + // [0, UNSIGNED_MAX] and can not be lower than zero. + if ((unsigned long long)x < 0) + clang_analyzer_warnIfReached(); // no-warning + else + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + + if ((unsigned int)x < 0) + clang_analyzer_warnIfReached(); // no-warning + else + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + + if ((unsigned short)x < 0) + clang_analyzer_warnIfReached(); // no-warning + else + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + + if ((unsigned char)x < 0) + clang_analyzer_warnIfReached(); // no-warning + else + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} +} + +void test10(unsigned int x, signed char sc) { + // Promotion from `unsigned` to `signed` produces a signed range, + // which is able to cover all the values of the original, + // so that such cast is not lower than zero. + if ((signed long long)x < 0) + clang_analyzer_warnIfReached(); // no-warning + else + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + + // Any other cast(conversion or truncation) from `unsigned` to `signed` + // produces a signed range, which is [SIGNED_MIN, SIGNED_MAX] + // and can be lower than zero. + if ((signed int)x < 0) // explicit cast + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + + signed short ss = x; // initialization + if (ss < 0) + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + + sc = x; // assignment + if (sc < 0) + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} +} + +void test11(unsigned int x) { + // Promotion from 'unsigned' to 'signed' entirely covers the original range. + // Thence such cast is not lower than zero and the `true` branch is + // infiseable. But it doesn't affect the original range, which still remains + // as [0, UNSIGNED_MAX]. + if ((signed long long)x < 0) + clang_analyzer_warnIfReached(); // no-warning + else + clang_analyzer_eval(x < 0); // expected-warning {{FALSE}} + + // Any other cast(conversion or truncation) from `unsigned` to `signed` + // produces a signed range, which is [SIGNED_MIN, SIGNED_MAX]. But it doesn't + // affect the original range, which still remains as [0, UNSIGNED_MAX]. + if ((signed int)x < 0) + clang_analyzer_eval(x < 0); // expected-warning {{FALSE}} + + if ((signed short)x < 0) + clang_analyzer_eval(x < 0); // expected-warning {{FALSE}} + + if ((signed char)x < 0) + clang_analyzer_eval(x < 0); // expected-warning {{FALSE}} +} + +void test12(int x, char c) { + if (x >= 5308) { + if (x <= 5419) { + // Truncation on assignment: int[5308, 5419] -> char[-68, 43] + c = x; + clang_analyzer_eval(-68 <= c && c <= 43); // expected-warning {{TRUE}} + + if (c < 50) + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + else + clang_analyzer_warnIfReached(); // no-warning + + // Truncation on initializaion: int[5308, 5419] -> char[-68, 43] + char c1 = x; + clang_analyzer_eval(-68 <= c1 && c1 <= 43); // expected-warning {{TRUE}} + } + } +} + +void test13(int x) { + if (x > 913440767 && x < 913440769) { // 0x36720000 + + if ((short)x) // Truncation: int[913440768] -> short[0] + clang_analyzer_warnIfReached(); // no-warning + else + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + + if ((short)x != 0) + clang_analyzer_warnIfReached(); // no-warning + else + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + } +} + +void test14(int x) { + if (x >= -1569193983 && x <= 578290016) { + // The big range of `x` covers all possible values of short. + // Truncation: int[-1569193983, 578290016] -> short[-32768, 32767] + if ((short)x > 0) { + clang_analyzer_eval(-1569193983 <= x && x <= 578290016); // expected-warning {{TRUE}} + short s = x; + clang_analyzer_eval(-32768 <= s && s <= 32767); // expected-warning {{TRUE}} + } + } +} + +void test15(int x) { + if (x >= -1569193983 && x <= -1569193871) { // [0xA2780001, 0xA2780071] + // The small range of `x` covers only several values of short. + // Truncation: int[-1569193983, -1569193871] -> short[1, 113] + if ((short)x) + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + else + clang_analyzer_warnIfReached(); // no-warning + + if ((short)x > 0) + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + else + clang_analyzer_warnIfReached(); // no-warning + + if ((short)x < 114) + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + else + clang_analyzer_warnIfReached(); // no-warning + } +} + +void test16(char x) { + if (x < 0) + clang_analyzer_eval(-128 <= x && x < 0); // expected-warning {{TRUE}} + else + clang_analyzer_eval(0 <= x && x <= 127); // expected-warning {{TRUE}} +} + +void test17(char x) { + if (-11 <= x && x <= -10) { + unsigned u = x; + // Conversion: char[-11, -10] -> unsigned int[4294967285, 4294967286] + clang_analyzer_eval(4294967285 <= u && u <= 4294967286); // expected-warning {{TRUE}} + unsigned short us = x; + // Conversion: char[-11, -10] -> unsigned short[65525, 65526] + clang_analyzer_eval(65525 <= us && us <= 65526); // expected-warning {{TRUE}} + unsigned char uc = x; + // Conversion: char[-11, -10] -> unsigned char[245, 246] + clang_analyzer_eval(245 <= uc && uc <= 246); // expected-warning {{TRUE}} + } +} + +void test18(char c, short s, int i) { + // Any char value always is less then 1000. + int OneThousand = 1000; + c = i; + if (c < OneThousand) + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + else + clang_analyzer_warnIfReached(); // no-warning + + // Any short value always is greater then 40000. + int MinusFourtyThousands = -40000; + s = i; + if (s > MinusFourtyThousands) + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + else + clang_analyzer_warnIfReached(); // no-warning +} + +void test19(char x, short y) { + if (-43 <= x && x <= -42) { // x[-42, -43] + y = 42; + clang_analyzer_eval(int16_t(x) < int16_t(y)); // expected-warning {{TRUE}} + clang_analyzer_eval(int16_t(x) < int32_t(y)); // expected-warning {{TRUE}} + clang_analyzer_eval(int32_t(x) < int16_t(y)); // expected-warning {{TRUE}} + clang_analyzer_eval(int32_t(x) < int32_t(y)); // expected-warning {{TRUE}} + + clang_analyzer_eval(int16_t(x) < uint16_t(y)); // expected-warning {{TRUE}} + clang_analyzer_eval(int16_t(x) < uint32_t(y)); // expected-warning {{FALSE}} + clang_analyzer_eval(int32_t(x) < uint16_t(y)); // expected-warning {{TRUE}} + clang_analyzer_eval(int32_t(x) < uint32_t(y)); // expected-warning {{FALSE}} + + clang_analyzer_eval(uint16_t(x) < int16_t(y)); // expected-warning {{FALSE}} + clang_analyzer_eval(uint16_t(x) < int32_t(y)); // expected-warning {{FALSE}} + clang_analyzer_eval(uint32_t(x) < int16_t(y)); // expected-warning {{FALSE}} + clang_analyzer_eval(uint32_t(x) < int32_t(y)); // expected-warning {{FALSE}} + + clang_analyzer_eval(uint16_t(x) < uint16_t(y)); // expected-warning {{FALSE}} + clang_analyzer_eval(uint16_t(x) < uint32_t(y)); // expected-warning {{FALSE}} + clang_analyzer_eval(uint32_t(x) < uint16_t(y)); // expected-warning {{FALSE}} + clang_analyzer_eval(uint32_t(x) < uint32_t(y)); // expected-warning {{FALSE}} + } +} + +void test20(char x, short y) { + if (42 <= y && y <= 43) { // y[42, 43] + x = -42; + clang_analyzer_eval(int16_t(x) < int16_t(y)); // expected-warning {{TRUE}} + clang_analyzer_eval(int16_t(x) < int32_t(y)); // expected-warning {{TRUE}} + clang_analyzer_eval(int32_t(x) < int16_t(y)); // expected-warning {{TRUE}} + clang_analyzer_eval(int32_t(x) < int32_t(y)); // expected-warning {{TRUE}} + + clang_analyzer_eval(int16_t(x) < uint16_t(y)); // expected-warning {{TRUE}} + clang_analyzer_eval(int16_t(x) < uint32_t(y)); // expected-warning {{FALSE}} + clang_analyzer_eval(int32_t(x) < uint16_t(y)); // expected-warning {{TRUE}} + clang_analyzer_eval(int32_t(x) < uint32_t(y)); // expected-warning {{FALSE}} + + clang_analyzer_eval(uint16_t(x) < int16_t(y)); // expected-warning {{FALSE}} + clang_analyzer_eval(uint16_t(x) < int32_t(y)); // expected-warning {{FALSE}} + clang_analyzer_eval(uint32_t(x) < int16_t(y)); // expected-warning {{FALSE}} + clang_analyzer_eval(uint32_t(x) < int32_t(y)); // expected-warning {{FALSE}} + + clang_analyzer_eval(uint16_t(x) < uint16_t(y)); // expected-warning {{FALSE}} + clang_analyzer_eval(uint16_t(x) < uint32_t(y)); // expected-warning {{FALSE}} + clang_analyzer_eval(uint32_t(x) < uint16_t(y)); // expected-warning {{FALSE}} + clang_analyzer_eval(uint32_t(x) < uint32_t(y)); // expected-warning {{FALSE}} + } +} + +void test21(unsigned x) { + if (x > 42) { + // Unsigned range can generate two signed ranges. + // Conversion: unsigned[43, 4294967295] -> int[-2147483648, -1]U[43, 2147483647] + int i = x; // initialization + clang_analyzer_eval(-1 < i && i < 43); // expected-warning {{FALSE}} + } +} + +void test22(int x, unsigned u) { + if (x > -42) { + // Signed range can generate two unsigned ranges. + // Conversion: int[-41, 2147483647] -> unsigned[0, 2147483647]U[4294967255, 4294967295] + u = x; // assignment + clang_analyzer_eval(2147483647 < u && u < 4294967255); // expected-warning {{FALSE}} + } +}