Index: clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp =================================================================== --- clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp +++ clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp @@ -27,6 +27,7 @@ #include "llvm/Support/raw_ostream.h" #include #include +#include using namespace clang; using namespace ento; @@ -1228,6 +1229,8 @@ RangeSet VisitSymExpr(SymbolRef Sym) { if (Optional RS = getRangeForNegatedSym(Sym)) return *RS; + if (Optional RS = inferRangeForIntegralCast(State, Sym)) + return *RS; // If we've reached this line, the actual type of the symbolic // expression is not supported for advanced inference. // In this case, we simply backoff to the default "let's simply @@ -1296,6 +1299,10 @@ } RangeSet infer(SymbolRef Sym) { + AnalyzerOptions &Opts = State->getAnalysisManager().getAnalyzerOptions(); + if (Opts.ShouldSupportSymbolicIntegerCasts) + return Visit(Sym); + return intersect(RangeFactory, // Of course, we should take the constraint directly // associated with this symbol into consideration. @@ -1629,6 +1636,100 @@ return RangeSet(RangeFactory, Zero); } + Optional inferRangeForIntegralCast(ProgramStateRef State, + SymbolRef Sym) { + AnalyzerOptions &Opts = State->getAnalysisManager().getAnalyzerOptions(); + if (!Opts.ShouldSupportSymbolicIntegerCasts) + return None; + + using NestedCastTypes = SmallVector; + using ParsedSym = std::tuple< + NestedCastTypes, // Nested types of cast. E.g. (int)(char)(short x). + BitWidthType, // Minimal bitwidth. E.g. (int)(char)(short x) -> + // sizeof(char). + SymbolRef, // Root symbol. E.g. (int)(char)(short x) -> short x. + bool // Success flag. + >; + + auto ParseSym = [&](SymbolRef Sym) -> ParsedSym { + ASTContext &C = State->getStateManager().getContext(); + BitWidthType MinBitWidth = std::numeric_limits::max(); + NestedCastTypes Types; + do { + const QualType T = Sym->getType(); + // Check if we can handle the symbol. + if (!T->isIntegralOrEnumerationType()) + return {{}, {}, {}, false}; + // Find a minimal bitwidth. + const BitWidthType BitWidth = C.getIntWidth(T); + if (MinBitWidth > BitWidth) + MinBitWidth = BitWidth; + // Collect nested cast types. + Types.push_back(T); + // Traverse through cast symbols. + auto *SC = dyn_cast(Sym); + if (!SC) + break; + // Go to the root symbol. + Sym = SC->getOperand(); + } while (true); + return {std::move(Types), MinBitWidth, Sym, true}; + }; + + ParsedSym PS = ParseSym(Sym); + + if (!std::get<3>(PS)) // Success flag. + return None; + + auto DoSequenceCast = [&](RangeSet RS, + const NestedCastTypes &Types) -> RangeSet { + if (Types.size() == 1) + return RangeFactory.castTo(RS, Types.front()); + + auto TypesReversed = llvm::make_range(Types.rbegin(), Types.rend()); + for (const QualType T : TypesReversed) + RS = RangeFactory.castTo(RS, T); + + return RS; + }; + + auto findClosestRange = [&](SymbolRef Sym, + BitWidthType BW) -> const RangeSet * { + const ClassMap *CM = State->get(Sym); + if (!CM) + return nullptr; + EquivalenceClass EC; + bool Found = false; + if (const EquivalenceClass *ECPtr = CM->lookup(BW)) { + Found = true; + EC = *ECPtr; + } else { + BitWidthType ClosestBiggerBW = std::numeric_limits::max(); + for (ClassMap::value_type &V : *CM) { + if (V.first >= BW) { + if (V.first < ClosestBiggerBW) { + ClosestBiggerBW = V.first; + EC = V.second; + } + } + } + Found = (ClosestBiggerBW != std::numeric_limits::max()); + } + if (!Found) + return nullptr; + if (const RangeSet *RS = State->get(EC)) + return RS; + return nullptr; + }; + + if (const RangeSet *RS = findClosestRange(std::get<2>(PS), std::get<1>(PS))) + return DoSequenceCast(*RS, std::get<0>(PS)); + + RangeSet RS = infer(std::get<2>(PS)->getType()); + RS = DoSequenceCast(RS, std::get<0>(PS)); + return RS; + } + BasicValueFactory &ValueFactory; RangeSet::Factory &RangeFactory; ProgramStateRef State; @@ -2081,6 +2182,10 @@ /// Base method for handling new constraints for classes. [[nodiscard]] ProgramStateRef assign(EquivalenceClass Class, RangeSet NewConstraint) { + AnalyzerOptions &Opts = State->getAnalysisManager().getAnalyzerOptions(); + if (Opts.ShouldSupportSymbolicIntegerCasts) + if (!intersectWithExistingIntegralCastSymbol(State, Class, NewConstraint)) + return nullptr; // There is a chance that we might need to update constraints for the // classes that are known to be disequal to Class. // @@ -2116,6 +2221,37 @@ return setConstraint(State, Class, NewConstraint); } + bool intersectWithExistingIntegralCastSymbol(ProgramStateRef State, + EquivalenceClass Class, + RangeSet NewConstraint) { + std::map M; + SymbolSet SS = Class.getClassMembers(State); + BitWidthType BW = std::numeric_limits::max(); + for (SymbolRef S : SS) { + MinBitWidthAndSymbolRoot MBWSR = parseSymbolCast(State, S); + BW = std::min(BW, MBWSR.first); + if (const ClassMap *CM = State->get(MBWSR.second)) + for (const ClassMap::value_type &BWEC : *CM) { + auto it = M.find(BWEC.second); + if (it != M.end()) + it->second = std::min(BWEC.first, it->second); + else + M.emplace_hint(it, BWEC.second, BWEC.first); + } + } + + for (const auto &P : M) + if (const RangeSet *RSPtr = getConstraint(State, P.first)) { + const APSIntType Ty{std::min(BW, P.second), true}; + RangeSet RS1 = RangeFactory.castTo(*RSPtr, Ty); + RangeSet RS2 = RangeFactory.castTo(NewConstraint, Ty); + RS1 = RangeFactory.intersect(RS1, RS2); + if (RS1.isEmpty()) + return false; + } + return true; + } + ProgramStateRef trackDisequality(ProgramStateRef State, SymbolRef LHS, SymbolRef RHS) { return EquivalenceClass::markDisequal(RangeFactory, State, LHS, RHS); Index: clang/test/Analysis/svalbuilder-casts.cpp =================================================================== --- clang/test/Analysis/svalbuilder-casts.cpp +++ clang/test/Analysis/svalbuilder-casts.cpp @@ -39,17 +39,17 @@ // These are not truncated to short as zero. static_assert((short)1 != 0, ""); - clang_analyzer_eval(x == 1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(x == 1); // expected-warning{{FALSE}} static_assert((short)-1 != 0, ""); - clang_analyzer_eval(x == -1); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(x == -1); // expected-warning{{FALSE}} static_assert((short)65537 != 0, ""); - clang_analyzer_eval(x == 65537); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(x == 65537); // expected-warning{{FALSE}} static_assert((short)-65537 != 0, ""); - clang_analyzer_eval(x == -65537); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(x == -65537); // expected-warning{{FALSE}} static_assert((short)131073 != 0, ""); - clang_analyzer_eval(x == 131073); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(x == 131073); // expected-warning{{FALSE}} static_assert((short)-131073 != 0, ""); - clang_analyzer_eval(x == -131073); // expected-warning{{UNKNOWN}} + clang_analyzer_eval(x == -131073); // expected-warning{{FALSE}} // Check for implicit cast. short s = y; Index: clang/test/Analysis/symbol-integral-cast.cpp =================================================================== --- /dev/null +++ clang/test/Analysis/symbol-integral-cast.cpp @@ -0,0 +1,395 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=debug.ExprInspection -analyzer-config eagerly-assume=false -analyzer-config support-symbolic-integer-casts=true -verify %s + +template +void clang_analyzer_eval(T); +void clang_analyzer_warnIfReached(); +template +void clang_analyzer_value(T x); + +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_value((short)x); // expected-warning {{16s:0}} + clang_analyzer_value(x); // expected-warning {{32s:0}} + } else { + // FIXME: Shall be simplified to ConcreteInt 16s:0. + clang_analyzer_value((short)x); // expected-warning {{16s:{ [0, 0] }}} + clang_analyzer_value(x); // expected-warning {{32s:{ [-2147483648, -1], [1, 2147483647] }}} + } + } +} + +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_value(s); // expected-warning {{16s:0}} + clang_analyzer_value(x); // expected-warning {{32s:{ [-2147483648, 65536], [65538, 2147483647] }}} + } + } +} + +void test3(int x, short s) { + s = x; + if ((short)x > -10 && s < 10) { + if (x > 0 && x < 10) { + // FIXME: If the range of the whole variable was constrained then reason + // again about truncated bytes to make the ranges more precise. + // Shall be 16s:{ [1, 9] } + clang_analyzer_value((short)x); // expected-warning {{16s:{ [-9, 9] }}} + clang_analyzer_value(x); // expected-warning {{32s:{ [1, 9] }}} + } + } +} + +void test4(unsigned x) { + if ((char)x > 8) { + clang_analyzer_value((char)x); // expected-warning {{8s:{ [9, 127] }}} + if (x < 42) { + // FIXME: Update lower(less) bytes if higher(more) bytes are updated. + // Should be 8s:{ [9, 41] }. + clang_analyzer_value((char)x); // expected-warning {{8s:{ [9, 127] }}} + } + } +} + +void test5(unsigned x) { + if ((char)x > -10 && (char)x < 10) { + if ((short)x == 8) { + clang_analyzer_value(x); // expected-warning {{32u:{ [0, 4294967295] }}} + clang_analyzer_value((short)x); // expected-warning {{16s:{ [8, 8] }}} + // FIXME: Update lower(less) bytes if higher(more) bytes are updated. + // Should be 8s:{ [8, 8] }. + clang_analyzer_value((char)x); // expected-warning {{8s:{ [-9, 9] }}} + } + } +} + +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_value(x); // expected-warning {{32s:{ [1, 2147483647] }}} +} + +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_value(x); // expected-warning {{32s:{ [-2147483648, -1] }}} +} + +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_value(c); // expected-warning {{8s:{ [-68, 43] }}} + // Truncation on initializaion: int[5308, 5419] -> char[-68, 43] + char c1 = x; + clang_analyzer_value(c1); // expected-warning {{8s:{ [-68, 43] }}} + } + } +} + +void test13(int x) { + if (x > 913440767 && x < 913440769) { // 0x36720000 + // Truncation: int[913440768] -> short[0] + clang_analyzer_value((short)x); // expected-warning {{16s:0}} + short s = x; + clang_analyzer_value(s); // expected-warning {{16s:0}} + } +} + +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_value(x); // expected-warning {{32s:{ [-1569193983, 578290016] }}} + short s = x; + clang_analyzer_value(s); // expected-warning {{16s:{ [1, 32767] }}} + } + } +} + +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] + clang_analyzer_value(x); // expected-warning {{32s:{ [-1569193983, -1569193871] }}} + clang_analyzer_value((short)x); // expected-warning {{16s:{ [1, 113] }}} + } +} + +void test16(char x) { + if (x < 0) + clang_analyzer_value(x); // expected-warning {{8s:{ [-128, -1] }}} + else + clang_analyzer_value(x); // expected-warning {{8s:{ [0, 127] }}} +} + +void test17(char x) { + if (-11 <= x && x <= -10) { + unsigned u = x; + // Conversion: char[-11, -10] -> unsigned int[4294967285, 4294967286] + clang_analyzer_value(u); // expected-warning {{32u:{ [4294967285, 4294967286] }}} + unsigned short us = x; + // Conversion: char[-11, -10] -> unsigned short[65525, 65526] + clang_analyzer_value(us); // expected-warning {{16u:{ [65525, 65526] }}} + unsigned char uc = x; + // Conversion: char[-11, -10] -> unsigned char[245, 246] + clang_analyzer_value(uc); // expected-warning {{8u:{ [245, 246] }}} + } +} + +void test18(char c, short s, int i) { + // Any char value always is less then 1000. + int OneThousand = 1000; + c = i; + clang_analyzer_eval(c < OneThousand); // expected-warning {{TRUE}} + int MinusFourtyThousands = -40000; + s = i; + clang_analyzer_eval(s > MinusFourtyThousands); // expected-warning {{TRUE}} +} + +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_value(i); // expected-warning {{32s:{ [-2147483648, -1], [43, 2147483647] }}} + } +} + +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_value(u); // expected-warning {{32u:{ [0, 2147483647], [4294967255, 4294967295] }}} + } +} + +// PR51036 +void test23(signed char c) { + // Conversion: char[-128, 127] -> unsigned int[0, 127]U[4294967168, 4294967295] + if ((unsigned int)c <= 200) { + // [0, 127]U[4294967168, 4294967295] x [0, 200] = [0, 127] + clang_analyzer_value(c); // expected-warning {{8s:{ [0, 127] }}} + } +} + +void test24(int x, int y) { + if (x == y) { + short s = x; + if (!s) { + if (y == 65537) + // FIXME: `(short)y == 0` and `y == 65537` is infeasible. + // This should not warn. + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + else + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} + } + } +} + +void test25(int x, int y) { + if ((unsigned char)x == y) { + // FIXME: Should be 32s:{ [0, 255] } + clang_analyzer_value(y); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} + } +} + +void test26(int x, long long y, long z) { + // FIXME: Assign range at comparison expression. + + if (x == y) { + clang_analyzer_value(x); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} + // FIXME: Should be 64s:{ [-2147483648, 2147483647] } + clang_analyzer_value(y); // expected-warning {{64s:{ [-9223372036854775808, 9223372036854775807] }}} + if (y == (short)z) { + clang_analyzer_value((short)z); // expected-warning {{16s:{ [-32768, 32767] }}} + // FIXME: Should be 32s:{ [-32768, 32767] } + clang_analyzer_value(x); // expected-warning {{32s:{ [-2147483648, 2147483647] }}} + // FIXME: Should be 64s:{ [-32768, 32767] } + clang_analyzer_value(y); // expected-warning {{64s:{ [-9223372036854775808, 9223372036854775807] }}} + if (x > 0) { + // FIXME: Should be 16s:{ [1, 32767] } + clang_analyzer_value((short)z); // expected-warning {{16s:{ [-32768, 32767] }}} + // FIXME: Should be 32s:{ [1, 32767] } + clang_analyzer_value(x); // expected-warning {{32s:{ [1, 2147483647] }}} + // FIXME: Should be 64s:{ [1, 32767] } + clang_analyzer_value(y); // expected-warning {{64s:{ [1, 2147483647] }}} + if (y < 42) { + clang_analyzer_value((short)z); // expected-warning {{16s:{ [1, 41] }}} + clang_analyzer_value(x); // expected-warning {{32s:{ [1, 41] }}} + clang_analyzer_value(y); // expected-warning {{64s:{ [1, 41] }}} + } else if ((short)z < 42) { + // FIXME: Should be 16s:{ [1, 41] } + clang_analyzer_value((short)z); // expected-warning {{16s:{ [-32768, 41] }}} + // FIXME: Should be 32s:{ [1, 41] } + clang_analyzer_value(x); // expected-warning {{32s:{ [-32768, 41] }}} + // FIXME: Should be 64s:{ [1, 41] } + clang_analyzer_value(y); // expected-warning {{64s:{ [-32768, 41] }}} + } + } + } + } +}