Skip to content

Commit

Permalink
[analyzer] CallEvent: Add helper methods for obtaining the callee sta…
Browse files Browse the repository at this point in the history
…ck 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

llvm-svn: 338474
  • Loading branch information
haoNoQ committed Aug 1, 2018
1 parent c4581f4 commit b21b479
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 7 deletions.
7 changes: 7 additions & 0 deletions clang/include/clang/Analysis/ConstructionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,13 @@ class ConstructionContextItem {
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) {}

Expand Down
41 changes: 41 additions & 0 deletions clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -404,6 +405,46 @@ class CallEvent {
/// \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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -581,6 +581,14 @@ class ExprEngine : public SubEngine {
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.
Expand Down Expand Up @@ -773,13 +781,6 @@ class ExprEngine : public SubEngine {
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.
Expand Down
63 changes: 63 additions & 0 deletions clang/lib/StaticAnalyzer/Core/CallEvent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit b21b479

Please sign in to comment.