summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorArtem Dergachev <artem.dergachev@gmail.com>2018-08-01 01:58:15 +0000
committerArtem Dergachev <artem.dergachev@gmail.com>2018-08-01 01:58:15 +0000
commit726e1d4f31118950407db6af4bcc4c980d9726cb (patch)
tree7b3a3a2775843fc8d34caa057e78b62fa70679b2
parent0c285b370c0599a9665201ad3195410c190566d9 (diff)
[analyzer] CallEvent: Add helper methods for obtaining the callee stack frame.
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
-rw-r--r--include/clang/Analysis/ConstructionContext.h7
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h41
-rw-r--r--include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h15
-rw-r--r--lib/StaticAnalyzer/Core/CallEvent.cpp63
4 files changed, 119 insertions, 7 deletions
diff --git a/include/clang/Analysis/ConstructionContext.h b/include/clang/Analysis/ConstructionContext.h
index 2724c0f68b..48e0540803 100644
--- a/include/clang/Analysis/ConstructionContext.h
+++ b/include/clang/Analysis/ConstructionContext.h
@@ -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) {}
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
index 68c50eb57b..9078fb94d2 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -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 {
diff --git a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
index e2de1d634a..06a90fa847 100644
--- a/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
+++ b/include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h
@@ -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.
diff --git a/lib/StaticAnalyzer/Core/CallEvent.cpp b/lib/StaticAnalyzer/Core/CallEvent.cpp
index 8db7b06f18..fe9260e32d 100644
--- a/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -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) {