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 @@ -84,9 +84,20 @@ typedef uint32_t ArgNo; static const ArgNo Ret; + using DescString = SmallString<96>; /// Returns the 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 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'. + static void appendOutOfRangeDesc(llvm::APSInt RMin, llvm::APSInt RMax, + QualType ArgT, BasicValueFactory &BVF, + DescString &Out); class ValueConstraint; @@ -101,6 +112,12 @@ /// (or return value) of a function. Derived classes implement different kind /// of constraints, e.g range constraints or correlation between two /// arguments. + /// These are used as argument constraints (preconditions) of functions, in + /// which case a bug report may be emitted if the constraint is not satisfied. + /// Another use is as conditions for summary cases, to create different + /// classes of behavior for a function. In this case no description of the + /// constraint is needed because the summary cases have an own (not generated) + /// description string. class ValueConstraint { public: ValueConstraint(ArgNo ArgN) : ArgN(ArgN) {} @@ -114,9 +131,9 @@ llvm_unreachable("Not implemented"); }; - // Check whether the constraint is malformed or not. It is malformed if the - // specified argument has a mismatch with the given FunctionDecl (e.g. the - // arg number is out-of-range of the function's argument list). + /// Check whether the constraint is malformed or not. It is malformed if the + /// specified argument has a mismatch with the given FunctionDecl (e.g. the + /// arg number is out-of-range of the function's argument list). bool checkValidity(const FunctionDecl *FD) const { const bool ValidArg = ArgN == Ret || ArgN < FD->getNumParams(); assert(ValidArg && "Arg out of range!"); @@ -127,31 +144,34 @@ } ArgNo getArgNo() const { return ArgN; } - // Return those arguments that should be tracked when we report a bug. By - // default it is the argument that is constrained, however, in some special - // cases we need to track other arguments as well. E.g. a buffer size might - // be encoded in another argument. + /// Return those arguments that should be tracked when we report a bug. By + /// default it is the argument that is constrained, however, in some special + /// cases we need to track other arguments as well. E.g. a buffer size might + /// be encoded in another argument. virtual std::vector getArgsToTrack() const { return {ArgN}; } virtual StringRef getName() const = 0; - // Represents that in which context do we require a description of the - // constraint. + /// Represents that in which context do we require a description of the + /// constraint. enum class DescriptionKind { - // The constraint is violated. + /// The constraint is violated. Violation, - // We assume that the constraint is satisfied. + /// We assume that the constraint is satisfied. Assumption }; - // Give a description that explains the constraint to the user. Used when - // the bug is reported. - virtual std::string describe(DescriptionKind DK, ProgramStateRef State, + /// 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 { // 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. - llvm_unreachable("Not implemented"); + llvm_unreachable( + "Description not implemented for summary case constraints"); } protected: @@ -178,13 +198,19 @@ // type (e.g. [0, Socklen_tMax]). If the type is not found, then the range // is default initialized to be empty. IntRangeVector Ranges; + // A textual description of this constraint for the specific case where the + // constraint is used. If empty a generated description will be used. + StringRef Description; public: StringRef getName() const override { return "Range"; } - RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges) - : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges) {} + RangeConstraint(ArgNo ArgN, RangeKind Kind, const IntRangeVector &Ranges, + StringRef Desc = "") + : ValueConstraint(ArgN), Kind(Kind), Ranges(Ranges), Description(Desc) { + } - std::string describe(DescriptionKind DK, ProgramStateRef State, + std::string describe(DescriptionKind DK, const CallEvent &Call, + ProgramStateRef State, const Summary &Summary) const override; const IntRangeVector &getRanges() const { return Ranges; } @@ -256,7 +282,8 @@ public: NotNullConstraint(ArgNo ArgN, bool CannotBeNull = true) : ValueConstraint(ArgN), CannotBeNull(CannotBeNull) {} - std::string describe(DescriptionKind DK, ProgramStateRef State, + std::string describe(DescriptionKind DK, const CallEvent &Call, + ProgramStateRef State, const Summary &Summary) const override; StringRef getName() const override { return "NonNull"; } ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, @@ -328,7 +355,8 @@ return Result; } - std::string describe(DescriptionKind DK, ProgramStateRef State, + std::string describe(DescriptionKind DK, const CallEvent &Call, + ProgramStateRef State, const Summary &Summary) const override; ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, @@ -698,6 +726,11 @@ static SVal getArgSVal(const CallEvent &Call, ArgNo ArgN) { return ArgN == Ret ? Call.getReturnValue() : Call.getArgSVal(ArgN); } + static std::string getFunctionName(const CallEvent &Call) { + assert(Call.getDecl() && + "Call was found by a summary, should have declaration"); + return cast(Call.getDecl())->getNameAsString(); + } public: void checkPreCall(const CallEvent &Call, CheckerContext &C) const; @@ -729,28 +762,22 @@ CheckerContext &C) const { if (!ChecksEnabled[CK_StdCLibraryFunctionArgsChecker]) return; - std::string Msg = - (Twine("Function argument constraint is not satisfied, constraint: ") + - VC->getName().data()) - .str(); + assert(Call.getDecl() && + "Function found in summary must have a declaration available"); + std::string Msg = VC->describe(ValueConstraint::DescriptionKind::Violation, + Call, C.getState(), Summary); + Msg[0] = toupper(Msg[0]); if (!BT_InvalidArg) BT_InvalidArg = std::make_unique( CheckNames[CK_StdCLibraryFunctionArgsChecker], - "Unsatisfied argument constraints", categories::LogicError); + "Function call with invalid argument", categories::LogicError); auto R = std::make_unique(*BT_InvalidArg, Msg, N); - for (ArgNo ArgN : VC->getArgsToTrack()) + for (ArgNo ArgN : VC->getArgsToTrack()) { bugreporter::trackExpressionValue(N, Call.getArgExpr(ArgN), *R); - - // Highlight the range of the argument that was violated. - R->addRange(Call.getArgSourceRange(VC->getArgNo())); - - // Describe the argument constraint violation in a note. - std::string Descr = VC->describe( - ValueConstraint::DescriptionKind::Violation, C.getState(), Summary); - // Capitalize the first letter b/c we want a full sentence. - Descr[0] = toupper(Descr[0]); - R->addNote(Descr, R->getLocation(), Call.getArgSourceRange(VC->getArgNo())); + // All tracked arguments are important, highlight them. + R->addRange(Call.getArgSourceRange(ArgN)); + } C.emitReport(std::move(R)); } @@ -772,82 +799,170 @@ const StdLibraryFunctionsChecker::ArgNo StdLibraryFunctionsChecker::Ret = std::numeric_limits::max(); -} // end of anonymous namespace - static BasicValueFactory &getBVF(ProgramStateRef State) { ProgramStateManager &Mgr = State->getStateManager(); SValBuilder &SVB = Mgr.getSValBuilder(); return SVB.getBasicValueFactory(); } +} // 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::appendInsideRangeDesc(llvm::APSInt RMin, + llvm::APSInt RMax, + QualType ArgT, + BasicValueFactory &BVF, + DescString &Out) { + if (RMin.isZero() && RMax.isZero()) + Out.append("zero"); + else if (RMin == RMax) + RMin.toString(Out); + else if (RMin == BVF.getMinValue(ArgT)) { + if (RMax == -1) + Out.append("< 0"); + else { + Out.append("<= "); + RMax.toString(Out); + } + } else if (RMax == BVF.getMaxValue(ArgT)) { + if (RMin.isOne()) + Out.append("> 0"); + else { + Out.append(">= "); + RMin.toString(Out); + } + } 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); + } else { + Out.append("between "); + RMin.toString(Out); + Out.append(" and "); + RMax.toString(Out); + } +} + +void StdLibraryFunctionsChecker::appendOutOfRangeDesc(llvm::APSInt RMin, + llvm::APSInt RMax, + QualType ArgT, + BasicValueFactory &BVF, + DescString &Out) { + if (RMin.isZero() && RMax.isZero()) + Out.append("nonzero"); + else if (RMin == RMax) { + Out.append("not equal to "); + RMin.toString(Out); + } else if (RMin == BVF.getMinValue(ArgT)) { + if (RMax == -1) + Out.append(">= 0"); + else { + Out.append("> "); + RMax.toString(Out); + } + } else if (RMax == BVF.getMaxValue(ArgT)) { + if (RMin.isOne()) + Out.append("<= 0"); + else { + Out.append("< "); + RMin.toString(Out); + } + } else if (RMin.isNegative() == RMax.isNegative() && + RMin.getLimitedValue() == RMax.getLimitedValue() - 1) { + Out.append("not "); + RMin.toString(Out); + Out.append(" and not "); + RMax.toString(Out); + } else { + Out.append("not between "); + RMin.toString(Out); + Out.append(" and "); + RMax.toString(Out); + } +} + std::string StdLibraryFunctionsChecker::NotNullConstraint::describe( - DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const { + 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 += DK == Violation ? " should not be NULL" : " is not NULL"; + Result += " to '"; + Result += getFunctionName(Call); + Result += DK == Violation ? "' should not be NULL" : "' is not NULL"; return Result.c_str(); } std::string StdLibraryFunctionsChecker::RangeConstraint::describe( - DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const { + DescriptionKind DK, const CallEvent &Call, ProgramStateRef State, + const Summary &Summary) const { BasicValueFactory &BVF = getBVF(State); QualType T = Summary.getArgType(getArgNo()); - SmallString<48> Result; + DescString Result; const auto Violation = ValueConstraint::DescriptionKind::Violation; Result += "the "; Result += getArgDesc(ArgN); - Result += DK == Violation ? " should be " : " is "; - - // Range kind as a string. - Kind == OutOfRange ? Result += "out of" : Result += "within"; - - // Get the range values as a string. - Result += " the range "; - if (Ranges.size() > 1) - Result += "["; - unsigned I = Ranges.size(); - for (const std::pair &R : Ranges) { - Result += "["; - const llvm::APSInt &Min = BVF.getValue(R.first, T); - const llvm::APSInt &Max = BVF.getValue(R.second, T); - Min.toString(Result); - Result += ", "; - Max.toString(Result); - Result += "]"; - if (--I > 0) - Result += ", "; + Result += " to '"; + Result += getFunctionName(Call); + Result += DK == Violation ? "' should be " : "' is "; + if (!Description.empty()) { + Result += 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); + if (--I > 0) + Result += " or "; + } + } else { + for (const std::pair &R : Ranges) { + appendOutOfRangeDesc(BVF.getValue(R.first, T), + BVF.getValue(R.second, T), T, BVF, Result); + if (--I > 0) + Result += " and "; + } + } } - if (Ranges.size() > 1) - Result += "]"; return Result.c_str(); } -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; -} - std::string StdLibraryFunctionsChecker::BufferSizeConstraint::describe( - DescriptionKind DK, ProgramStateRef State, const Summary &Summary) const { + 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 += DK == Violation ? " should be " : " is "; - Result += "equal to or greater than the value of "; + Result += " to '"; + Result += getFunctionName(Call); + Result += DK == Violation ? "' should be " : "' is "; + Result += "equal to or greater than "; if (ConcreteSize) { ConcreteSize->toString(Result); } else if (SizeArgN) { - Result += "the "; + Result += "the value of the "; Result += getArgDesc(*SizeArgN); if (SizeMultiplierArgN) { Result += " times the "; @@ -998,7 +1113,7 @@ SmallString<64> Msg; Msg += "Assuming "; Msg += Constraint->describe(ValueConstraint::DescriptionKind::Assumption, - NewState, Summary); + Call, NewState, Summary); const auto ArgSVal = Call.getArgSVal(Constraint->getArgNo()); NewNode = C.addTransition( NewState, NewNode, @@ -1358,9 +1473,9 @@ } addToFunctionSummaryMap(ACtx, FunctionSummaryMap, DisplayLoadedSummaries); // Below are helpers functions to create the summaries. - auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind, - IntRangeVector Ranges) { - return std::make_shared(ArgN, Kind, Ranges); + auto ArgumentCondition = [](ArgNo ArgN, RangeKind Kind, IntRangeVector Ranges, + StringRef Desc = "") { + return std::make_shared(ArgN, Kind, Ranges, Desc); }; auto BufferSize = [](auto... Args) { return std::make_shared(Args...); @@ -1443,8 +1558,9 @@ {{'0', '9'}, {'A', 'Z'}, {'a', 'z'}, {128, UCharRangeMax}}), ReturnValueCondition(WithinRange, SingleValue(0))}, ErrnoIrrelevant, "Assuming the character is non-alphanumeric") - .ArgConstraint(ArgumentCondition( - 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); + .ArgConstraint(ArgumentCondition(0U, WithinRange, + {{EOFv, EOFv}, {0, UCharRangeMax}}, + "an unsigned char value or EOF"))); addToFunctionSummaryMap( "isalpha", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) @@ -1603,18 +1719,21 @@ addToFunctionSummaryMap( "toupper", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) - .ArgConstraint(ArgumentCondition( - 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); + .ArgConstraint(ArgumentCondition(0U, WithinRange, + {{EOFv, EOFv}, {0, UCharRangeMax}}, + "an unsigned char value or EOF"))); addToFunctionSummaryMap( "tolower", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) - .ArgConstraint(ArgumentCondition( - 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); + .ArgConstraint(ArgumentCondition(0U, WithinRange, + {{EOFv, EOFv}, {0, UCharRangeMax}}, + "an unsigned char value or EOF"))); addToFunctionSummaryMap( "toascii", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) - .ArgConstraint(ArgumentCondition( - 0U, WithinRange, {{EOFv, EOFv}, {0, UCharRangeMax}}))); + .ArgConstraint(ArgumentCondition(0U, WithinRange, + {{EOFv, EOFv}, {0, UCharRangeMax}}, + "an unsigned char value or EOF"))); // The getc() family of functions that returns either a char or an EOF. addToFunctionSummaryMap( @@ -3097,11 +3216,13 @@ // Functions for testing. if (ChecksEnabled[CK_StdCLibraryFunctionsTesterChecker]) { + const RangeInt IntMin = BVF.getMinValue(IntTy).getLimitedValue(); + addToFunctionSummaryMap( "__not_null", Signature(ArgTypes{IntPtrTy}, RetType{IntTy}), Summary(EvalCallAsPure).ArgConstraint(NotNull(ArgNo(0)))); - // Test range values. + // Test inside range constraints. addToFunctionSummaryMap( "__single_val_0", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) @@ -3114,11 +3235,124 @@ "__range_1_2", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(1, 2)))); - addToFunctionSummaryMap("__range_1_2__4_5", + addToFunctionSummaryMap( + "__range_m1_1", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(-1, 1)))); + addToFunctionSummaryMap( + "__range_m2_m1", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(-2, -1)))); + addToFunctionSummaryMap( + "__range_m10_10", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, WithinRange, Range(-10, 10)))); + addToFunctionSummaryMap("__range_m1_inf", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, Range(-1, IntMax)))); + addToFunctionSummaryMap("__range_0_inf", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .ArgConstraint(ArgumentCondition( - 0U, WithinRange, Range({1, 2}, {4, 5})))); + 0U, WithinRange, Range(0, IntMax)))); + addToFunctionSummaryMap("__range_1_inf", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, Range(1, IntMax)))); + addToFunctionSummaryMap("__range_minf_m1", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, Range(IntMin, -1)))); + addToFunctionSummaryMap("__range_minf_0", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, Range(IntMin, 0)))); + addToFunctionSummaryMap("__range_minf_1", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, Range(IntMin, 1)))); + addToFunctionSummaryMap("__range_1_2__4_6", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, WithinRange, Range({1, 2}, {4, 6})))); + addToFunctionSummaryMap( + "__range_1_2__4_inf", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, WithinRange, + Range({1, 2}, {4, IntMax})))); + + // Test out of range constraints. + addToFunctionSummaryMap( + "__single_val_out_0", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(0)))); + addToFunctionSummaryMap( + "__single_val_out_1", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, OutOfRange, SingleValue(1)))); + addToFunctionSummaryMap( + "__range_out_1_2", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(1, 2)))); + addToFunctionSummaryMap( + "__range_out_m1_1", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(-1, 1)))); + addToFunctionSummaryMap( + "__range_out_m2_m1", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(-2, -1)))); + addToFunctionSummaryMap( + "__range_out_m10_10", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition(0U, OutOfRange, Range(-10, 10)))); + addToFunctionSummaryMap("__range_out_m1_inf", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, OutOfRange, Range(-1, IntMax)))); + addToFunctionSummaryMap("__range_out_0_inf", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, OutOfRange, Range(0, IntMax)))); + addToFunctionSummaryMap("__range_out_1_inf", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, OutOfRange, Range(1, IntMax)))); + addToFunctionSummaryMap("__range_out_minf_m1", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, OutOfRange, Range(IntMin, -1)))); + addToFunctionSummaryMap("__range_out_minf_0", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, OutOfRange, Range(IntMin, 0)))); + addToFunctionSummaryMap("__range_out_minf_1", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, OutOfRange, Range(IntMin, 1)))); + addToFunctionSummaryMap("__range_out_1_2__4_6", + Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint(ArgumentCondition( + 0U, OutOfRange, Range({1, 2}, {4, 6})))); + addToFunctionSummaryMap( + "__range_out_1_2__4_inf", Signature(ArgTypes{IntTy}, RetType{IntTy}), + Summary(EvalCallAsPure) + .ArgConstraint( + ArgumentCondition(0U, OutOfRange, Range({1, 2}, {4, IntMax})))); // Test range kind. addToFunctionSummaryMap( 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 is not NULL}} + __not_null(x); // expected-note{{Assuming 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 is within the range [0, 0]}} + __single_val_0(x); // expected-note{{Assuming 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 is equal to or greater than the value of 10}} + __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}} 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,8 +15,7 @@ int __not_null(int *); void test_not_null(int *x) { __not_null(nullptr); // \ - // expected-note{{The 1st argument should not be NULL}} \ - // expected-warning{{}} + // expected-warning{{The 1st argument to '__not_null' should not be NULL}} } // Check the BufferSizeConstraint violation notes. @@ -29,66 +28,159 @@ case 1: { char buf[9]; __buf_size_arg_constraint_concrete(buf); // \ - // expected-note{{The size of the 1st argument should be equal to or greater than the value of 10}} \ - // expected-warning{{}} + // expected-warning{{The size of the 1st argument to '__buf_size_arg_constraint_concrete' should be equal to or greater than 10}} break; } case 2: { char buf[3]; __buf_size_arg_constraint(buf, 4); // \ - // expected-note{{The size of the 1st argument should be equal to or greater than the value of the 2nd arg}} \ - // expected-warning{{}} + // 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}} break; } case 3: { char buf[3]; __buf_size_arg_constraint_mul(buf, 4, 2); // \ - // expected-note{{The size of the 1st argument should be equal to or greater than the value of the 2nd argument times the 3rd argument}} \ - // expected-warning{{}} + // 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}} break; } } } // Check the RangeConstraint violation notes. -int __single_val_1(int); // [1, 1] -int __range_1_2(int); // [1, 2] -int __range_1_2__4_5(int); // [1, 2], [4, 5] -void test_range(int x) { - __single_val_1(2); // \ - // expected-note{{The 1st argument should be within the range [1, 1]}} \ - // expected-warning{{}} -} -// Do more specific check against the range strings. + +int __single_val_0(int); // [0, 0] +int __single_val_1(int); // [1, 1] +int __range_1_2(int); // [1, 2] +int __range_m1_1(int); // [-1, 1] +int __range_m2_m1(int); // [-2, -1] +int __range_m10_10(int); // [-10, 10] +int __range_m1_inf(int); // [-1, inf] +int __range_0_inf(int); // [0, inf] +int __range_1_inf(int); // [1, inf] +int __range_minf_m1(int); // [-inf, -1] +int __range_minf_0(int); // [-inf, 0] +int __range_minf_1(int); // [-inf, 1] +int __range_1_2__4_6(int); // [1, 2], [4, 6] +int __range_1_2__4_inf(int); // [1, 2], [4, inf] + +int __single_val_out_0(int); // [0, 0] +int __single_val_out_1(int); // [1, 1] +int __range_out_1_2(int); // [1, 2] +int __range_out_m1_1(int); // [-1, 1] +int __range_out_m2_m1(int); // [-2, -1] +int __range_out_m10_10(int); // [-10, 10] +int __range_out_m1_inf(int); // [-1, inf] +int __range_out_0_inf(int); // [0, inf] +int __range_out_1_inf(int); // [1, inf] +int __range_out_minf_m1(int); // [-inf, -1] +int __range_out_minf_0(int); // [-inf, 0] +int __range_out_minf_1(int); // [-inf, 1] +int __range_out_1_2__4_6(int); // [1, 2], [4, 6] +int __range_out_1_2__4_inf(int); // [1, 2], [4, inf] + void test_range_values(int x) { switch (x) { - case 1: - __single_val_1(2); // expected-note{{[1, 1]}} \ - // expected-warning{{}} - break; - case 2: - __range_1_2(3); // expected-note{{[1, 2]}} \ - // expected-warning{{}} - break; - case 3: - __range_1_2__4_5(3); // expected-note{{[[1, 2], [4, 5]]}} \ - // expected-warning{{}} - break; + case 0: + __single_val_0(1); // expected-warning{{should be zero}} + break; + case 1: + __single_val_1(2); // expected-warning{{should be 1}} + break; + case 2: + __range_1_2(3); // expected-warning{{should be 1 or 2}} + break; + case 3: + __range_m1_1(3); // expected-warning{{should be between -1 and 1}} + break; + case 4: + __range_m2_m1(1); // expected-warning{{should be -2 or -1}} + break; + case 5: + __range_m10_10(11); // expected-warning{{should be between -10 and 10}} + break; + case 6: + __range_m10_10(-11); // expected-warning{{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}} + break; + case 8: + __range_1_2__4_inf(3); // expected-warning{{should be 1 or 2 or >= 4}} + break; } } -// Do more specific check against the range kinds. -int __within(int); // [1, 1] -int __out_of(int); // [1, 1] -void test_range_kind(int x) { + +void test_range_values_inf(int x) { switch (x) { - case 1: - __within(2); // expected-note{{within}} \ - // expected-warning{{}} - break; - case 2: - __out_of(1); // expected-note{{out of}} \ - // expected-warning{{}} - break; + case 1: + __range_m1_inf(-2); // expected-warning{{should be >= -1}} + break; + case 2: + __range_0_inf(-1); // expected-warning{{should be >= 0}} + break; + case 3: + __range_1_inf(0); // expected-warning{{should be > 0}} + break; + case 4: + __range_minf_m1(0); // expected-warning{{should be < 0}} + break; + case 5: + __range_minf_0(1); // expected-warning{{should be <= 0}} + break; + case 6: + __range_minf_1(2); // expected-warning{{should be <= 1}} + break; } } +void test_range_values_out(int x) { + switch (x) { + case 0: + __single_val_out_0(0); // expected-warning{{should be nonzero}} + break; + case 1: + __single_val_out_1(1); // expected-warning{{should be not equal to 1}} + break; + case 2: + __range_out_1_2(2); // expected-warning{{should be not 1 and not 2}} + break; + case 3: + __range_out_m1_1(-1); // expected-warning{{should be not between -1 and 1}} + break; + case 4: + __range_out_m2_m1(-2); // expected-warning{{should be not -2 and not -1}} + break; + case 5: + __range_out_m10_10(0); // expected-warning{{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}} + break; + case 7: + __range_out_1_2__4_inf(4); // expected-warning{{should be not 1 and not 2 and < 4}} + break; + } +} + +void test_range_values_out_inf(int x) { + switch (x) { + case 1: + __range_out_minf_m1(-1); // expected-warning{{should be >= 0}} + break; + case 2: + __range_out_minf_0(0); // expected-warning{{should be > 0}} + break; + case 3: + __range_out_minf_1(1); // expected-warning{{should be > 1}} + break; + case 4: + __range_out_m1_inf(-1); // expected-warning{{should be < -1}} + break; + case 5: + __range_out_0_inf(0); // expected-warning{{should be < 0}} + break; + case 6: + __range_out_1_inf(1); // expected-warning{{should be <= 0}} + break; + } +} 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,9 +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{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} } int __buf_size_arg_constraint_mul(const void *, size_t, size_t); @@ -27,7 +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{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} } 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,11 +30,9 @@ void test_alnum_concrete(int v) { int ret = isalnum(256); // \ - // report-warning{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} (void)ret; } @@ -57,11 +55,9 @@ // bugpath-note{{Taking true branch}} int ret = isalnum(x); // \ - // report-warning{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} (void)ret; } @@ -71,11 +67,9 @@ void test_toupper_concrete(int v) { int ret = toupper(256); // \ - // report-warning{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} (void)ret; } @@ -97,11 +91,9 @@ // bugpath-note{{Taking true branch}} int ret = toupper(x); // \ - // report-warning{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} (void)ret; } @@ -111,11 +103,9 @@ void test_tolower_concrete(int v) { int ret = tolower(256); // \ - // report-warning{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} (void)ret; } @@ -137,11 +127,9 @@ // bugpath-note{{Taking true branch}} int ret = tolower(x); // \ - // report-warning{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} (void)ret; } @@ -151,11 +139,9 @@ void test_toascii_concrete(int v) { int ret = toascii(256); // \ - // report-warning{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} (void)ret; } @@ -177,11 +163,9 @@ // bugpath-note{{Taking true branch}} int ret = toascii(x); // \ - // report-warning{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} (void)ret; } @@ -192,11 +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{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} } void test_notnull_symbolic(FILE *fp, int *buf) { fread(buf, sizeof(int), 10, fp); @@ -210,11 +192,9 @@ if (!buf) // bugpath-note{{Assuming 'buf' is null}} \ // bugpath-note{{Taking true branch}} fread(buf, sizeof(int), 10, fp); // \ - // report-warning{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} } void test_no_node_after_bug(FILE *fp, size_t size, size_t n, void *buf) { if (fp) // \ @@ -222,11 +202,9 @@ // bugpath-note{{Taking false branch}} return; size_t ret = fread(buf, size, n, fp); // \ - // report-warning{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} clang_analyzer_warnIfReached(); // not reachable } @@ -242,11 +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{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} } int __two_constrained_args(int, int); @@ -279,22 +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{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} } 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{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} } void test_buf_size_symbolic(int s) { char buf[3]; @@ -319,11 +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{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} } void test_buf_size_symbolic_with_multiplication(size_t s) { short buf[3]; @@ -347,9 +317,7 @@ void test_min_buf_size(void) { char buf[9];// bugpath-note{{'buf' initialized here}} __buf_size_arg_constraint_concrete(buf); // \ - // report-warning{{Function argument constraint is not satisfied}} \ - // report-note{{}} \ - // bugpath-warning{{Function argument constraint is not satisfied}} \ - // bugpath-note{{}} \ - // bugpath-note{{Function argument constraint is not satisfied}} + // 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}} } 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,6 +14,5 @@ void test_arg_constraint_on_fun_with_default_param() { __defaultparam(nullptr); // \ - // expected-warning{{Function argument constraint is not satisfied}} \ - // expected-note{{}} + // expected-warning{{The 1st argument to '__defaultparam' 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,9 +88,8 @@ fclose(F); return; } - fclose(F); // stdargs-warning {{Function argument constraint is not satisfied}} - // stdargs-note@-1 {{Function argument constraint is not satisfied}} - // stdargs-note@-2 {{The 1st argument should not be NULL}} + 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}} } 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 @@ -18,37 +18,31 @@ void test_fopen(void) { FILE *fp = fopen("path", "r"); clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} any-warning{{FALSE}} - fclose(fp); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 1st argument should not be NULL}} + fclose(fp); // stdargs-warning{{should not be NULL}} } void test_tmpfile(void) { FILE *fp = tmpfile(); clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} any-warning{{FALSE}} - fclose(fp); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 1st argument should not be NULL}} + fclose(fp); // stdargs-warning{{should not be NULL}} } void test_fclose(void) { FILE *fp = tmpfile(); - fclose(fp); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 1st argument should not be NULL}} + fclose(fp); // stdargs-warning{{should not be NULL}} clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} } void test_freopen(void) { FILE *fp = tmpfile(); - fp = freopen("file", "w", fp); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 3rd argument should not be NULL}} - fclose(fp); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 1st argument should not be NULL}} + fp = freopen("file", "w", fp); // stdargs-warning{{should not be NULL}} + fclose(fp); // stdargs-warning{{should not be NULL}} clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} } void test_fread(void) { FILE *fp = tmpfile(); - size_t ret = fread(buf, size, n, fp); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 4th argument should not be NULL}} + size_t ret = fread(buf, size, n, fp); // stdargs-warning{{The 4th argument to 'fread' 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}} @@ -58,8 +52,7 @@ void test_fwrite(void) { FILE *fp = tmpfile(); - size_t ret = fwrite(buf, size, n, fp); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 4th argument should not be NULL}} + size_t ret = fwrite(buf, size, n, fp); // stdargs-warning{{The 4th argument to 'fwrite' 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}} @@ -69,24 +62,21 @@ void test_fseek(void) { FILE *fp = tmpfile(); - fseek(fp, 0, 0); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 1st argument should not be NULL}} + fseek(fp, 0, 0); // stdargs-warning{{should not be NULL}} clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} fclose(fp); } void test_ftell(void) { FILE *fp = tmpfile(); - ftell(fp); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 1st argument should not be NULL}} + ftell(fp); // stdargs-warning{{should not be NULL}} clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} fclose(fp); } void test_rewind(void) { FILE *fp = tmpfile(); - rewind(fp); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 1st argument should not be NULL}} + rewind(fp); // stdargs-warning{{should not be NULL}} clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} fclose(fp); } @@ -94,8 +84,7 @@ void test_fgetpos(void) { FILE *fp = tmpfile(); fpos_t pos; - fgetpos(fp, &pos); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 1st argument should not be NULL}} + fgetpos(fp, &pos); // stdargs-warning{{should not be NULL}} clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} fclose(fp); } @@ -103,40 +92,35 @@ void test_fsetpos(void) { FILE *fp = tmpfile(); fpos_t pos; - fsetpos(fp, &pos); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 1st argument should not be NULL}} + fsetpos(fp, &pos); // stdargs-warning{{should not be NULL}} clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} fclose(fp); } void test_clearerr(void) { FILE *fp = tmpfile(); - clearerr(fp); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 1st argument should not be NULL}} + clearerr(fp); // stdargs-warning{{should not be NULL}} clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} fclose(fp); } void test_feof(void) { FILE *fp = tmpfile(); - feof(fp); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 1st argument should not be NULL}} + feof(fp); // stdargs-warning{{should not be NULL}} clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} fclose(fp); } void test_ferror(void) { FILE *fp = tmpfile(); - ferror(fp); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 1st argument should not be NULL}} + ferror(fp); // stdargs-warning{{should not be NULL}} clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} fclose(fp); } void test_fileno(void) { FILE *fp = tmpfile(); - fileno(fp); // stdargs-warning{{Function argument constraint is not satisfied}} \ - // stdargs-note{{The 1st argument should not be NULL}} + fileno(fp); // stdargs-warning{{should not be NULL}} clang_analyzer_eval(fp != NULL); // any-warning{{TRUE}} fclose(fp); }