diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp --- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -98,21 +98,19 @@ /// Special argument number for specifying the return value. static const ArgNo Ret; - using DescString = SmallString<96>; - - /// Returns the string representation of an argument index. + /// Get a string representation of an argument index. /// E.g.: (1) -> '1st arg', (2) - > '2nd arg' - static SmallString<8> getArgDesc(ArgNo); - /// Append textual description of a numeric range [RMin,RMax] to the string - /// 'Out'. + static void printArgDesc(ArgNo, llvm::raw_ostream &Out); + /// Append textual description of a numeric range [RMin,RMax] to + /// \p Out. static void appendInsideRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax, QualType ArgT, BasicValueFactory &BVF, - DescString &Out); - /// Append textual description of a numeric range out of [RMin,RMax] to the - /// string 'Out'. + llvm::raw_ostream &Out); + /// Append textual description of a numeric range out of [RMin,RMax] to + /// \p Out. static void appendOutOfRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax, QualType ArgT, BasicValueFactory &BVF, - DescString &Out); + llvm::raw_ostream &Out); class ValueConstraint; @@ -160,9 +158,9 @@ /// Give a description that explains the constraint to the user. Used when /// a bug is reported or when the constraint is applied and displayed as a /// note. - virtual std::string describe(DescriptionKind DK, const CallEvent &Call, - ProgramStateRef State, - const Summary &Summary) const { + virtual void describe(DescriptionKind DK, const CallEvent &Call, + ProgramStateRef State, const Summary &Summary, + llvm::raw_ostream &Out) const { // There are some descendant classes that are not used as argument // constraints, e.g. ComparisonConstraint. In that case we can safely // ignore the implementation of this function. @@ -170,6 +168,24 @@ "Description not implemented for summary case constraints"); } + /// This function is called when the current constraint represents the + /// opposite of a constraint that was not satisfied in a given state, but + /// the opposite is satisfied. In this case the available information in the + /// program state and this constraint can be used to get a more detailed + /// description about the original constraint violation. This can be get by + /// try to narrow the current constraint while it remains satisfied in the + /// given program state. If useful information is found it is put into + /// \p Out, but it is possible to produce no output. If anything is added to + /// \p Out it should look like " but is ...", the string is added to the bug + /// report about a constraint violation. + virtual void describeBug(const CallEvent &Call, ProgramStateRef State, + const Summary &Summary, + llvm::raw_ostream &Out) const { + if (auto N = getArgSVal(Call, getArgNo()).getAs()) + if (const llvm::APSInt *Int = N->getAsInteger()) + Out << " but is " << *Int; + } + /// Return those arguments that should be tracked when we report a bug about /// argument constraint violation. By default it is the argument that is /// constrained, however, in some special cases we need to track other @@ -249,9 +265,13 @@ const Summary &Summary, CheckerContext &C) const override; - std::string describe(DescriptionKind DK, const CallEvent &Call, - ProgramStateRef State, - const Summary &Summary) const override; + void describe(DescriptionKind DK, const CallEvent &Call, + ProgramStateRef State, const Summary &Summary, + llvm::raw_ostream &Out) const override; + + void describeBug(const CallEvent &Call, ProgramStateRef State, + const Summary &Summary, + llvm::raw_ostream &Out) const override; ValueConstraintPtr negate() const override { RangeConstraint Tmp(*this); @@ -272,15 +292,15 @@ using RangeApplyFunction = std::function; - /// @brief Apply a function on all intervals in the range. + /// Apply a function on all intervals in the range. void applyOnWithinRange( BasicValueFactory &BVF, QualType ArgT, const RangeApplyFunction &F) const; - /// @brief Apply a function on all intervals in the complement range. + /// Apply a function on all intervals in the complementary range. void applyOnOutOfRange( BasicValueFactory &BVF, QualType ArgT, const RangeApplyFunction &F) const; - /// @brief Apply a function on all intervals of the range or the complement + /// Apply a function on all intervals of the range or the complementary /// range. void applyOnRange(RangeKind Kind, BasicValueFactory &BVF, QualType ArgT, @@ -326,9 +346,9 @@ const Summary &Summary, CheckerContext &C) const override; - std::string describe(DescriptionKind DK, const CallEvent &Call, - ProgramStateRef State, - const Summary &Summary) const override; + void describe(DescriptionKind DK, const CallEvent &Call, + ProgramStateRef State, const Summary &Summary, + llvm::raw_ostream &Out) const override; ValueConstraintPtr negate() const override { NotNullConstraint Tmp(*this); @@ -380,9 +400,9 @@ const Summary &Summary, CheckerContext &C) const override; - std::string describe(DescriptionKind DK, const CallEvent &Call, - ProgramStateRef State, - const Summary &Summary) const override; + void describe(DescriptionKind DK, const CallEvent &Call, + ProgramStateRef State, const Summary &Summary, + llvm::raw_ostream &Out) const override; std::vector getArgsToTrack() const override { std::vector Result{ArgN}; @@ -757,8 +777,12 @@ return; assert(Call.getDecl() && "Function found in summary must have a declaration available"); - std::string Msg = VC->describe(ValueConstraint::DescriptionKind::Violation, - Call, C.getState(), Summary); + SmallString<256> Msg; + llvm::raw_svector_ostream MsgOs(Msg); + + VC->describe(ValueConstraint::DescriptionKind::Violation, Call, + C.getState(), Summary, MsgOs); + NegatedVC->describeBug(Call, N->getState(), Summary, MsgOs); Msg[0] = toupper(Msg[0]); if (!BT_InvalidArg) BT_InvalidArg = std::make_unique( @@ -800,55 +824,40 @@ } // end of anonymous namespace -SmallString<8> -StdLibraryFunctionsChecker::getArgDesc(StdLibraryFunctionsChecker::ArgNo ArgN) { - SmallString<8> Result; - Result += std::to_string(ArgN + 1); - Result += llvm::getOrdinalSuffix(ArgN + 1); - Result += " argument"; - return Result; +void StdLibraryFunctionsChecker::printArgDesc( + StdLibraryFunctionsChecker::ArgNo ArgN, llvm::raw_ostream &Out) { + Out << std::to_string(ArgN + 1); + Out << llvm::getOrdinalSuffix(ArgN + 1); + Out << " argument"; } void StdLibraryFunctionsChecker::appendInsideRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax, QualType ArgT, BasicValueFactory &BVF, - DescString &Out) { + llvm::raw_ostream &Out) { if (RMin.isZero() && RMax.isZero()) - Out.append("zero"); + Out << "zero"; else if (RMin == RMax) - RMin.toString(Out); + Out << RMin; else if (RMin == BVF.getMinValue(ArgT)) { if (RMax == -1) - Out.append("< 0"); - else { - Out.append("<= "); - RMax.toString(Out); - } + Out << "< 0"; + else + Out << "<= " << RMax; } else if (RMax == BVF.getMaxValue(ArgT)) { if (RMin.isOne()) - Out.append("> 0"); - else { - Out.append(">= "); - RMin.toString(Out); - } + Out << "> 0"; + else + Out << ">= " << RMin; } else if (RMin.isNegative() == RMax.isNegative() && RMin.getLimitedValue() == RMax.getLimitedValue() - 1) { - RMin.toString(Out); - Out.append(" or "); - RMax.toString(Out); + Out << RMin << " or " << RMax; } else if (RMin.isNegative() == RMax.isNegative() && RMin.getLimitedValue() == RMax.getLimitedValue() - 2) { - RMin.toString(Out); - Out.append(", "); - (RMin + 1).toString(Out, 10, RMin.isSigned()); - Out.append(" or "); - RMax.toString(Out); + Out << RMin << ", " << (RMin + 1) << " or " << RMax; } else { - Out.append("between "); - RMin.toString(Out); - Out.append(" and "); - RMax.toString(Out); + Out << "between " << RMin << " and " << RMax; } } @@ -856,37 +865,26 @@ llvm::APSInt RMax, QualType ArgT, BasicValueFactory &BVF, - DescString &Out) { + llvm::raw_ostream &Out) { if (RMin.isZero() && RMax.isZero()) - Out.append("nonzero"); + Out << "nonzero"; else if (RMin == RMax) { - Out.append("not equal to "); - RMin.toString(Out); + Out << "not equal to " << RMin; } else if (RMin == BVF.getMinValue(ArgT)) { if (RMax == -1) - Out.append(">= 0"); - else { - Out.append("> "); - RMax.toString(Out); - } + Out << ">= 0"; + else + Out << "> " << RMax; } else if (RMax == BVF.getMaxValue(ArgT)) { if (RMin.isOne()) - Out.append("<= 0"); - else { - Out.append("< "); - RMin.toString(Out); - } + Out << "<= 0"; + else + Out << "< " << RMin; } else if (RMin.isNegative() == RMax.isNegative() && RMin.getLimitedValue() == RMax.getLimitedValue() - 1) { - Out.append("not "); - RMin.toString(Out); - Out.append(" and not "); - RMax.toString(Out); + Out << "not " << RMin << " and not " << RMax; } else { - Out.append("not between "); - RMin.toString(Out); - Out.append(" and "); - RMax.toString(Out); + Out << "not between " << RMin << " and " << RMax; } } @@ -968,42 +966,77 @@ return State; } -std::string StdLibraryFunctionsChecker::RangeConstraint::describe( +void StdLibraryFunctionsChecker::RangeConstraint::describe( DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, - const Summary &Summary) const { + const Summary &Summary, llvm::raw_ostream &Out) const { BasicValueFactory &BVF = getBVF(State); QualType T = Summary.getArgType(getArgNo()); - DescString Result; const auto Violation = ValueConstraint::DescriptionKind::Violation; - Result += "the "; - Result += getArgDesc(ArgN); - Result += " to '"; - Result += getFunctionName(Call); - Result += DK == Violation ? "' should be " : "' is "; + Out << "the "; + printArgDesc(ArgN, Out); + Out << " to '"; + Out << getFunctionName(Call); + Out << ((DK == Violation) ? "' should be " : "' is "); if (!Description.empty()) { - Result += Description; + Out << Description; } else { unsigned I = Ranges.size(); if (Kind == WithinRange) { for (const std::pair &R : Ranges) { appendInsideRangeDesc(BVF.getValue(R.first, T), - BVF.getValue(R.second, T), T, BVF, Result); + BVF.getValue(R.second, T), T, BVF, Out); if (--I > 0) - Result += " or "; + Out << " or "; } } else { for (const std::pair &R : Ranges) { appendOutOfRangeDesc(BVF.getValue(R.first, T), - BVF.getValue(R.second, T), T, BVF, Result); + BVF.getValue(R.second, T), T, BVF, Out); if (--I > 0) - Result += " and "; + Out << " and "; } } } +} + +void StdLibraryFunctionsChecker::RangeConstraint::describeBug( + const CallEvent &Call, ProgramStateRef State, const Summary &Summary, + llvm::raw_ostream &Out) const { + unsigned int NRanges = 0; + bool HaveAllRanges = true; + + ProgramStateManager &Mgr = State->getStateManager(); + BasicValueFactory &BVF = Mgr.getSValBuilder().getBasicValueFactory(); + ConstraintManager &CM = Mgr.getConstraintManager(); + SVal V = getArgSVal(Call, getArgNo()); - return Result.c_str(); + if (auto N = V.getAs()) { + if (const llvm::APSInt *Int = N->getAsInteger()) { + Out << " but is " << *Int; + return; + } + QualType T = Summary.getArgType(getArgNo()); + SmallString<128> MoreInfo; + llvm::raw_svector_ostream MoreInfoOs(MoreInfo); + auto ApplyF = [&](const llvm::APSInt &Min, const llvm::APSInt &Max) { + if (CM.assumeInclusiveRange(State, *N, Min, Max, true)) { + if (NRanges > 0) + MoreInfoOs << " or "; + appendInsideRangeDesc(Min, Max, T, BVF, MoreInfoOs); + ++NRanges; + } else { + HaveAllRanges = false; + } + return true; + }; + + applyOnRange(Kind, BVF, T, ApplyF); + assert(NRanges > 0); + if (!HaveAllRanges || NRanges == 1) + Out << " but is " << MoreInfo; + } } ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply( @@ -1042,17 +1075,15 @@ return State->assume(L, CannotBeNull); } -std::string StdLibraryFunctionsChecker::NotNullConstraint::describe( +void StdLibraryFunctionsChecker::NotNullConstraint::describe( DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, - const Summary &Summary) const { - SmallString<48> Result; + const Summary &Summary, llvm::raw_ostream &Out) const { const auto Violation = ValueConstraint::DescriptionKind::Violation; - Result += "the "; - Result += getArgDesc(ArgN); - Result += " to '"; - Result += getFunctionName(Call); - Result += DK == Violation ? "' should not be NULL" : "' is not NULL"; - return Result.c_str(); + Out << "the "; + printArgDesc(ArgN, Out); + Out << " to '"; + Out << getFunctionName(Call); + Out << (DK == Violation ? "' should not be NULL" : "' is not NULL"); } ProgramStateRef StdLibraryFunctionsChecker::BufferSizeConstraint::apply(ProgramStateRef State, const CallEvent &Call, @@ -1097,28 +1128,26 @@ llvm_unreachable("Size argument or the dynamic size is Undefined"); } -std::string StdLibraryFunctionsChecker::BufferSizeConstraint::describe( +void StdLibraryFunctionsChecker::BufferSizeConstraint::describe( DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, - const Summary &Summary) const { - SmallString<96> Result; + const Summary &Summary, llvm::raw_ostream &Out) const { const auto Violation = ValueConstraint::DescriptionKind::Violation; - Result += "the size of the "; - Result += getArgDesc(ArgN); - Result += " to '"; - Result += getFunctionName(Call); - Result += DK == Violation ? "' should be " : "' is "; - Result += "equal to or greater than "; + Out << "the size of the "; + printArgDesc(ArgN, Out); + Out << " to '"; + Out << getFunctionName(Call); + Out << (DK == Violation ? "' should be " : "' is "); + Out << "equal to or greater than "; if (ConcreteSize) { - ConcreteSize->toString(Result); + Out << *ConcreteSize; } else if (SizeArgN) { - Result += "the value of the "; - Result += getArgDesc(*SizeArgN); + Out << "the value of the "; + printArgDesc(*SizeArgN, Out); if (SizeMultiplierArgN) { - Result += " times the "; - Result += getArgDesc(*SizeMultiplierArgN); + Out << " times the "; + printArgDesc(*SizeMultiplierArgN, Out); } } - return Result.c_str(); } void StdLibraryFunctionsChecker::checkPreCall(const CallEvent &Call, @@ -1150,10 +1179,11 @@ assert(SuccessSt); NewState = SuccessSt; if (NewState != State) { - SmallString<64> Msg; - Msg += "Assuming "; - Msg += Constraint->describe(ValueConstraint::DescriptionKind::Assumption, - Call, NewState, Summary); + SmallString<128> Msg; + llvm::raw_svector_ostream Os(Msg); + Os << "Assuming "; + Constraint->describe(ValueConstraint::DescriptionKind::Assumption, Call, + NewState, Summary, Os); const auto ArgSVal = Call.getArgSVal(Constraint->getArgNo()); NewNode = C.addTransition( NewState, NewNode, @@ -3457,6 +3487,27 @@ ErrnoIrrelevant, "Function returns 0") .Case({ReturnValueCondition(WithinRange, SingleValue(1))}, ErrnoIrrelevant, "Function returns 1")); + addToFunctionSummaryMap( + "__test_case_range_1_2__4_6", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .Case({ArgumentCondition(0U, WithinRange, + IntRangeVector{{IntMin, 0}, {3, 3}}), + ReturnValueCondition(WithinRange, SingleValue(1))}, + ErrnoIrrelevant) + .Case({ArgumentCondition(0U, WithinRange, + IntRangeVector{{3, 3}, {7, IntMax}}), + ReturnValueCondition(WithinRange, SingleValue(2))}, + ErrnoIrrelevant) + .Case({ArgumentCondition(0U, WithinRange, + IntRangeVector{{IntMin, 0}, {7, IntMax}}), + ReturnValueCondition(WithinRange, SingleValue(3))}, + ErrnoIrrelevant) + .Case({ArgumentCondition( + 0U, WithinRange, + IntRangeVector{{IntMin, 0}, {3, 3}, {7, IntMax}}), + ReturnValueCondition(WithinRange, SingleValue(4))}, + ErrnoIrrelevant)); } SummariesInitialized = true; diff --git a/clang/test/Analysis/std-c-library-functions-arg-constraints-notes.cpp b/clang/test/Analysis/std-c-library-functions-arg-constraints-notes.cpp --- a/clang/test/Analysis/std-c-library-functions-arg-constraints-notes.cpp +++ b/clang/test/Analysis/std-c-library-functions-arg-constraints-notes.cpp @@ -78,34 +78,36 @@ int __range_out_1_2__4_6(int); // [1, 2], [4, 6] int __range_out_1_2__4_inf(int); // [1, 2], [4, inf] +int __test_case_range_1_2__4_6(int); + void test_range_values(int x) { switch (x) { case 0: - __single_val_0(1); // expected-warning{{should be zero}} + __single_val_0(1); // expected-warning{{should be zero but is 1}} break; case 1: - __single_val_1(2); // expected-warning{{should be 1}} + __single_val_1(2); // expected-warning{{should be 1 but is 2}} break; case 2: - __range_1_2(3); // expected-warning{{should be 1 or 2}} + __range_1_2(3); // expected-warning{{should be 1 or 2 but is 3}} break; case 3: - __range_m1_1(3); // expected-warning{{should be between -1 and 1}} + __range_m1_1(3); // expected-warning{{should be between -1 and 1 but is 3}} break; case 4: - __range_m2_m1(1); // expected-warning{{should be -2 or -1}} + __range_m2_m1(1); // expected-warning{{should be -2 or -1 but is 1}} break; case 5: - __range_m10_10(11); // expected-warning{{should be between -10 and 10}} + __range_m10_10(11); // expected-warning{{should be between -10 and 10 but is 11}} break; case 6: - __range_m10_10(-11); // expected-warning{{should be between -10 and 10}} + __range_m10_10(-11); // expected-warning{{should be between -10 and 10 but is -11}} break; case 7: - __range_1_2__4_6(3); // expected-warning{{should be 1 or 2 or 4, 5 or 6}} + __range_1_2__4_6(3); // expected-warning{{should be 1 or 2 or 4, 5 or 6 but is 3}} break; case 8: - __range_1_2__4_inf(3); // expected-warning{{should be 1 or 2 or >= 4}} + __range_1_2__4_inf(3); // expected-warning{{should be 1 or 2 or >= 4 but is 3}} break; } } @@ -113,22 +115,22 @@ void test_range_values_inf(int x) { switch (x) { case 1: - __range_m1_inf(-2); // expected-warning{{should be >= -1}} + __range_m1_inf(-2); // expected-warning{{should be >= -1 but is -2}} break; case 2: - __range_0_inf(-1); // expected-warning{{should be >= 0}} + __range_0_inf(-1); // expected-warning{{should be >= 0 but is -1}} break; case 3: - __range_1_inf(0); // expected-warning{{should be > 0}} + __range_1_inf(0); // expected-warning{{should be > 0 but is 0}} break; case 4: - __range_minf_m1(0); // expected-warning{{should be < 0}} + __range_minf_m1(0); // expected-warning{{should be < 0 but is 0}} break; case 5: - __range_minf_0(1); // expected-warning{{should be <= 0}} + __range_minf_0(1); // expected-warning{{should be <= 0 but is 1}} break; case 6: - __range_minf_1(2); // expected-warning{{should be <= 1}} + __range_minf_1(2); // expected-warning{{should be <= 1 but is 2}} break; } } @@ -136,28 +138,28 @@ void test_range_values_out(int x) { switch (x) { case 0: - __single_val_out_0(0); // expected-warning{{should be nonzero}} + __single_val_out_0(0); // expected-warning{{should be nonzero but is 0}} break; case 1: - __single_val_out_1(1); // expected-warning{{should be not equal to 1}} + __single_val_out_1(1); // expected-warning{{should be not equal to 1 but is 1}} break; case 2: - __range_out_1_2(2); // expected-warning{{should be not 1 and not 2}} + __range_out_1_2(2); // expected-warning{{should be not 1 and not 2 but is 2}} break; case 3: - __range_out_m1_1(-1); // expected-warning{{should be not between -1 and 1}} + __range_out_m1_1(-1); // expected-warning{{should be not between -1 and 1 but is -1}} break; case 4: - __range_out_m2_m1(-2); // expected-warning{{should be not -2 and not -1}} + __range_out_m2_m1(-2); // expected-warning{{should be not -2 and not -1 but is -2}} break; case 5: - __range_out_m10_10(0); // expected-warning{{should be not between -10 and 10}} + __range_out_m10_10(0); // expected-warning{{should be not between -10 and 10 but is 0}} break; case 6: - __range_out_1_2__4_6(1); // expected-warning{{should be not 1 and not 2 and not between 4 and 6}} + __range_out_1_2__4_6(1); // expected-warning{{should be not 1 and not 2 and not between 4 and 6 but is 1}} break; case 7: - __range_out_1_2__4_inf(4); // expected-warning{{should be not 1 and not 2 and < 4}} + __range_out_1_2__4_inf(4); // expected-warning{{should be not 1 and not 2 and < 4 but is 4}} break; } } @@ -165,22 +167,71 @@ void test_range_values_out_inf(int x) { switch (x) { case 1: - __range_out_minf_m1(-1); // expected-warning{{should be >= 0}} + __range_out_minf_m1(-1); // expected-warning{{should be >= 0 but is -1}} break; case 2: - __range_out_minf_0(0); // expected-warning{{should be > 0}} + __range_out_minf_0(0); // expected-warning{{should be > 0 but is 0}} break; case 3: - __range_out_minf_1(1); // expected-warning{{should be > 1}} + __range_out_minf_1(1); // expected-warning{{should be > 1 but is 1}} break; case 4: - __range_out_m1_inf(-1); // expected-warning{{should be < -1}} + __range_out_m1_inf(-1); // expected-warning{{should be < -1 but is -1}} break; case 5: - __range_out_0_inf(0); // expected-warning{{should be < 0}} + __range_out_0_inf(0); // expected-warning{{should be < 0 but is 0}} break; case 6: - __range_out_1_inf(1); // expected-warning{{should be <= 0}} + __range_out_1_inf(1); // expected-warning{{should be <= 0 but is 1}} break; } } + +void test_explanation(int x, int y) { + switch (y) { + case 1: + if (x > 0) + __single_val_0(x); // expected-warning{{should be zero but is > 0 [}} + return; + case 2: + if (x < 0) + __single_val_0(x); // expected-warning{{should be zero but is < 0 [}} + return; + case 3: + if (x < -1) + __single_val_0(x); // expected-warning{{should be zero but is < 0 [}} + return; + case 4: + if (x != 0) + __single_val_0(x); // expected-warning{{should be zero [}} + return; + case 5: + if (x == 3) + __range_1_2__4_6(x); // expected-warning{{should be 1 or 2 or 4, 5 or 6 but is 3 [}} + return; + case 6: + if (x > 6) + __range_1_2__4_6(x); // expected-warning{{should be 1 or 2 or 4, 5 or 6 but is >= 7 [}} + return; + case 7: + if (x < 1) + __range_1_2__4_6(x); // expected-warning{{should be 1 or 2 or 4, 5 or 6 but is <= 0 [}} + return; + case 8: + if (__test_case_range_1_2__4_6(x) == 1) + __range_1_2__4_6(x); // expected-warning{{should be 1 or 2 or 4, 5 or 6 but is 3 or <= 0 [}} + return; + case 9: + if (__test_case_range_1_2__4_6(x) == 2) + __range_1_2__4_6(x); // expected-warning{{should be 1 or 2 or 4, 5 or 6 but is 3 or >= 7 [}} + return; + case 10: + if (__test_case_range_1_2__4_6(x) == 3) + __range_1_2__4_6(x); // expected-warning{{should be 1 or 2 or 4, 5 or 6 but is <= 0 or >= 7 [}} + return; + case 11: + if (__test_case_range_1_2__4_6(x) == 4) + __range_1_2__4_6(x); // expected-warning{{should be 1 or 2 or 4, 5 or 6 [}} + return; + } +}