Index: include/clang/StaticAnalyzer/Checkers/Checkers.td =================================================================== --- include/clang/StaticAnalyzer/Checkers/Checkers.td +++ include/clang/StaticAnalyzer/Checkers/Checkers.td @@ -224,6 +224,10 @@ HelpText<"Improve modeling of the C standard library functions">, DescFile<"StdLibraryFunctionsChecker.cpp">; +def StdCXXLibraryFunctionsChecker : Checker<"StdCXXLibraryFunctions">, + HelpText<"Improve modeling of the C++ standard library functions">, + DescFile<"StdLibraryFunctionsChecker.cpp">; + def TrustNonnullChecker : Checker<"TrustNonnull">, HelpText<"Trust that returns from framework methods annotated with _Nonnull are not null">, DescFile<"TrustNonnullChecker.cpp">; Index: lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp =================================================================== --- lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp +++ lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp @@ -77,7 +77,8 @@ /// Given a range, should the argument stay inside or outside this range? /// The special `ComparesToArgument' value indicates that we should /// impose a constraint that involves other argument or return value symbols. - enum ValueRangeKindTy { OutOfRange, WithinRange, ComparesToArgument }; + enum ValueRangeKindTy { OutOfRange, WithinRange, ComparesToArgument, + BoundToArgument}; // The universal integral type to use in value range descriptions. // Unsigned to make sure overflows are well-defined. @@ -95,6 +96,19 @@ typedef uint32_t ArgNoTy; static const ArgNoTy Ret = std::numeric_limits::max(); +public: + /// A flag and a string to store which of the various checks are enabled + /// and what is their name. + enum CheckKindTy { + CK_StdCLibraryFunctionsChecker, + CK_StdCXXLibraryFunctionsChecker, + CK_NumCheckKinds + }; + + DefaultBool ChecksEnabled[CK_NumCheckKinds]; + CheckName CheckNames[CK_NumCheckKinds]; + +private: /// Incapsulates a single range on a single symbol within a branch. class ValueRange { ArgNoTy ArgNo; // Argument to which we apply the range. @@ -120,7 +134,7 @@ } ArgNoTy getOtherArgNo() const { - assert(Kind == ComparesToArgument); + assert(Kind == ComparesToArgument || Kind == BoundToArgument); assert(Args.size() == 1); return static_cast(Args[0].second); } @@ -142,6 +156,9 @@ ProgramStateRef applyAsComparesToArgument(ProgramStateRef State, const CallEvent &Call, const FunctionSummaryTy &Summary) const; + ProgramStateRef + applyAsBoundToArgument(ProgramStateRef State, const CallEvent &Call, + const FunctionSummaryTy &Summary) const; public: ProgramStateRef apply(ProgramStateRef State, const CallEvent &Call, @@ -153,6 +170,8 @@ return applyAsWithinRange(State, Call, Summary); case ComparesToArgument: return applyAsComparesToArgument(State, Call, Summary); + case BoundToArgument: + return applyAsBoundToArgument(State, Call, Summary); } llvm_unreachable("Unknown ValueRange kind!"); } @@ -166,6 +185,7 @@ /// approach to invalidation, and a list of branches - essentially, a list /// of list of ranges - essentially, a list of lists of lists of segments. struct FunctionSummaryTy { + const CheckKindTy CheckKind; const std::vector ArgTypes; const QualType RetType; const InvalidationKindTy InvalidationKind; @@ -322,7 +342,7 @@ ProgramStateManager &Mgr = State->getStateManager(); SValBuilder &SVB = Mgr.getSValBuilder(); QualType CondT = SVB.getConditionType(); - QualType T = getArgType(Summary, getArgNo()); + QualType T = getArgType(Call, getArgNo()); SVal V = getArgSVal(Call, getArgNo()); BinaryOperator::Opcode Op = getOpcode(); @@ -337,6 +357,26 @@ return State; } +ProgramStateRef +StdLibraryFunctionsChecker::ValueRange::applyAsBoundToArgument( + ProgramStateRef State, const CallEvent &Call, + const FunctionSummaryTy &Summary) const { + assert(getArgNo() == Ret && "Cannot bind argument to another argument"); + + ProgramStateManager &Mgr = State->getStateManager(); + SValBuilder &SVB = Mgr.getSValBuilder(); + QualType T = getArgType(Call, getArgNo()); + + ArgNoTy OtherArg = getOtherArgNo(); + SVal OtherV = getArgSVal(Call, OtherArg); + QualType OtherT = getArgType(Call, OtherArg); + // Note: we avoid integral promotion for comparison. + OtherV = SVB.evalCast(OtherV, T, OtherT); + State = State->BindExpr(Call.getOriginExpr(), Call.getLocationContext(), + OtherV); + return State; +} + void StdLibraryFunctionsChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const { const FunctionDecl *FD = dyn_cast_or_null(Call.getDecl()); @@ -375,7 +415,7 @@ return false; Optional FoundSummary = findFunctionSummary(FD, CE, C); - if (!FoundSummary) + if (!FoundSummary || !ChecksEnabled[FoundSummary->CheckKind]) return false; const FunctionSummaryTy &Summary = *FoundSummary; @@ -444,7 +484,9 @@ if (!II) return None; StringRef Name = II->getName(); - if (Name.empty() || !C.isCLibraryFunction(FD, Name)) + + if (Name.empty() || + !(C.isCLibraryFunction(FD, Name) || FD->isInStdNamespace())) return None; auto FSMI = FunctionSummaryMap.find(Name); @@ -531,12 +573,12 @@ #define SUMMARY_WITH_VARIANTS(identifier) {#identifier, { #define END_SUMMARY_WITH_VARIANTS }}, -#define VARIANT(argument_types, return_type, invalidation_approach) \ - { argument_types, return_type, invalidation_approach, { +#define VARIANT(lang, argument_types, return_type, invalidation_approach) \ + { lang, argument_types, return_type, invalidation_approach, { #define END_VARIANT } }, -#define SUMMARY(identifier, argument_types, return_type, \ +#define SUMMARY(identifier, lang, argument_types, return_type, \ invalidation_approach) \ - { #identifier, { { argument_types, return_type, invalidation_approach, { + { #identifier, { { lang, argument_types, return_type, invalidation_approach, { #define END_SUMMARY } } } }, #define ARGUMENT_TYPES(...) { __VA_ARGS__ } #define RETURN_TYPE(x) x @@ -552,11 +594,15 @@ #define ARG_NO(x) x##U #define RANGE(x, y) { x, y }, #define SINGLE_VALUE(x) RANGE(x, x) -#define IS_LESS_THAN(arg) { BO_LE, arg } +#define IS_EQUAL_TO(arg) { BO_EQ, arg } +#define IS_NOT_EQUAL_TO(arg) { BO_NE, arg } +#define IS_LESS_THAN_OR_EQUAL_TO(arg) { BO_LE, arg } +#define LANG_C CK_StdCLibraryFunctionsChecker +#define LANG_CXX CK_StdCXXLibraryFunctionsChecker FunctionSummaryMap = { // The isascii() family of functions. - SUMMARY(isalnum, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + SUMMARY(isalnum, LANG_C, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(EvalCallAsPure)) CASE // Boils down to isupper() or islower() or isdigit() ARGUMENT_CONDITION(ARG_NO(0), WithinRange) @@ -587,7 +633,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY - SUMMARY(isalpha, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + SUMMARY(isalpha, LANG_C, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(EvalCallAsPure)) CASE // isupper() or islower(). Note that 'Z' is less than 'a'. ARGUMENT_CONDITION(ARG_NO(0), WithinRange) @@ -614,7 +660,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY - SUMMARY(isascii, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + SUMMARY(isascii, LANG_C, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(EvalCallAsPure)) CASE // Is ASCII. ARGUMENT_CONDITION(ARG_NO(0), WithinRange) @@ -633,7 +679,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY - SUMMARY(isblank, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + SUMMARY(isblank, LANG_C, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(EvalCallAsPure)) CASE ARGUMENT_CONDITION(ARG_NO(0), WithinRange) @@ -654,7 +700,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY - SUMMARY(iscntrl, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + SUMMARY(iscntrl, LANG_C, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(EvalCallAsPure)) CASE // 0..31 or 127 ARGUMENT_CONDITION(ARG_NO(0), WithinRange) @@ -675,7 +721,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY - SUMMARY(isdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + SUMMARY(isdigit, LANG_C, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(EvalCallAsPure)) CASE // Is a digit. ARGUMENT_CONDITION(ARG_NO(0), WithinRange) @@ -694,7 +740,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY - SUMMARY(isgraph, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + SUMMARY(isgraph, LANG_C, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(EvalCallAsPure)) CASE ARGUMENT_CONDITION(ARG_NO(0), WithinRange) @@ -713,7 +759,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY - SUMMARY(islower, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + SUMMARY(islower, LANG_C, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(EvalCallAsPure)) CASE // Is certainly lowercase. ARGUMENT_CONDITION(ARG_NO(0), WithinRange) @@ -748,7 +794,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY - SUMMARY(isprint, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + SUMMARY(isprint, LANG_C, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(EvalCallAsPure)) CASE ARGUMENT_CONDITION(ARG_NO(0), WithinRange) @@ -767,7 +813,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY - SUMMARY(ispunct, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + SUMMARY(ispunct, LANG_C, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(EvalCallAsPure)) CASE ARGUMENT_CONDITION(ARG_NO(0), WithinRange) @@ -792,7 +838,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY - SUMMARY(isspace, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + SUMMARY(isspace, LANG_C, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(EvalCallAsPure)) CASE // Space, '\f', '\n', '\r', '\t', '\v'. ARGUMENT_CONDITION(ARG_NO(0), WithinRange) @@ -819,7 +865,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY - SUMMARY(isupper, ARGUMENT_TYPES(IntTy), RETURN_TYPE (IntTy), + SUMMARY(isupper, LANG_C, ARGUMENT_TYPES(IntTy), RETURN_TYPE (IntTy), INVALIDATION_APPROACH(EvalCallAsPure)) CASE // Is certainly uppercase. ARGUMENT_CONDITION(ARG_NO(0), WithinRange) @@ -843,7 +889,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY - SUMMARY(isxdigit, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), + SUMMARY(isxdigit, LANG_C, ARGUMENT_TYPES(IntTy), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(EvalCallAsPure)) CASE ARGUMENT_CONDITION(ARG_NO(0), WithinRange) @@ -868,7 +914,7 @@ END_SUMMARY // The getc() family of functions that returns either a char or an EOF. - SUMMARY(getc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy), + SUMMARY(getc, LANG_C, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall)) CASE // FIXME: EOF is assumed to be defined as -1. RETURN_VALUE_CONDITION(WithinRange) @@ -876,7 +922,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY - SUMMARY(fgetc, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy), + SUMMARY(fgetc, LANG_C, ARGUMENT_TYPES(Irrelevant), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall)) CASE // FIXME: EOF is assumed to be defined as -1. RETURN_VALUE_CONDITION(WithinRange) @@ -884,7 +930,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY - SUMMARY(getchar, ARGUMENT_TYPES(), RETURN_TYPE(IntTy), + SUMMARY(getchar, LANG_C, ARGUMENT_TYPES(), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall)) CASE // FIXME: EOF is assumed to be defined as -1. RETURN_VALUE_CONDITION(WithinRange) @@ -897,33 +943,33 @@ // We are not sure how ssize_t is defined on every platform, so we provide // three variants that should cover common cases. SUMMARY_WITH_VARIANTS(read) - VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), + VARIANT(LANG_C, ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall)) CASE RETURN_VALUE_CONDITION(ComparesToArgument) - IS_LESS_THAN(ARG_NO(2)) + IS_LESS_THAN_OR_EQUAL_TO(ARG_NO(2)) END_RETURN_VALUE_CONDITION RETURN_VALUE_CONDITION(WithinRange) RANGE(-1, IntMax) END_RETURN_VALUE_CONDITION END_CASE END_VARIANT - VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), + VARIANT(LANG_C, ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall)) CASE RETURN_VALUE_CONDITION(ComparesToArgument) - IS_LESS_THAN(ARG_NO(2)) + IS_LESS_THAN_OR_EQUAL_TO(ARG_NO(2)) END_RETURN_VALUE_CONDITION RETURN_VALUE_CONDITION(WithinRange) RANGE(-1, LongMax) END_RETURN_VALUE_CONDITION END_CASE END_VARIANT - VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), + VARIANT(LANG_C, ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall)) CASE RETURN_VALUE_CONDITION(ComparesToArgument) - IS_LESS_THAN(ARG_NO(2)) + IS_LESS_THAN_OR_EQUAL_TO(ARG_NO(2)) END_RETURN_VALUE_CONDITION RETURN_VALUE_CONDITION(WithinRange) RANGE(-1, LongLongMax) @@ -934,33 +980,33 @@ SUMMARY_WITH_VARIANTS(write) // Again, due to elusive nature of ssize_t, we have duplicate // our summaries to cover different variants. - VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), + VARIANT(LANG_C, ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall)) CASE RETURN_VALUE_CONDITION(ComparesToArgument) - IS_LESS_THAN(ARG_NO(2)) + IS_LESS_THAN_OR_EQUAL_TO(ARG_NO(2)) END_RETURN_VALUE_CONDITION RETURN_VALUE_CONDITION(WithinRange) RANGE(-1, IntMax) END_RETURN_VALUE_CONDITION END_CASE END_VARIANT - VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), + VARIANT(LANG_C, ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall)) CASE RETURN_VALUE_CONDITION(ComparesToArgument) - IS_LESS_THAN(ARG_NO(2)) + IS_LESS_THAN_OR_EQUAL_TO(ARG_NO(2)) END_RETURN_VALUE_CONDITION RETURN_VALUE_CONDITION(WithinRange) RANGE(-1, LongMax) END_RETURN_VALUE_CONDITION END_CASE END_VARIANT - VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), + VARIANT(LANG_C, ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy), RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall)) CASE RETURN_VALUE_CONDITION(ComparesToArgument) - IS_LESS_THAN(ARG_NO(2)) + IS_LESS_THAN_OR_EQUAL_TO(ARG_NO(2)) END_RETURN_VALUE_CONDITION RETURN_VALUE_CONDITION(WithinRange) RANGE(-1, LongLongMax) @@ -968,28 +1014,28 @@ END_CASE END_VARIANT END_SUMMARY_WITH_VARIANTS - SUMMARY(fread, + SUMMARY(fread, LANG_C, ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant), RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall)) CASE RETURN_VALUE_CONDITION(ComparesToArgument) - IS_LESS_THAN(ARG_NO(2)) + IS_LESS_THAN_OR_EQUAL_TO(ARG_NO(2)) END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY - SUMMARY(fwrite, + SUMMARY(fwrite, LANG_C, ARGUMENT_TYPES(Irrelevant, Irrelevant, SizeTy, Irrelevant), RETURN_TYPE(SizeTy), INVALIDATION_APPROACH(NoEvalCall)) CASE RETURN_VALUE_CONDITION(ComparesToArgument) - IS_LESS_THAN(ARG_NO(2)) + IS_LESS_THAN_OR_EQUAL_TO(ARG_NO(2)) END_RETURN_VALUE_CONDITION END_CASE END_SUMMARY // getline()-like functions either fail or read at least the delimiter. SUMMARY_WITH_VARIANTS(getline) - VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), + VARIANT(LANG_C, ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall)) CASE RETURN_VALUE_CONDITION(WithinRange) @@ -998,7 +1044,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_VARIANT - VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), + VARIANT(LANG_C, ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall)) CASE RETURN_VALUE_CONDITION(WithinRange) @@ -1007,7 +1053,7 @@ END_RETURN_VALUE_CONDITION END_CASE END_VARIANT - VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), + VARIANT(LANG_C, ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall)) CASE RETURN_VALUE_CONDITION(WithinRange) @@ -1018,7 +1064,8 @@ END_VARIANT END_SUMMARY_WITH_VARIANTS SUMMARY_WITH_VARIANTS(getdelim) - VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), + VARIANT(LANG_C, + ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), RETURN_TYPE(IntTy), INVALIDATION_APPROACH(NoEvalCall)) CASE RETURN_VALUE_CONDITION(WithinRange) @@ -1027,7 +1074,8 @@ END_RETURN_VALUE_CONDITION END_CASE END_VARIANT - VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), + VARIANT(LANG_C, + ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), RETURN_TYPE(LongTy), INVALIDATION_APPROACH(NoEvalCall)) CASE RETURN_VALUE_CONDITION(WithinRange) @@ -1036,7 +1084,8 @@ END_RETURN_VALUE_CONDITION END_CASE END_VARIANT - VARIANT(ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), + VARIANT(LANG_C, + ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), RETURN_TYPE(LongLongTy), INVALIDATION_APPROACH(NoEvalCall)) CASE RETURN_VALUE_CONDITION(WithinRange) @@ -1046,13 +1095,149 @@ END_CASE END_VARIANT END_SUMMARY_WITH_VARIANTS + + // C++ STL + + // STL algorithms + + // std::find() at el. + SUMMARY(find, LANG_CXX, + ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), + RETURN_TYPE(Irrelevant), INVALIDATION_APPROACH(EvalCallAsPure)) + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(ARG_NO(1)) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(Ret) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(find_end, LANG_CXX, + ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), + RETURN_TYPE(Irrelevant), INVALIDATION_APPROACH(EvalCallAsPure)) + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(ARG_NO(1)) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(Ret) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(find_first_of, LANG_CXX, + ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), + RETURN_TYPE(Irrelevant), INVALIDATION_APPROACH(EvalCallAsPure)) + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(ARG_NO(1)) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(Ret) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(find_if, LANG_CXX, + ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), + RETURN_TYPE(Irrelevant), INVALIDATION_APPROACH(EvalCallAsPure)) + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(ARG_NO(1)) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(Ret) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(find_if_not, LANG_CXX, + ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), + RETURN_TYPE(Irrelevant), INVALIDATION_APPROACH(EvalCallAsPure)) + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(ARG_NO(1)) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(Ret) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(lower_bound, LANG_CXX, + ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), + RETURN_TYPE(Irrelevant), INVALIDATION_APPROACH(EvalCallAsPure)) + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(ARG_NO(1)) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(Ret) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(upper_bound, LANG_CXX, + ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant), + RETURN_TYPE(Irrelevant), INVALIDATION_APPROACH(EvalCallAsPure)) + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(ARG_NO(1)) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(Ret) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(search, LANG_CXX, + ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), + RETURN_TYPE(Irrelevant), INVALIDATION_APPROACH(EvalCallAsPure)) + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(ARG_NO(1)) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(Ret) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + SUMMARY(search_n, LANG_CXX, + ARGUMENT_TYPES(Irrelevant, Irrelevant, Irrelevant, Irrelevant), + RETURN_TYPE(Irrelevant), INVALIDATION_APPROACH(EvalCallAsPure)) + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(ARG_NO(1)) + END_RETURN_VALUE_CONDITION + END_CASE + CASE + RETURN_VALUE_CONDITION(BoundToArgument) + IS_EQUAL_TO(Ret) + END_RETURN_VALUE_CONDITION + END_CASE + END_SUMMARY + }; } -void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) { - // If this checker grows large enough to support C++, Objective-C, or other - // standard libraries, we could use multiple register...Checker() functions, - // which would register various checkers with the help of the same Checker - // class, turning on different function summaries. - mgr.registerChecker(); +#define REGISTER_CHECKER(name) \ + void ento::register##name(CheckerManager &Mgr) { \ + auto *checker = Mgr.registerChecker(); \ + checker->ChecksEnabled[StdLibraryFunctionsChecker::CK_##name] = true; \ + checker->CheckNames[StdLibraryFunctionsChecker::CK_##name] = \ + Mgr.getCurrentCheckName(); \ } + +REGISTER_CHECKER(StdCLibraryFunctionsChecker) +REGISTER_CHECKER(StdCXXLibraryFunctionsChecker) Index: test/Analysis/Inputs/system-header-simulator-cxx.h =================================================================== --- test/Analysis/Inputs/system-header-simulator-cxx.h +++ test/Analysis/Inputs/system-header-simulator-cxx.h @@ -232,14 +232,17 @@ template class vector { + public: typedef T value_type; typedef size_t size_type; typedef __vector_iterator iterator; typedef __vector_iterator const_iterator; + private: T *_start; T *_finish; T *_end_of_storage; + public: vector() : _start(0), _finish(0), _end_of_storage(0) {} template @@ -714,6 +717,32 @@ template InputIterator find(InputIterator first, InputIterator last, const T &val); + template + ForwardIterator1 find_end(ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2); + template + ForwardIterator1 find_first_of(ForwardIterator1 first1, + ForwardIterator1 last1, + ForwardIterator2 first2, + ForwardIterator2 last2); + template + InputIterator find_if(InputIterator first, InputIterator last, + UnaryPredicate pred); + template + InputIterator find_if_not(InputIterator first, InputIterator last, + UnaryPredicate pred); + template + InputIterator lower_bound(InputIterator first, InputIterator last, + const T &val); + template + InputIterator upper_bound(InputIterator first, InputIterator last, + const T &val); + template + ForwardIterator1 search(ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2); + template + ForwardIterator1 search_n(ForwardIterator1 first1, ForwardIterator1 last1, + ForwardIterator2 first2, ForwardIterator2 last2); template ForwardIterator1 find_first_of(ForwardIterator1 first1, Index: test/Analysis/diagnostics/explicit-suppression.cpp =================================================================== --- test/Analysis/diagnostics/explicit-suppression.cpp +++ test/Analysis/diagnostics/explicit-suppression.cpp @@ -19,6 +19,6 @@ void testCopyNull(C *I, C *E) { std::copy(I, E, (C *)0); #ifndef SUPPRESSED - // expected-warning@../Inputs/system-header-simulator-cxx.h:627 {{Called C++ object pointer is null}} + // expected-warning@../Inputs/system-header-simulator-cxx.h:630 {{Called C++ object pointer is null}} #endif } Index: test/Analysis/iterator-range.cpp =================================================================== --- test/Analysis/iterator-range.cpp +++ test/Analysis/iterator-range.cpp @@ -1,5 +1,5 @@ -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=false %s -verify -// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,cplusplus,alpha.cplusplus.IteratorRange -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,apiModeling.StdCXXLibraryFunctions,cplusplus,alpha.cplusplus.IteratorRange -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=false %s -verify +// RUN: %clang_analyze_cc1 -std=c++11 -analyzer-checker=core,apiModeling.StdCXXLibraryFunctions,cplusplus,alpha.cplusplus.IteratorRange -analyzer-config aggressive-binary-operation-simplification=true -analyzer-config c++-container-inlining=true -DINLINE=1 %s -verify #include "Inputs/system-header-simulator-cxx.h" @@ -97,6 +97,108 @@ *i2; // expected-warning{{Iterator accessed outside of its range}} } +void good_find(std::vector &V, int e) { + auto first = std::find(V.begin(), V.end(), e); + if (V.end() != first) + *first; // no-warning +} + +void bad_find(std::vector &V, int e) { + auto first = std::find(V.begin(), V.end(), e); + *first; // expected-warning{{Iterator accessed outside of its range}} +} + +void good_find_end(std::vector &V, std::vector &seq) { + auto last = std::find_end(V.begin(), V.end(), seq.begin(), seq.end()); + if (V.end() != last) + *last; // no-warning +} + +void bad_find_end(std::vector &V, std::vector &seq) { + auto last = std::find_end(V.begin(), V.end(), seq.begin(), seq.end()); + *last; // expected-warning{{Iterator accessed outside of its range}} +} + +void good_find_first_of(std::vector &V, std::vector &seq) { + auto first = + std::find_first_of(V.begin(), V.end(), seq.begin(), seq.end()); + if (V.end() != first) + *first; // no-warning +} + +void bad_find_first_of(std::vector &V, std::vector &seq) { + auto first = std::find_end(V.begin(), V.end(), seq.begin(), seq.end()); + *first; // expected-warning{{Iterator accessed outside of its range}} +} + +bool odd(int i) { return i % 2; } + +void good_find_if(std::vector &V) { + auto first = std::find_if(V.begin(), V.end(), odd); + if (V.end() != first) + *first; // no-warning +} + +void bad_find_if(std::vector &V, int e) { + auto first = std::find_if(V.begin(), V.end(), odd); + *first; // expected-warning{{Iterator accessed outside of its range}} +} + +void good_find_if_not(std::vector &V) { + auto first = std::find_if_not(V.begin(), V.end(), odd); + if (V.end() != first) + *first; // no-warning +} + +void bad_find_if_not(std::vector &V, int e) { + auto first = std::find_if_not(V.begin(), V.end(), odd); + *first; // expected-warning{{Iterator accessed outside of its range}} +} + +void good_lower_bound(std::vector &V, int e) { + auto first = std::lower_bound(V.begin(), V.end(), e); + if (V.end() != first) + *first; // no-warning +} + +void bad_lower_bound(std::vector &V, int e) { + auto first = std::lower_bound(V.begin(), V.end(), e); + *first; // expected-warning{{Iterator accessed outside of its range}} +} + +void good_upper_bound(std::vector &V, int e) { + auto last = std::lower_bound(V.begin(), V.end(), e); + if (V.end() != last) + *last; // no-warning +} + +void bad_upper_bound(std::vector &V, int e) { + auto last = std::lower_bound(V.begin(), V.end(), e); + *last; // expected-warning{{Iterator accessed outside of its range}} +} + +void good_search(std::vector &V, std::vector &seq) { + auto first = std::search(V.begin(), V.end(), seq.begin(), seq.end()); + if (V.end() != first) + *first; // no-warning +} + +void bad_search(std::vector &V, std::vector &seq) { + auto first = std::search(V.begin(), V.end(), seq.begin(), seq.end()); + *first; // expected-warning{{Iterator accessed outside of its range}} +} + +void good_search_n(std::vector &V, std::vector &seq) { + auto nth = std::search_n(V.begin(), V.end(), seq.begin(), seq.end()); + if (V.end() != nth) + *nth; // no-warning +} + +void bad_search_n(std::vector &V, std::vector &seq) { + auto nth = std::search_n(V.begin(), V.end(), seq.begin(), seq.end()); + *nth; // expected-warning{{Iterator accessed outside of its range}} +} + template InputIterator nonStdFind(InputIterator first, InputIterator last, const T &val) { Index: test/Analysis/std-c-library-functions.cpp =================================================================== --- test/Analysis/std-c-library-functions.cpp +++ test/Analysis/std-c-library-functions.cpp @@ -12,3 +12,20 @@ void test() { clang_analyzer_eval(isalpha('A')); // no-crash // expected-warning{{UNKNOWN}} } + +namespace std { +int isalnum(int); +} + +namespace { +// Non std!!! + +int isalnum(int) { + return 0; +} +} + +void test_non_std() { + clang_analyzer_eval(std::isalnum('A')); // expected-warning{{TRUE}} + clang_analyzer_eval(isalnum('A')); // expected-warning{{FALSE}} +} Index: test/Analysis/std-cxx-library-functions.cpp =================================================================== --- /dev/null +++ test/Analysis/std-cxx-library-functions.cpp @@ -0,0 +1,11 @@ +// RUN: %clang_analyze_cc1 -analyzer-checker=apiModeling.StdCXXLibraryFunctions,debug.ExprInspection -verify %s + +#include "Inputs/system-header-simulator-cxx.h" + +void clang_analyzer_eval(bool); + +void test_find(std::vector V, int n) { + const std::vector::iterator b = V.begin(), e = V.end(); + clang_analyzer_eval(std::find(b, e, n) == e); // expected-warning{{TRUE}} + // expected-warning@-1{{FALSE}} +}