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

Reply via email to