[analyzer] Utility to match function calls.

This patch adds a small utility to match function calls. This utility abstracts away the mutable keywords and the lazy initialization and caching logic of identifiers from the checkers. The SimpleStreamChecker is ported over this utility within this patch to show the reduction of code and to test this change.

Differential Revision: http://reviews.llvm.org/D15921

llvm-svn: 258572
This commit is contained in:
Gabor Horvath 2016-01-22 22:32:46 +00:00
parent df91910bd4
commit 343730c58f
3 changed files with 42 additions and 21 deletions

View File

@ -49,6 +49,27 @@ enum CallEventKind {
class CallEvent;
class CallEventManager;
/// This class represents a description of a function call using the number of
/// arguments and the name of the function.
class CallDescription {
friend CallEvent;
mutable IdentifierInfo *II;
StringRef FuncName;
unsigned RequiredArgs;
public:
const static unsigned NoArgRequirement = ~0;
/// \brief Constructs a CallDescription object.
///
/// @param FuncName The name of the function that will be matched.
///
/// @param RequiredArgs The number of arguments that is expected to match a
/// call. Omit this parameter to match every occurance of call with a given
/// name regardless the number of arguments.
CallDescription(StringRef FuncName, unsigned RequiredArgs = NoArgRequirement)
: FuncName(FuncName), RequiredArgs(RequiredArgs) {}
};
template<typename T = CallEvent>
class CallEventRef : public IntrusiveRefCntPtr<const T> {
public:
@ -227,6 +248,13 @@ public:
return false;
}
/// \brief Returns true if the CallEvent is a call to a function that matches
/// the CallDescription.
///
/// Note that this function is not intended to be used to match Obj-C method
/// calls.
bool isCalled(const CallDescription &CD) const;
/// \brief Returns a source range for the entire call, suitable for
/// outputting in diagnostics.
virtual SourceRange getSourceRange() const {

View File

@ -51,14 +51,11 @@ class SimpleStreamChecker : public Checker<check::PostCall,
check::PreCall,
check::DeadSymbols,
check::PointerEscape> {
mutable IdentifierInfo *IIfopen, *IIfclose;
CallDescription OpenFn, CloseFn;
std::unique_ptr<BugType> DoubleCloseBugType;
std::unique_ptr<BugType> LeakBugType;
void initIdentifierInfo(ASTContext &Ctx) const;
void reportDoubleClose(SymbolRef FileDescSym,
const CallEvent &Call,
CheckerContext &C) const;
@ -106,7 +103,7 @@ public:
} // end anonymous namespace
SimpleStreamChecker::SimpleStreamChecker()
: IIfopen(nullptr), IIfclose(nullptr) {
: OpenFn("fopen"), CloseFn("fclose", 1) {
// Initialize the bug types.
DoubleCloseBugType.reset(
new BugType(this, "Double fclose", "Unix Stream API Error"));
@ -119,12 +116,10 @@ SimpleStreamChecker::SimpleStreamChecker()
void SimpleStreamChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
initIdentifierInfo(C.getASTContext());
if (!Call.isGlobalCFunction())
return;
if (Call.getCalleeIdentifier() != IIfopen)
if (!Call.isCalled(OpenFn))
return;
// Get the symbolic value corresponding to the file handle.
@ -140,15 +135,10 @@ void SimpleStreamChecker::checkPostCall(const CallEvent &Call,
void SimpleStreamChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
initIdentifierInfo(C.getASTContext());
if (!Call.isGlobalCFunction())
return;
if (Call.getCalleeIdentifier() != IIfclose)
return;
if (Call.getNumArgs() != 1)
if (!Call.isCalled(CloseFn))
return;
// Get the symbolic value corresponding to the file handle.
@ -275,13 +265,6 @@ SimpleStreamChecker::checkPointerEscape(ProgramStateRef State,
return State;
}
void SimpleStreamChecker::initIdentifierInfo(ASTContext &Ctx) const {
if (IIfopen)
return;
IIfopen = &Ctx.Idents.get("fopen");
IIfclose = &Ctx.Idents.get("fclose");
}
void ento::registerSimpleStreamChecker(CheckerManager &mgr) {
mgr.registerChecker<SimpleStreamChecker>();
}

View File

@ -210,6 +210,16 @@ ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit,
return PostImplicitCall(D, Loc, getLocationContext(), Tag);
}
bool CallEvent::isCalled(const CallDescription &CD) const {
assert(getKind() != CE_ObjCMessage && "Obj-C methods are not supported");
if (!CD.II)
CD.II = &getState()->getStateManager().getContext().Idents.get(CD.FuncName);
if (getCalleeIdentifier() != CD.II)
return false;
return (CD.RequiredArgs == CallDescription::NoArgRequirement ||
CD.RequiredArgs == getNumArgs());
}
SVal CallEvent::getArgSVal(unsigned Index) const {
const Expr *ArgE = getArgExpr(Index);
if (!ArgE)