]> granicus.if.org Git - clang/commitdiff
[analyzer] CallEvent: Add helper methods for obtaining the callee stack frame.
authorArtem Dergachev <artem.dergachev@gmail.com>
Wed, 1 Aug 2018 01:58:15 +0000 (01:58 +0000)
committerArtem Dergachev <artem.dergachev@gmail.com>
Wed, 1 Aug 2018 01:58:15 +0000 (01:58 +0000)
Newly added methods allow reasoning about the stack frame of the call (as
opposed to the stack frame on which the call was made, which was always
available) - obtain the stack frame context, obtain parameter regions - even if
the call is not going to be (or was not) inlined, i.e. even if the analysis
has never actually entered the stack frame.

Differential Revision: https://reviews.llvm.org/D49715

git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@338474 91177308-0d34-0410-b5e6-96231b3b80d8

include/clang/Analysis/ConstructionContext.h
include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
lib/StaticAnalyzer/Core/CallEvent.cpp

index 2724c0f68b987a80f76e4e32c5620f9929620272..48e0540803af7e4b280d0e514c270d675d7c7df2 100644 (file)
@@ -112,6 +112,13 @@ public:
   ConstructionContextItem(const ObjCMessageExpr *ME, unsigned Index)
       : Data(ME), Kind(ArgumentKind), Index(Index) {}
 
+  // A polymorphic version of the previous calls with dynamic type check.
+  ConstructionContextItem(const Expr *E, unsigned Index)
+      : Data(E), Kind(ArgumentKind), Index(Index) {
+    assert(isa<CallExpr>(E) || isa<CXXConstructExpr>(E) ||
+           isa<ObjCMessageExpr>(E));
+  }
+
   ConstructionContextItem(const CXXCtorInitializer *Init)
       : Data(Init), Kind(InitializerKind), Index(0) {}
 
index 68c50eb57beb327ccfaad6e4888e8d98d4ad0cbd..9078fb94d2821db0754c053439250ac5c6adfdfc 100644 (file)
@@ -29,6 +29,7 @@
 #include "clang/Basic/LLVM.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
 #include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
@@ -404,6 +405,46 @@ public:
   /// \p D must not be null.
   static bool isVariadic(const Decl *D);
 
+  /// Returns AnalysisDeclContext for the callee stack frame.
+  /// Currently may fail; returns null on failure.
+  AnalysisDeclContext *getCalleeAnalysisDeclContext() const;
+
+  /// Returns the callee stack frame. That stack frame will only be entered
+  /// during analysis if the call is inlined, but it may still be useful
+  /// in intermediate calculations even if the call isn't inlined.
+  /// May fail; returns null on failure.
+  const StackFrameContext *getCalleeStackFrame() const;
+
+  /// Returns memory location for a parameter variable within the callee stack
+  /// frame. May fail; returns null on failure.
+  const VarRegion *getParameterLocation(unsigned Index) const;
+
+  /// Returns true if on the current path, the argument was constructed by
+  /// calling a C++ constructor over it. This is an internal detail of the
+  /// analysis which doesn't necessarily represent the program semantics:
+  /// if we are supposed to construct an argument directly, we may still
+  /// not do that because we don't know how (i.e., construction context is
+  /// unavailable in the CFG or not supported by the analyzer).
+  bool isArgumentConstructedDirectly(unsigned Index) const {
+    // This assumes that the object was not yet removed from the state.
+    return ExprEngine::getObjectUnderConstruction(
+        getState(), {getOriginExpr(), Index}, getCalleeStackFrame()).hasValue();
+  }
+
+  /// Some calls have parameter numbering mismatched from argument numbering.
+  /// This function converts an argument index to the corresponding
+  /// parameter index. Returns None is the argument doesn't correspond
+  /// to any parameter variable.
+  Optional<unsigned> getAdjustedParameterIndex(unsigned ArgumentIndex) const {
+    if (dyn_cast_or_null<CXXOperatorCallExpr>(getOriginExpr()) &&
+        dyn_cast_or_null<CXXMethodDecl>(getDecl())) {
+      // For member operator calls argument 0 on the expression corresponds
+      // to implicit this-parameter on the declaration.
+      return (ArgumentIndex > 0) ? Optional<unsigned>(ArgumentIndex - 1) : None;
+    }
+    return ArgumentIndex;
+  }
+
   // Iterator access to formal parameters and their types.
 private:
   struct GetTypeFn {
index e2de1d634ab3e53d36d28da9438d744bef9901bf..06a90fa847a6f975f531ed45270a9190e47c48b5 100644 (file)
@@ -581,6 +581,14 @@ public:
     return svalBuilder.evalBinOp(ST, Op, LHS, RHS, T);
   }
 
+  /// By looking at a certain item that may be potentially part of an object's
+  /// ConstructionContext, retrieve such object's location. A particular
+  /// statement can be transparently passed as \p Item in most cases.
+  static Optional<SVal>
+  getObjectUnderConstruction(ProgramStateRef State,
+                             const ConstructionContextItem &Item,
+                             const LocationContext *LC);
+
 protected:
   /// evalBind - Handle the semantics of binding a value to a specific location.
   ///  This method is used by evalStore, VisitDeclStmt, and others.
@@ -773,13 +781,6 @@ private:
                            const ConstructionContextItem &Item,
                            const LocationContext *LC);
 
-  /// If the given statement corresponds to an object under construction,
-  /// being part of its construciton context, retrieve that object's location.
-  static Optional<SVal>
-  getObjectUnderConstruction(ProgramStateRef State,
-                             const ConstructionContextItem &Item,
-                             const LocationContext *LC);
-
   /// If the given expression corresponds to a temporary that was used for
   /// passing into an elidable copy/move constructor and that constructor
   /// was actually elided, track that we also need to elide the destructor.
index 8db7b06f186dada5235c986a47f1ba1f37255006..fe9260e32dd8bc479e51e675f0003fc519023c2e 100644 (file)
@@ -27,6 +27,7 @@
 #include "clang/AST/Type.h"
 #include "clang/Analysis/AnalysisDeclContext.h"
 #include "clang/Analysis/CFG.h"
+#include "clang/Analysis/CFGStmtMap.h"
 #include "clang/Analysis/ProgramPoint.h"
 #include "clang/CrossTU/CrossTranslationUnit.h"
 #include "clang/Basic/IdentifierTable.h"
@@ -166,6 +167,68 @@ bool CallEvent::isGlobalCFunction(StringRef FunctionName) const {
   return CheckerContext::isCLibraryFunction(FD, FunctionName);
 }
 
+AnalysisDeclContext *CallEvent::getCalleeAnalysisDeclContext() const {
+  const Decl *D = getDecl();
+
+  // If the callee is completely unknown, we cannot construct the stack frame.
+  if (!D)
+    return nullptr;
+
+  // FIXME: Skip virtual functions for now. There's no easy procedure to foresee
+  // the exact decl that should be used, especially when it's not a definition.
+  if (const Decl *RD = getRuntimeDefinition().getDecl())
+    if (RD != D)
+      return nullptr;
+
+  return LCtx->getAnalysisDeclContext()->getManager()->getContext(D);
+}
+
+const StackFrameContext *CallEvent::getCalleeStackFrame() const {
+  AnalysisDeclContext *ADC = getCalleeAnalysisDeclContext();
+  if (!ADC)
+    return nullptr;
+
+  const Expr *E = getOriginExpr();
+  if (!E)
+    return nullptr;
+
+  // Recover CFG block via reverse lookup.
+  // TODO: If we were to keep CFG element information as part of the CallEvent
+  // instead of doing this reverse lookup, we would be able to build the stack
+  // frame for non-expression-based calls, and also we wouldn't need the reverse
+  // lookup.
+  CFGStmtMap *Map = LCtx->getAnalysisDeclContext()->getCFGStmtMap();
+  const CFGBlock *B = Map->getBlock(E);
+  assert(B);
+
+  // Also recover CFG index by scanning the CFG block.
+  unsigned Idx = 0, Sz = B->size();
+  for (; Idx < Sz; ++Idx)
+    if (auto StmtElem = (*B)[Idx].getAs<CFGStmt>())
+      if (StmtElem->getStmt() == E)
+        break;
+  assert(Idx < Sz);
+
+  return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, Idx);
+}
+
+const VarRegion *CallEvent::getParameterLocation(unsigned Index) const {
+  const StackFrameContext *SFC = getCalleeStackFrame();
+  // We cannot construct a VarRegion without a stack frame.
+  if (!SFC)
+    return nullptr;
+
+  const ParmVarDecl *PVD = parameters()[Index];
+  const VarRegion *VR =
+      State->getStateManager().getRegionManager().getVarRegion(PVD, SFC);
+
+  // This sanity check would fail if our parameter declaration doesn't
+  // correspond to the stack frame's function declaration.
+  assert(VR->getStackFrame() == SFC);
+
+  return VR;
+}
+
 /// Returns true if a type is a pointer-to-const or reference-to-const
 /// with no further indirection.
 static bool isPointerToConst(QualType Ty) {