shdnx created this revision. shdnx added reviewers: zaks.anna, jordan_rose. shdnx added a subscriber: cfe-commits.
Adds a new analyzer hook: ``` ProgramStateRef checkInitialState(const EntryPointInfo& EPInfo) /* non-const */; ``` This allows checkers to act on entry points, set up their initial state (by returning a new state) or prevent the analyzer from continuing from this entry point (by returning nullptr). It also serves to balance the existing checkEndFunction() and checkEndAnalysis() hooks. EntryPointInfo is currently a very simple class containing a const Decl* of the declaration being used as an entry point and a ProgramStateRef of the initial state. It can later be extended, if we want to add more information to it. Original discussion: http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20151123/143961.html and http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20151130/144002.html Original-original discussion (very old, ~2 years ago, when the idea first came up): http://lists.llvm.org/pipermail/cfe-commits/Week-of-Mon-20131216/095565.html Artem Dergachev has commented that a similar hook allowing to add multiple transitions using a CheckerContext might be more favourable: > At a glance, I wonder if it's worth it to provide a CheckerContext > inside this callback and then handle transitions properly (which would > allow the checker to split the program state at the very beginning of > the function). I cannot instantly think of a use-case (hmm, maybe > somebody would like to eagerly discriminate between a NULL and non-NULL > pointer argument of the function), but at the same time I don't see any > obvious problems with adding it, especially because it'd be hard to > change the API when the use-case appears. That wasn't a use case I had in mind, but it might be a good idea. That would serve a more general function, and I'm thinking that its interface would look something like: ``` void checkEntryPoint(const Decl *D, CheckerContext &Context) const; ``` Other thoughts? http://reviews.llvm.org/D15090 Files: include/clang/StaticAnalyzer/Core/Checker.h include/clang/StaticAnalyzer/Core/CheckerManager.h include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointInfo.h lib/StaticAnalyzer/Core/CheckerManager.cpp lib/StaticAnalyzer/Core/CoreEngine.cpp lib/StaticAnalyzer/Core/ExprEngine.cpp
Index: lib/StaticAnalyzer/Core/ExprEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/ExprEngine.cpp +++ lib/StaticAnalyzer/Core/ExprEngine.cpp @@ -176,7 +176,7 @@ } } - return state; + return getCheckerManager().runCheckersForInitialState(D, state); } ProgramStateRef Index: lib/StaticAnalyzer/Core/CoreEngine.cpp =================================================================== --- lib/StaticAnalyzer/Core/CoreEngine.cpp +++ lib/StaticAnalyzer/Core/CoreEngine.cpp @@ -191,11 +191,21 @@ // Set the current block counter to being empty. WList->setBlockCounter(BCounterFactory.GetEmptyCounter()); - if (!InitState) - // Generate the root. - generateNode(StartLoc, SubEng.getInitialState(L), nullptr); - else - generateNode(StartLoc, InitState, nullptr); + if (!InitState) { + InitState = SubEng.getInitialState(L); + + // it's possible for a checker to return NULL state, in which case getInitialState() + // above will return NULL - this means that we don't want this code path checked; + // return early + // TODO: correct? (seems to work) + if (!InitState) { + SubEng.processEndWorklist(hasWorkRemaining()); + return WList->hasWork(); + } + } + + // Generate the root. + generateNode(StartLoc, InitState, nullptr); } // Check if we have a steps limit Index: lib/StaticAnalyzer/Core/CheckerManager.cpp =================================================================== --- lib/StaticAnalyzer/Core/CheckerManager.cpp +++ lib/StaticAnalyzer/Core/CheckerManager.cpp @@ -11,6 +11,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/StaticAnalyzer/Core/PathSensitive/EntryPointInfo.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" #include "clang/AST/DeclBase.h" #include "clang/Analysis/ProgramPoint.h" @@ -36,7 +37,8 @@ !DeadSymbolsCheckers.empty() || !RegionChangesCheckers.empty() || !EvalAssumeCheckers.empty() || - !EvalCallCheckers.empty(); + !EvalCallCheckers.empty() || + !InitialStateCheckers.empty(); } void CheckerManager::finishedCheckerRegistration() { @@ -611,6 +613,18 @@ I->second->printState(Out, State, NL, Sep); } +ProgramStateRef +CheckerManager::runCheckersForInitialState(const Decl *D, ProgramStateRef State) { + EntryPointInfo EntryInfo(D, State); + + for (unsigned i = 0, e = InitialStateCheckers.size(); i != e; ++i) { + if (!EntryInfo.State) return nullptr; // exit early in case of unfeasible state [TODO: revise] + EntryInfo.State = InitialStateCheckers[i](EntryInfo); + } + + return EntryInfo.State; +} + //===----------------------------------------------------------------------===// // Internal registration functions for AST traversing. //===----------------------------------------------------------------------===// @@ -716,6 +730,10 @@ EndOfTranslationUnitCheckers.push_back(checkfn); } +void CheckerManager::_registerForInitialState(CheckInitialStateFunc checkfn) { + InitialStateCheckers.push_back(checkfn); +} + //===----------------------------------------------------------------------===// // Implementation details. //===----------------------------------------------------------------------===// Index: include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointInfo.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointInfo.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/EntryPointInfo.h @@ -0,0 +1,50 @@ +//== EntryPointInfo.h - Entry point information ------------*- C++ -*--=// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +// This file defines EntryPointInfo. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_ENTRYPOINTINFO_H +#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_ENTRYPOINTINFO_H + +#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h" +#include "llvm/Support/Casting.h" + +namespace clang { +class Decl; +class FunctionDecl; + +namespace ento { + +class EntryPointInfo { + const Decl *D; + ProgramStateRef State; + + friend class CheckerManager; + +public: + explicit EntryPointInfo(const Decl* D_, ProgramStateRef S_) : D(D_), State(S_) {} + + const ProgramStateRef &getState() const { return State; } + + const Decl *getDecl() const { return D; } + + template <typename TDecl> + const TDecl *getDeclAs() const { return llvm::dyn_cast<TDecl>(D); } + + const FunctionDecl *getDeclAsFunction() const { + return getDeclAs<FunctionDecl>(); + } +}; + +} // end namespace enzo +} // end namespace clang + +#endif Index: include/clang/StaticAnalyzer/Core/CheckerManager.h =================================================================== --- include/clang/StaticAnalyzer/Core/CheckerManager.h +++ include/clang/StaticAnalyzer/Core/CheckerManager.h @@ -44,6 +44,7 @@ struct NodeBuilderContext; class MemRegion; class SymbolReaper; + class EntryPointInfo; template <typename T> class CheckerFn; @@ -387,6 +388,14 @@ void runCheckersForPrintState(raw_ostream &Out, ProgramStateRef State, const char *NL, const char *Sep); + /// \brief Run checkers for manipulating the initial program state at the + /// start of the analysis. + /// + /// \param D AST node of entry point + /// \param State Initial program state + /// \returns Checkers can modify the state by returning a new one. + ProgramStateRef runCheckersForInitialState(const Decl *D, ProgramStateRef State); + //===----------------------------------------------------------------------===// // Internal registration functions for AST traversing. //===----------------------------------------------------------------------===// @@ -464,6 +473,9 @@ AnalysisManager&, BugReporter &)> CheckEndOfTranslationUnit; + typedef CheckerFn<ProgramStateRef (const EntryPointInfo &)> + CheckInitialStateFunc; + typedef bool (*HandlesStmtFunc)(const Stmt *D); void _registerForPreStmt(CheckStmtFunc checkfn, HandlesStmtFunc isForStmtFn); @@ -505,6 +517,8 @@ void _registerForEndOfTranslationUnit(CheckEndOfTranslationUnit checkfn); + void _registerForInitialState(CheckInitialStateFunc checkfn); + //===----------------------------------------------------------------------===// // Internal registration functions for events. //===----------------------------------------------------------------------===// @@ -615,6 +629,8 @@ std::vector<CheckEndOfTranslationUnit> EndOfTranslationUnitCheckers; + std::vector<CheckInitialStateFunc> InitialStateCheckers; + struct EventInfo { SmallVector<CheckEventFunc, 4> Checkers; bool HasDispatcher; Index: include/clang/StaticAnalyzer/Core/Checker.h =================================================================== --- include/clang/StaticAnalyzer/Core/Checker.h +++ include/clang/StaticAnalyzer/Core/Checker.h @@ -22,6 +22,7 @@ namespace clang { namespace ento { class BugReporter; + class EntryPointInfo; namespace check { @@ -426,6 +427,19 @@ } }; +class InitialState { + template <typename CHECKER> + static ProgramStateRef _checkInitialState(void *checker, const EntryPointInfo &info) { + return ((CHECKER *)checker)->checkInitialState(info); + } + +public: + template <typename CHECKER> + static void _register(CHECKER *checker, CheckerManager &mgr) { + mgr._registerForInitialState(CheckerManager::CheckInitialStateFunc(checker, _checkInitialState<CHECKER>)); + } +}; + } // end check namespace namespace eval {
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits