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 @@ -102,21 +102,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 + 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 \p 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; @@ -152,21 +150,25 @@ /// Represents that in which context do we require a description of the /// constraint. - enum class DescriptionKind { - /// The constraint is violated. + enum DescriptionKind { + /// Describe a constraint that was violated. + /// Description should start with something like "should be". Violation, - /// We assume that the constraint is satisfied. + /// Describe a constraint that was assumed to be true. /// This can be used when a precondition is satisfied, or when a summary /// case is applied. + /// Description should start with something like "is". Assumption }; /// 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 { + /// note. The description should not mention the argument (getArgNo). + /// See StdLibraryFunctionsChecker::reportBug about how this function is + /// used (this function is used not only there). + 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. @@ -174,6 +176,31 @@ "Description not implemented for summary case constraints"); } + /// Give a description that explains the actual argument value (where the + /// current ValueConstraint applies to) to the user. This function should be + /// called only when the current constraint is satisfied by the argument. + /// It should produce a more precise description than the constraint itself. + /// The actual value of the argument and the program state can be used to + /// make the description more precise. In the most simple case, if the + /// argument has a fixed known value this value can be printed into \p Out, + /// this is done by default. + /// The function should return true if a description was printed to \p Out, + /// otherwise false. + /// See StdLibraryFunctionsChecker::reportBug about how this function is + /// used. + virtual bool describeArgumentValue(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 << *Int; + return true; + } + } + return false; + } + /// 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 @@ -254,9 +281,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; + + bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State, + const Summary &Summary, + llvm::raw_ostream &Out) const override; ValueConstraintPtr negate() const override { RangeConstraint Tmp(*this); @@ -342,9 +373,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; + + bool describeArgumentValue(const CallEvent &Call, ProgramStateRef State, + const Summary &Summary, + llvm::raw_ostream &Out) const override; ValueConstraintPtr negate() const override { NotNullConstraint Tmp(*this); @@ -396,9 +431,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}; @@ -773,8 +808,20 @@ 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); + + MsgOs << "The "; + printArgDesc(VC->getArgNo(), MsgOs); + MsgOs << " to '" << getFunctionName(Call) << "' "; + bool ValuesPrinted = + NegatedVC->describeArgumentValue(Call, N->getState(), Summary, MsgOs); + if (ValuesPrinted) + MsgOs << " but "; + else + MsgOs << "is out of the accepted range; It "; + VC->describe(ValueConstraint::Violation, Call, C.getState(), Summary, + MsgOs); Msg[0] = toupper(Msg[0]); if (!BT_InvalidArg) BT_InvalidArg = std::make_unique( @@ -816,55 +863,37 @@ } // 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); - } 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 << " or " << RMax; } else { - Out.append("between "); - RMin.toString(Out); - Out.append(" and "); - RMax.toString(Out); + Out << "between " << RMin << " and " << RMax; } } @@ -872,37 +901,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; } } @@ -981,42 +999,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 << ((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 "; } } } +} + +bool StdLibraryFunctionsChecker::RangeConstraint::describeArgumentValue( + 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 << "is "; + Out << *Int; + return true; + } + 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 << "is "; + Out << MoreInfo; + return true; + } + } + return false; } ProgramStateRef StdLibraryFunctionsChecker::ComparisonConstraint::apply( @@ -1055,17 +1108,23 @@ 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 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(); + const Summary &Summary, llvm::raw_ostream &Out) const { + assert(CannotBeNull && + "Describe should not be used when the value must be NULL"); + if (DK == Violation) + Out << "should not be NULL"; + else + Out << "is not NULL"; +} + +bool StdLibraryFunctionsChecker::NotNullConstraint::describeArgumentValue( + const CallEvent &Call, ProgramStateRef State, const Summary &Summary, + llvm::raw_ostream &Out) const { + assert(!CannotBeNull && "This function is used when the value is NULL"); + Out << "is NULL"; + return true; } ProgramStateRef StdLibraryFunctionsChecker::BufferSizeConstraint::apply( @@ -1110,28 +1169,21 @@ 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 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 "; + const Summary &Summary, llvm::raw_ostream &Out) const { + Out << ((DK == Violation) ? "should be " : "is "); + Out << "a buffer with size 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, @@ -1164,10 +1216,15 @@ 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 that the "; + printArgDesc(Constraint->getArgNo(), Os); + Os << " to '"; + Os << getFunctionName(Call); + Os << "' "; + Constraint->describe(ValueConstraint::Assumption, Call, NewState, Summary, + Os); const auto ArgSVal = Call.getArgSVal(Constraint->getArgNo()); NewNode = C.addTransition( NewState, NewNode, @@ -3471,6 +3528,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-note-tags.cpp b/clang/test/Analysis/std-c-library-functions-arg-constraints-note-tags.cpp --- a/clang/test/Analysis/std-c-library-functions-arg-constraints-note-tags.cpp +++ b/clang/test/Analysis/std-c-library-functions-arg-constraints-note-tags.cpp @@ -19,7 +19,7 @@ // Check NotNullConstraint assumption notes. int __not_null(int *); int test_not_null_note(int *x, int y) { - __not_null(x); // expected-note{{Assuming the 1st argument to '__not_null' is not NULL}} + __not_null(x); // expected-note{{Assuming that the 1st argument to '__not_null' is not NULL}} if (x) // expected-note{{'x' is non-null}} \ // expected-note{{Taking true branch}} if (!y) // expected-note{{Assuming 'y' is 0}} \ @@ -33,7 +33,7 @@ // Check the RangeConstraint assumption notes. int __single_val_0(int); // [0, 0] int test_range_constraint_note(int x, int y) { - __single_val_0(x); // expected-note{{Assuming the 1st argument to '__single_val_0' is zero}} + __single_val_0(x); // expected-note{{Assuming that the 1st argument to '__single_val_0' is zero}} return y / x; // expected-warning{{Division by zero}} \ // expected-note{{Division by zero}} } @@ -41,7 +41,7 @@ // Check the BufferSizeConstraint assumption notes. int __buf_size_arg_constraint_concrete(const void *buf); // size of buf must be >= 10 void test_buffer_size_note(char *buf, int y) { - __buf_size_arg_constraint_concrete(buf); // expected-note {{Assuming the size of the 1st argument to '__buf_size_arg_constraint_concrete' is equal to or greater than 10}} + __buf_size_arg_constraint_concrete(buf); // expected-note {{Assuming that the 1st argument to '__buf_size_arg_constraint_concrete' is a buffer with size equal to or greater than 10}} clang_analyzer_eval(clang_analyzer_getExtent(buf) >= 10); // expected-warning{{TRUE}} \ // expected-note{{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 @@ -15,7 +15,7 @@ int __not_null(int *); void test_not_null(int *x) { __not_null(nullptr); // \ - // expected-warning{{The 1st argument to '__not_null' should not be NULL}} + // expected-warning{{The 1st argument to '__not_null' is NULL but should not be NULL [}} } // Check the BufferSizeConstraint violation notes. @@ -28,19 +28,19 @@ case 1: { char buf[9]; __buf_size_arg_constraint_concrete(buf); // \ - // expected-warning{{The size of the 1st argument to '__buf_size_arg_constraint_concrete' should be equal to or greater than 10}} + // expected-warning{{The 1st argument to '__buf_size_arg_constraint_concrete' is out of the accepted range; It should be a buffer with size equal to or greater than 10 [}} break; } case 2: { char buf[3]; __buf_size_arg_constraint(buf, 4); // \ - // expected-warning{{The size of the 1st argument to '__buf_size_arg_constraint' should be equal to or greater than the value of the 2nd argument}} + // expected-warning{{The 1st argument to '__buf_size_arg_constraint' is out of the accepted range; It should be a buffer with size equal to or greater than the value of the 2nd argument}} break; } case 3: { char buf[3]; __buf_size_arg_constraint_mul(buf, 4, 2); // \ - // expected-warning{{The size of the 1st argument to '__buf_size_arg_constraint_mul' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} + // expected-warning{{The 1st argument to '__buf_size_arg_constraint_mul' is out of the accepted range; It should be a buffer with size equal to or greater than the value of the 2nd argument times the 3rd argument}} break; } } @@ -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{{is 1 but should be zero}} break; case 1: - __single_val_1(2); // expected-warning{{should be 1}} + __single_val_1(2); // expected-warning{{is 2 but should be 1}} break; case 2: - __range_1_2(3); // expected-warning{{should be 1 or 2}} + __range_1_2(3); // expected-warning{{is 3 but should be 1 or 2}} break; case 3: - __range_m1_1(3); // expected-warning{{should be between -1 and 1}} + __range_m1_1(3); // expected-warning{{is 3 but should be between -1 and 1}} break; case 4: - __range_m2_m1(1); // expected-warning{{should be -2 or -1}} + __range_m2_m1(1); // expected-warning{{is 1 but should be -2 or -1}} break; case 5: - __range_m10_10(11); // expected-warning{{should be between -10 and 10}} + __range_m10_10(11); // expected-warning{{is 11 but should be between -10 and 10}} break; case 6: - __range_m10_10(-11); // expected-warning{{should be between -10 and 10}} + __range_m10_10(-11); // expected-warning{{is -11 but should be between -10 and 10}} 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{{is 3 but should be 1 or 2 or between 4 and 6}} 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{{is 3 but should be 1 or 2 or >= 4}} 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{{is -2 but should be >= -1}} break; case 2: - __range_0_inf(-1); // expected-warning{{should be >= 0}} + __range_0_inf(-1); // expected-warning{{is -1 but should be >= 0}} break; case 3: - __range_1_inf(0); // expected-warning{{should be > 0}} + __range_1_inf(0); // expected-warning{{is 0 but should be > 0}} break; case 4: - __range_minf_m1(0); // expected-warning{{should be < 0}} + __range_minf_m1(0); // expected-warning{{is 0 but should be < 0}} break; case 5: - __range_minf_0(1); // expected-warning{{should be <= 0}} + __range_minf_0(1); // expected-warning{{is 1 but should be <= 0}} break; case 6: - __range_minf_1(2); // expected-warning{{should be <= 1}} + __range_minf_1(2); // expected-warning{{is 2 but should be <= 1}} 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{{is 0 but should be nonzero}} break; case 1: - __single_val_out_1(1); // expected-warning{{should be not equal to 1}} + __single_val_out_1(1); // expected-warning{{is 1 but should be not equal to 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{{is 2 but should be not 1 and not 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{{is -1 but should be not between -1 and 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{{is -2 but should be not -2 and not -1}} break; case 5: - __range_out_m10_10(0); // expected-warning{{should be not between -10 and 10}} + __range_out_m10_10(0); // expected-warning{{is 0 but should be not between -10 and 10}} 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{{is 1 but should be not 1 and not 2 and not between 4 and 6}} 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{{is 4 but should be not 1 and not 2 and < 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{{is -1 but should be >= 0}} break; case 2: - __range_out_minf_0(0); // expected-warning{{should be > 0}} + __range_out_minf_0(0); // expected-warning{{is 0 but should be > 0}} break; case 3: - __range_out_minf_1(1); // expected-warning{{should be > 1}} + __range_out_minf_1(1); // expected-warning{{is 1 but should be > 1}} break; case 4: - __range_out_m1_inf(-1); // expected-warning{{should be < -1}} + __range_out_m1_inf(-1); // expected-warning{{is -1 but should be < -1}} break; case 5: - __range_out_0_inf(0); // expected-warning{{should be < 0}} + __range_out_0_inf(0); // expected-warning{{is 0 but should be < 0}} break; case 6: - __range_out_1_inf(1); // expected-warning{{should be <= 0}} + __range_out_1_inf(1); // expected-warning{{is 1 but should be <= 0}} break; } } + +void test_explanation(int x, int y) { + switch (y) { + case 1: + if (x > 0) + __single_val_0(x); // expected-warning{{is > 0 but should be zero [}} + return; + case 2: + if (x < 0) + __single_val_0(x); // expected-warning{{is < 0 but should be zero [}} + return; + case 3: + if (x < -1) + __single_val_0(x); // expected-warning{{is < 0 but should be zero [}} + return; + case 4: + if (x != 0) + __single_val_0(x); // expected-warning{{is out of the accepted range; It should be zero [}} + return; + case 5: + if (x == 3) + __range_1_2__4_6(x); // expected-warning{{is 3 but should be 1 or 2 or between 4 and 6 [}} + return; + case 6: + if (x > 6) + __range_1_2__4_6(x); // expected-warning{{is >= 7 but should be 1 or 2 or between 4 and 6 [}} + return; + case 7: + if (x < 1) + __range_1_2__4_6(x); // expected-warning{{is <= 0 but should be 1 or 2 or between 4 and 6 [}} + return; + case 8: + if (__test_case_range_1_2__4_6(x) == 1) + __range_1_2__4_6(x); // expected-warning{{is 3 or <= 0 but should be 1 or 2 or between 4 and 6 [}} + return; + case 9: + if (__test_case_range_1_2__4_6(x) == 2) + __range_1_2__4_6(x); // expected-warning{{is 3 or >= 7 but should be 1 or 2 or between 4 and 6 [}} + return; + case 10: + if (__test_case_range_1_2__4_6(x) == 3) + __range_1_2__4_6(x); // expected-warning{{is <= 0 or >= 7 but should be 1 or 2 or between 4 and 6 [}} + return; + case 11: + if (__test_case_range_1_2__4_6(x) == 4) + __range_1_2__4_6(x); // expected-warning{{is out of the accepted range; It should be 1 or 2 or between 4 and 6 [}} + return; + } +} diff --git a/clang/test/Analysis/std-c-library-functions-arg-constraints-tracking-notes.c b/clang/test/Analysis/std-c-library-functions-arg-constraints-tracking-notes.c --- a/clang/test/Analysis/std-c-library-functions-arg-constraints-tracking-notes.c +++ b/clang/test/Analysis/std-c-library-functions-arg-constraints-tracking-notes.c @@ -16,8 +16,8 @@ char buf[3]; // bugpath-note{{'buf' initialized here}} int s = 4; // bugpath-note{{'s' initialized to 4}} __buf_size_arg_constraint(buf, s); // \ - // bugpath-warning{{The size of the 1st argument to '__buf_size_arg_constraint' should be equal to or greater than the value of the 2nd argument}} \ - // bugpath-note{{The size of the 1st argument to '__buf_size_arg_constraint' should be equal to or greater than the value of the 2nd argument}} + // bugpath-warning{{The 1st argument to '__buf_size_arg_constraint' is out of the accepted range; It should be a buffer with size equal to or greater than the value of the 2nd argument}} \ + // bugpath-note{{The 1st argument to '__buf_size_arg_constraint' is out of the accepted range; It should be a buffer with size equal to or greater than the value of the 2nd argument}} } int __buf_size_arg_constraint_mul(const void *, size_t, size_t); @@ -26,6 +26,6 @@ int s1 = 4; // bugpath-note{{'s1' initialized to 4}} int s2 = sizeof(short); // bugpath-note{{'s2' initialized to}} __buf_size_arg_constraint_mul(buf, s1, s2); // \ - // bugpath-warning{{The size of the 1st argument to '__buf_size_arg_constraint_mul' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} \ - // bugpath-note{{The size of the 1st argument to '__buf_size_arg_constraint_mul' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} + // bugpath-warning{{The 1st argument to '__buf_size_arg_constraint_mul' is out of the accepted range; It should be a buffer with size equal to or greater than the value of the 2nd argument times the 3rd argument}} \ + // bugpath-note{{The 1st argument to '__buf_size_arg_constraint_mul' is out of the accepted range; It should be a buffer with size equal to or greater than the value of the 2nd argument times the 3rd argument}} } diff --git a/clang/test/Analysis/std-c-library-functions-arg-constraints.c b/clang/test/Analysis/std-c-library-functions-arg-constraints.c --- a/clang/test/Analysis/std-c-library-functions-arg-constraints.c +++ b/clang/test/Analysis/std-c-library-functions-arg-constraints.c @@ -30,9 +30,9 @@ void test_alnum_concrete(int v) { int ret = isalnum(256); // \ - // report-warning{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}} \ - // bugpath-warning{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}} \ - // bugpath-note{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}} + // report-warning{{The 1st argument to 'isalnum' is 256 but should be an unsigned char value or EOF}} \ + // bugpath-warning{{The 1st argument to 'isalnum' is 256 but should be an unsigned char value or EOF}} \ + // bugpath-note{{The 1st argument to 'isalnum' is 256 but should be an unsigned char value or EOF}} (void)ret; } @@ -55,9 +55,9 @@ // bugpath-note{{Taking true branch}} int ret = isalnum(x); // \ - // report-warning{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}} \ - // bugpath-warning{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}} \ - // bugpath-note{{The 1st argument to 'isalnum' should be an unsigned char value or EOF}} + // report-warning{{The 1st argument to 'isalnum' is >= 256 but should be an unsigned char value or EOF}} \ + // bugpath-warning{{The 1st argument to 'isalnum' is >= 256 but should be an unsigned char value or EOF}} \ + // bugpath-note{{The 1st argument to 'isalnum' is >= 256 but should be an unsigned char value or EOF}} (void)ret; } @@ -67,9 +67,9 @@ void test_toupper_concrete(int v) { int ret = toupper(256); // \ - // report-warning{{The 1st argument to 'toupper' should be an unsigned char value or EOF}} \ - // bugpath-warning{{The 1st argument to 'toupper' should be an unsigned char value or EOF}} \ - // bugpath-note{{The 1st argument to 'toupper' should be an unsigned char value or EOF}} + // report-warning{{The 1st argument to 'toupper' is 256 but should be an unsigned char value or EOF}} \ + // bugpath-warning{{The 1st argument to 'toupper' is 256 but should be an unsigned char value or EOF}} \ + // bugpath-note{{The 1st argument to 'toupper' is 256 but should be an unsigned char value or EOF}} (void)ret; } @@ -91,9 +91,9 @@ // bugpath-note{{Taking true branch}} int ret = toupper(x); // \ - // report-warning{{The 1st argument to 'toupper' should be an unsigned char value or EOF}} \ - // bugpath-warning{{The 1st argument to 'toupper' should be an unsigned char value or EOF}} \ - // bugpath-note{{The 1st argument to 'toupper' should be an unsigned char value or EOF}} + // report-warning{{The 1st argument to 'toupper' is >= 256 but should be an unsigned char value or EOF}} \ + // bugpath-warning{{The 1st argument to 'toupper' is >= 256 but should be an unsigned char value or EOF}} \ + // bugpath-note{{The 1st argument to 'toupper' is >= 256 but should be an unsigned char value or EOF}} (void)ret; } @@ -103,9 +103,9 @@ void test_tolower_concrete(int v) { int ret = tolower(256); // \ - // report-warning{{The 1st argument to 'tolower' should be an unsigned char value or EOF}} \ - // bugpath-warning{{The 1st argument to 'tolower' should be an unsigned char value or EOF}} \ - // bugpath-note{{The 1st argument to 'tolower' should be an unsigned char value or EOF}} + // report-warning{{The 1st argument to 'tolower' is 256 but should be an unsigned char value or EOF}} \ + // bugpath-warning{{The 1st argument to 'tolower' is 256 but should be an unsigned char value or EOF}} \ + // bugpath-note{{The 1st argument to 'tolower' is 256 but should be an unsigned char value or EOF}} (void)ret; } @@ -127,9 +127,9 @@ // bugpath-note{{Taking true branch}} int ret = tolower(x); // \ - // report-warning{{The 1st argument to 'tolower' should be an unsigned char value or EOF}} \ - // bugpath-warning{{The 1st argument to 'tolower' should be an unsigned char value or EOF}} \ - // bugpath-note{{The 1st argument to 'tolower' should be an unsigned char value or EOF}} + // report-warning{{The 1st argument to 'tolower' is >= 256 but should be an unsigned char value or EOF}} \ + // bugpath-warning{{The 1st argument to 'tolower' is >= 256 but should be an unsigned char value or EOF}} \ + // bugpath-note{{The 1st argument to 'tolower' is >= 256 but should be an unsigned char value or EOF}} (void)ret; } @@ -139,9 +139,9 @@ void test_toascii_concrete(int v) { int ret = toascii(256); // \ - // report-warning{{The 1st argument to 'toascii' should be an unsigned char value or EOF}} \ - // bugpath-warning{{The 1st argument to 'toascii' should be an unsigned char value or EOF}} \ - // bugpath-note{{The 1st argument to 'toascii' should be an unsigned char value or EOF}} + // report-warning{{The 1st argument to 'toascii' is 256 but should be an unsigned char value or EOF}} \ + // bugpath-warning{{The 1st argument to 'toascii' is 256 but should be an unsigned char value or EOF}} \ + // bugpath-note{{The 1st argument to 'toascii' is 256 but should be an unsigned char value or EOF}} (void)ret; } @@ -163,9 +163,9 @@ // bugpath-note{{Taking true branch}} int ret = toascii(x); // \ - // report-warning{{The 1st argument to 'toascii' should be an unsigned char value or EOF}} \ - // bugpath-warning{{The 1st argument to 'toascii' should be an unsigned char value or EOF}} \ - // bugpath-note{{The 1st argument to 'toascii' should be an unsigned char value or EOF}} + // report-warning{{The 1st argument to 'toascii' is >= 256 but should be an unsigned char value or EOF}} \ + // bugpath-warning{{The 1st argument to 'toascii' is >= 256 but should be an unsigned char value or EOF}} \ + // bugpath-note{{The 1st argument to 'toascii' is >= 256 but should be an unsigned char value or EOF}} (void)ret; } @@ -176,9 +176,9 @@ size_t fread(void *restrict, size_t, size_t, FILE *restrict); void test_notnull_concrete(FILE *fp) { fread(0, sizeof(int), 10, fp); // \ - // report-warning{{The 1st argument to 'fread' should not be NULL}} \ - // bugpath-warning{{The 1st argument to 'fread' should not be NULL}} \ - // bugpath-note{{The 1st argument to 'fread' should not be NULL}} + // report-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \ + // bugpath-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \ + // bugpath-note{{The 1st argument to 'fread' is NULL but should not be NULL}} } void test_notnull_symbolic(FILE *fp, int *buf) { fread(buf, sizeof(int), 10, fp); @@ -192,9 +192,9 @@ if (!buf) // bugpath-note{{Assuming 'buf' is null}} \ // bugpath-note{{Taking true branch}} fread(buf, sizeof(int), 10, fp); // \ - // report-warning{{The 1st argument to 'fread' should not be NULL}} \ - // bugpath-warning{{The 1st argument to 'fread' should not be NULL}} \ - // bugpath-note{{The 1st argument to 'fread' should not be NULL}} + // report-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \ + // bugpath-warning{{The 1st argument to 'fread' is NULL but should not be NULL}} \ + // bugpath-note{{The 1st argument to 'fread' is NULL but should not be NULL}} } void test_no_node_after_bug(FILE *fp, size_t size, size_t n, void *buf) { if (fp) // \ @@ -202,9 +202,9 @@ // bugpath-note{{Taking false branch}} return; size_t ret = fread(buf, size, n, fp); // \ - // report-warning{{The 4th argument to 'fread' should not be NULL}} \ - // bugpath-warning{{The 4th argument to 'fread' should not be NULL}} \ - // bugpath-note{{The 4th argument to 'fread' should not be NULL}} + // report-warning{{The 4th argument to 'fread' is NULL but should not be NULL}} \ + // bugpath-warning{{The 4th argument to 'fread' is NULL but should not be NULL}} \ + // bugpath-note{{The 4th argument to 'fread' is NULL but should not be NULL}} clang_analyzer_warnIfReached(); // not reachable } @@ -220,9 +220,9 @@ // The 3rd parameter should be the number of elements to read, not // the size in bytes. fread(wbuf, size, nitems, file); // \ - // report-warning{{The size of the 1st argument to 'fread' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} \ - // bugpath-warning{{The size of the 1st argument to 'fread' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} \ - // bugpath-note{{The size of the 1st argument to 'fread' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} + // report-warning{{The 1st argument to 'fread' is out of the accepted range; It should be a buffer with size equal to or greater than the value of the 2nd argument times the 3rd argument}} \ + // bugpath-warning{{The 1st argument to 'fread' is out of the accepted range; It should be a buffer with size equal to or greater than the value of the 2nd argument times the 3rd argument}} \ + // bugpath-note{{The 1st argument to 'fread' is out of the accepted range; It should be a buffer with size equal to or greater than the value of the 2nd argument times the 3rd argument}} } int __two_constrained_args(int, int); @@ -255,18 +255,18 @@ int __variadic(void *stream, const char *format, ...); void test_arg_constraint_on_variadic_fun(void) { __variadic(0, "%d%d", 1, 2); // \ - // report-warning{{The 1st argument to '__variadic' should not be NULL}} \ - // bugpath-warning{{The 1st argument to '__variadic' should not be NULL}} \ - // bugpath-note{{The 1st argument to '__variadic' should not be NULL}} + // report-warning{{The 1st argument to '__variadic' is NULL but should not be NULL}} \ + // bugpath-warning{{The 1st argument to '__variadic' is NULL but should not be NULL}} \ + // bugpath-note{{The 1st argument to '__variadic' is NULL but should not be NULL}} } int __buf_size_arg_constraint(const void *, size_t); void test_buf_size_concrete(void) { char buf[3]; // bugpath-note{{'buf' initialized here}} __buf_size_arg_constraint(buf, 4); // \ - // report-warning{{The size of the 1st argument to '__buf_size_arg_constraint' should be equal to or greater than the value of the 2nd argument}} \ - // bugpath-warning{{The size of the 1st argument to '__buf_size_arg_constraint' should be equal to or greater than the value of the 2nd argument}} \ - // bugpath-note{{The size of the 1st argument to '__buf_size_arg_constraint' should be equal to or greater than the value of the 2nd argument}} + // report-warning{{The 1st argument to '__buf_size_arg_constraint' is out of the accepted range; It should be a buffer with size equal to or greater than the value of the 2nd argument}} \ + // bugpath-warning{{The 1st argument to '__buf_size_arg_constraint' is out of the accepted range; It should be a buffer with size equal to or greater than the value of the 2nd argument}} \ + // bugpath-note{{The 1st argument to '__buf_size_arg_constraint' is out of the accepted range; It should be a buffer with size equal to or greater than the value of the 2nd argument}} } void test_buf_size_symbolic(int s) { char buf[3]; @@ -291,9 +291,9 @@ void test_buf_size_concrete_with_multiplication(void) { short buf[3]; // bugpath-note{{'buf' initialized here}} __buf_size_arg_constraint_mul(buf, 4, sizeof(short)); // \ - // report-warning{{The size of the 1st argument to '__buf_size_arg_constraint_mul' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} \ - // bugpath-warning{{The size of the 1st argument to '__buf_size_arg_constraint_mul' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} \ - // bugpath-note{{The size of the 1st argument to '__buf_size_arg_constraint_mul' should be equal to or greater than the value of the 2nd argument times the 3rd argument}} + // report-warning{{The 1st argument to '__buf_size_arg_constraint_mul' is out of the accepted range; It should be a buffer with size equal to or greater than the value of the 2nd argument times the 3rd argument}} \ + // bugpath-warning{{The 1st argument to '__buf_size_arg_constraint_mul' is out of the accepted range; It should be a buffer with size equal to or greater than the value of the 2nd argument times the 3rd argument}} \ + // bugpath-note{{The 1st argument to '__buf_size_arg_constraint_mul' is out of the accepted range; It should be a buffer with size equal to or greater than the value of the 2nd argument times the 3rd argument}} } void test_buf_size_symbolic_with_multiplication(size_t s) { short buf[3]; @@ -317,7 +317,7 @@ void test_min_buf_size(void) { char buf[9];// bugpath-note{{'buf' initialized here}} __buf_size_arg_constraint_concrete(buf); // \ - // report-warning{{The size of the 1st argument to '__buf_size_arg_constraint_concrete' should be equal to or greater than 10}} \ - // bugpath-warning{{The size of the 1st argument to '__buf_size_arg_constraint_concrete' should be equal to or greater than 10}} \ - // bugpath-note{{The size of the 1st argument to '__buf_size_arg_constraint_concrete' should be equal to or greater than 10}} + // report-warning{{The 1st argument to '__buf_size_arg_constraint_concrete' is out of the accepted range; It should be a buffer with size equal to or greater than 10}} \ + // bugpath-warning{{The 1st argument to '__buf_size_arg_constraint_concrete' is out of the accepted range; It should be a buffer with size equal to or greater than 10}} \ + // bugpath-note{{The 1st argument to '__buf_size_arg_constraint_concrete' is out of the accepted range; It should be a buffer with size equal to or greater than 10}} } diff --git a/clang/test/Analysis/std-c-library-functions-arg-constraints.cpp b/clang/test/Analysis/std-c-library-functions-arg-constraints.cpp --- a/clang/test/Analysis/std-c-library-functions-arg-constraints.cpp +++ b/clang/test/Analysis/std-c-library-functions-arg-constraints.cpp @@ -14,5 +14,5 @@ void test_arg_constraint_on_fun_with_default_param() { __defaultparam(nullptr); // \ - // expected-warning{{The 1st argument to '__defaultparam' should not be NULL}} + // expected-warning{{The 1st argument to '__defaultparam' is NULL but should not be NULL}} } diff --git a/clang/test/Analysis/stream-note.c b/clang/test/Analysis/stream-note.c --- a/clang/test/Analysis/stream-note.c +++ b/clang/test/Analysis/stream-note.c @@ -88,8 +88,8 @@ fclose(F); return; } - fclose(F); // stdargs-warning {{The 1st argument to 'fclose' should not be NULL}} - // stdargs-note@-1 {{The 1st argument to 'fclose' should not be NULL}} + fclose(F); // stdargs-warning {{The 1st argument to 'fclose' is NULL but should not be NULL}} + // stdargs-note@-1 {{The 1st argument to 'fclose' is NULL but should not be NULL}} } void check_eof_notes_feof_after_feof(void) { diff --git a/clang/test/Analysis/stream-stdlibraryfunctionargs.c b/clang/test/Analysis/stream-stdlibraryfunctionargs.c --- a/clang/test/Analysis/stream-stdlibraryfunctionargs.c +++ b/clang/test/Analysis/stream-stdlibraryfunctionargs.c @@ -42,7 +42,7 @@ void test_fread(void) { FILE *fp = tmpfile(); - size_t ret = fread(buf, size, n, fp); // stdargs-warning{{The 4th argument to 'fread' should not be NULL}} + size_t ret = fread(buf, size, n, fp); // stdargs-warning{{The 4th argument to 'fread' is NULL but should not be NULL}} clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} clang_analyzer_eval(ret <= n); // any-warning{{TRUE}} clang_analyzer_eval(ret == n); // any-warning{{TRUE}} any-warning{{FALSE}} @@ -52,7 +52,7 @@ void test_fwrite(void) { FILE *fp = tmpfile(); - size_t ret = fwrite(buf, size, n, fp); // stdargs-warning{{The 4th argument to 'fwrite' should not be NULL}} + size_t ret = fwrite(buf, size, n, fp); // stdargs-warning{{The 4th argument to 'fwrite' is NULL but should not be NULL}} clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} clang_analyzer_eval(ret <= n); // any-warning{{TRUE}} clang_analyzer_eval(ret == n); // any-warning{{TRUE}} any-warning{{FALSE}}