NoQ created this revision. NoQ added reviewers: dcoughlin, xazax.hun, a.sidorin, george.karpenkov, szepet, rnkovacs. Herald added subscribers: cfe-commits, mikhail.ramalho, baloghadamsoftware.
These methods allow reasoning about the stack frame that will be (or was) used when the function will be (or was) inlined, even if it will not be (or was not) inlined. They are implemented in a fairly ugly manner, and while i plan to address some of the issues in follow-up patches (at least, do something about virtual calls), i don't think it'll work perfectly soon. A proper implementation would require a large amount of refactoring for `Call::getDecl()` and `Call::getRuntimeDefinition()` and the logic in `ExprEngine`'s call modeling and i didn't accomplish much in a couple days of working on it, so i'm putting a proper implementation on hold for now, safely returning `nullptr` when the proper `Decl` cannot be reliably obtained. This was ultimately necessary to obtain the parameter regions on the future stack frame for the purposes of https://reviews.llvm.org/D49443. We don't need this function to be perfect because a lot of other things are imperfect and we're fine with falling back to a conservative behavior. Repository: rC Clang https://reviews.llvm.org/D49715 Files: 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: lib/StaticAnalyzer/Core/CallEvent.cpp =================================================================== --- lib/StaticAnalyzer/Core/CallEvent.cpp +++ 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,67 @@ 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(); + // We cannot construct a stack frame without an ADC. + if (!ADC) + return nullptr; + + const Expr *E = getOriginExpr(); + // We cannot lookup a CFG element without an origin-expression. + if (!E) + return nullptr; + + // Recover CFG block via reverse lookup. Maybe it should just be a part of the + // CallEvent object? That would have been convenient. + 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) { Index: include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h +++ include/clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h @@ -773,13 +773,15 @@ const ConstructionContextItem &Item, const LocationContext *LC); +public: /// 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); +private: /// 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: include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h =================================================================== --- include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ 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,36 @@ /// \p D must not be null. static bool isVariadic(const Decl *D); + /// Returns the AnalysisDeclContext for the callee stack frame. For now, + /// this is an unreliable, best-effort crystal ball function that may fail + /// for bad reasons, returning null, even in post-call of an inlined function. + AnalysisDeclContext *getCalleeAnalysisDeclContext() const; + + /// Returns the callee stack frame. That stack frame will only be entered + /// during analysis if the function is inlined, but it may still be useful + /// in intermediate calculations. For now, this is an unreliable, best-effort + /// crystal ball function that may fail for bad reasons, returning null, + /// even in post-call of an inlined function. + const StackFrameContext *getCalleeStackFrame() const; + + /// Returns memory location for a parameter variable within the callee stack + /// frame. For now, this is an unreliable, best-effort crystal ball function + /// that may fail for bad reasons, returning null, even in post-call of + /// an inlined function. + 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(); + } + // Iterator access to formal parameters and their types. private: struct GetTypeFn { Index: include/clang/Analysis/ConstructionContext.h =================================================================== --- include/clang/Analysis/ConstructionContext.h +++ include/clang/Analysis/ConstructionContext.h @@ -98,6 +98,13 @@ 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) {}
_______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits