As of now, we have two interfaces to for defining signatures: StdLibraryFunctionsChecker::Signature and CallDescription. An example for how Signatures are used can be seen here:
addToFunctionSummaryMap( "isprint", Signature(ArgTypes{IntTy}, RetType{IntTy}), Summary(EvalCallAsPure) .Case({ArgumentCondition(0U, WithinRange, Range(32, 126)), ReturnValueCondition(OutOfRange, SingleValue(0))}) .Case({ArgumentCondition(0U, OutOfRange, Range(32, 126)), ReturnValueCondition(WithinRange, SingleValue(0))}));
The name of the function is searched for in translation unit's identifier table, then the Signature is matched against each decl Decl with the same name. Ideally, this yields a FunctionDecl, which is mapped to its Summary.
This works well for C functions, but doesn't support C++ at all.
CallDescription emerged with a strong emphasis on recognizing C++ functions. The common example brough up is std::string, which in some standard library implementations is actually called something like std::__cxx11::basic_string, but not in others. Matching this can be a nightmare for checker developers. For this reason, CallDescriptions can be defined like this:
InnerPointerChecker() : AppendFn({"std", "basic_string", "append"}), AssignFn({"std", "basic_string", "assign"}), AddressofFn({"std", "addressof"}), ClearFn({"std", "basic_string", "clear"}), CStrFn({"std", "basic_string", "c_str"}), DataFn({"std", "data"}, 1), DataMemberFn({"std", "basic_string", "data"}), EraseFn({"std", "basic_string", "erase"}), InsertFn({"std", "basic_string", "insert"}), PopBackFn({"std", "basic_string", "pop_back"}), PushBackFn({"std", "basic_string", "push_back"}), ReplaceFn({"std", "basic_string", "replace"}), ReserveFn({"std", "basic_string", "reserve"}), ResizeFn({"std", "basic_string", "resize"}), ShrinkToFitFn({"std", "basic_string", "shrink_to_fit"}), SwapFn({"std", "basic_string", "swap"}) {}
Any identifier which matches at least these identifiers are considered a match (which sometimes leads to incorrect matching, e.g. D81745).
CallDescriptions are (usually) not used for digging up FunctionDecls from the translation unit, but rather during symbolic execution to check in a pre/post call event whether the called function matches the CallDescription:
bool InnerPointerChecker::isInnerPointerAccessFunction( const CallEvent &Call) const { return matchesAny(Call, CStrFn, DataFn, DataMemberFn); }
Most of the new checkers implementing pre/post condition checks on functions now use CallDescriptionMap or CallDescriptionSet. Its up to debate whether the newer Signature approach is better, but its not obvious, and converting from one to the other may be non-trivial as well.
Now, onto this patch. Since CallDescriptions can only be matched against CallEvents that are created during symbolic execution, it was not possible to use it in syntactic-only contexts. For example, even though InnerPointerChecker can check with its set of CallDescriptions whether a function call is interested during analysis, its unable to check without hassle whether a non-analyzer piece of code also calls such a function.
The patch adds the ability to use CallDescriptions in syntactic contexts as well. While we already have that in Signature, we still want to leverage the ability to use dynamic information when we have it (function pointers, for example). This could be done with Signature as well (StdLibraryFunctionsChecker does it), but it makes it even less of a drop-in replacement.