summaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'clang/lib/StaticAnalyzer/Core/CallEvent.cpp')
-rw-r--r--clang/lib/StaticAnalyzer/Core/CallEvent.cpp363
1 files changed, 187 insertions, 176 deletions
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 168d6fe6ec48f..78d13ddfb773c 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -172,23 +172,9 @@ AnalysisDeclContext *CallEvent::getCalleeAnalysisDeclContext() const {
if (!D)
return nullptr;
- // TODO: For now we skip functions without definitions, even if we have
- // our own getDecl(), because it's hard to find out which re-declaration
- // is going to be used, and usually clients don't really care about this
- // situation because there's a loss of precision anyway because we cannot
- // inline the call.
- RuntimeDefinition RD = getRuntimeDefinition();
- if (!RD.getDecl())
- return nullptr;
-
AnalysisDeclContext *ADC =
LCtx->getAnalysisDeclContext()->getManager()->getContext(D);
- // TODO: For now we skip virtual functions, because this also rises
- // the problem of which decl to use, but now it's across different classes.
- if (RD.mayHaveOtherDefinitions() || RD.getDecl() != ADC->getDecl())
- return nullptr;
-
return ADC;
}
@@ -222,39 +208,17 @@ CallEvent::getCalleeStackFrame(unsigned BlockCount) const {
return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, BlockCount, Idx);
}
-const VarRegion *CallEvent::getParameterLocation(unsigned Index,
- unsigned BlockCount) const {
+const ParamVarRegion
+*CallEvent::getParameterLocation(unsigned Index, unsigned BlockCount) const {
const StackFrameContext *SFC = getCalleeStackFrame(BlockCount);
// We cannot construct a VarRegion without a stack frame.
if (!SFC)
return nullptr;
- // Retrieve parameters of the definition, which are different from
- // CallEvent's parameters() because getDecl() isn't necessarily
- // the definition. SFC contains the definition that would be used
- // during analysis.
- const Decl *D = SFC->getDecl();
-
- // TODO: Refactor into a virtual method of CallEvent, like parameters().
- const ParmVarDecl *PVD = nullptr;
- if (const auto *FD = dyn_cast<FunctionDecl>(D))
- PVD = FD->parameters()[Index];
- else if (const auto *BD = dyn_cast<BlockDecl>(D))
- PVD = BD->parameters()[Index];
- else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
- PVD = MD->parameters()[Index];
- else if (const auto *CD = dyn_cast<CXXConstructorDecl>(D))
- PVD = CD->parameters()[Index];
- assert(PVD && "Unexpected Decl kind!");
-
- 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;
+ const ParamVarRegion *PVR =
+ State->getStateManager().getRegionManager().getParamVarRegion(
+ getOriginExpr(), Index, SFC);
+ return PVR;
}
/// Returns true if a type is a pointer-to-const or reference-to-const
@@ -325,8 +289,9 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
if (getKind() != CE_CXXAllocator)
if (isArgumentConstructedDirectly(Idx))
if (auto AdjIdx = getAdjustedParameterIndex(Idx))
- if (const VarRegion *VR = getParameterLocation(*AdjIdx, BlockCount))
- ValuesToInvalidate.push_back(loc::MemRegionVal(VR));
+ if (const TypedValueRegion *TVR =
+ getParameterLocation(*AdjIdx, BlockCount))
+ ValuesToInvalidate.push_back(loc::MemRegionVal(TVR));
}
// Invalidate designated regions using the batch invalidation API.
@@ -450,8 +415,7 @@ void CallEvent::dump(raw_ostream &Out) const {
return;
}
- // FIXME: a string representation of the kind would be nice.
- Out << "Unknown call (type " << getKind() << ")";
+ Out << "Unknown call (type " << getKindAsString() << ")";
}
bool CallEvent::isCallStmt(const Stmt *S) {
@@ -515,8 +479,7 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
unsigned Idx = 0;
ArrayRef<ParmVarDecl*>::iterator I = parameters.begin(), E = parameters.end();
for (; I != E && Idx < NumArgs; ++I, ++Idx) {
- const ParmVarDecl *ParamDecl = *I;
- assert(ParamDecl && "Formal parameter has no decl?");
+ assert(*I && "Formal parameter has no decl?");
// TODO: Support allocator calls.
if (Call.getKind() != CE_CXXAllocator)
@@ -528,7 +491,8 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
// which makes getArgSVal() fail and return UnknownVal.
SVal ArgVal = Call.getArgSVal(Idx);
if (!ArgVal.isUnknown()) {
- Loc ParamLoc = SVB.makeLoc(MRMgr.getVarRegion(ParamDecl, CalleeCtx));
+ Loc ParamLoc = SVB.makeLoc(
+ MRMgr.getParamVarRegion(Call.getOriginExpr(), Idx, CalleeCtx));
Bindings.push_back(std::make_pair(ParamLoc, ArgVal));
}
}
@@ -536,6 +500,37 @@ static void addParameterValuesToBindings(const StackFrameContext *CalleeCtx,
// FIXME: Variadic arguments are not handled at all right now.
}
+const ConstructionContext *CallEvent::getConstructionContext() const {
+ const StackFrameContext *StackFrame = getCalleeStackFrame(0);
+ if (!StackFrame)
+ return nullptr;
+
+ const CFGElement Element = StackFrame->getCallSiteCFGElement();
+ if (const auto Ctor = Element.getAs<CFGConstructor>()) {
+ return Ctor->getConstructionContext();
+ }
+
+ if (const auto RecCall = Element.getAs<CFGCXXRecordTypedCall>()) {
+ return RecCall->getConstructionContext();
+ }
+
+ return nullptr;
+}
+
+Optional<SVal>
+CallEvent::getReturnValueUnderConstruction() const {
+ const auto *CC = getConstructionContext();
+ if (!CC)
+ return None;
+
+ EvalCallOptions CallOpts;
+ ExprEngine &Engine = getState()->getStateManager().getOwningEngine();
+ SVal RetVal =
+ Engine.computeObjectUnderConstruction(getOriginExpr(), getState(),
+ getLocationContext(), CC, CallOpts);
+ return RetVal;
+}
+
ArrayRef<ParmVarDecl*> AnyFunctionCall::parameters() const {
const FunctionDecl *D = getDecl();
if (!D)
@@ -565,7 +560,7 @@ RuntimeDefinition AnyFunctionCall::getRuntimeDefinition() const {
return RuntimeDefinition(Decl);
}
- SubEngine &Engine = getState()->getStateManager().getOwningEngine();
+ ExprEngine &Engine = getState()->getStateManager().getOwningEngine();
AnalyzerOptions &Opts = Engine.getAnalysisManager().options;
// Try to get CTU definition only if CTUDir is provided.
@@ -889,24 +884,22 @@ void BlockCall::getInitialStackFrameContents(const StackFrameContext *CalleeCtx,
Params);
}
-SVal CXXConstructorCall::getCXXThisVal() const {
+SVal AnyCXXConstructorCall::getCXXThisVal() const {
if (Data)
return loc::MemRegionVal(static_cast<const MemRegion *>(Data));
return UnknownVal();
}
-void CXXConstructorCall::getExtraInvalidatedValues(ValueList &Values,
+void AnyCXXConstructorCall::getExtraInvalidatedValues(ValueList &Values,
RegionAndSymbolInvalidationTraits *ETraits) const {
- if (Data) {
- loc::MemRegionVal MV(static_cast<const MemRegion *>(Data));
- if (SymbolRef Sym = MV.getAsSymbol(true))
- ETraits->setTrait(Sym,
- RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
- Values.push_back(MV);
- }
+ SVal V = getCXXThisVal();
+ if (SymbolRef Sym = V.getAsSymbol(true))
+ ETraits->setTrait(Sym,
+ RegionAndSymbolInvalidationTraits::TK_SuppressEscape);
+ Values.push_back(V);
}
-void CXXConstructorCall::getInitialStackFrameContents(
+void AnyCXXConstructorCall::getInitialStackFrameContents(
const StackFrameContext *CalleeCtx,
BindingsTy &Bindings) const {
AnyFunctionCall::getInitialStackFrameContents(CalleeCtx, Bindings);
@@ -920,6 +913,14 @@ void CXXConstructorCall::getInitialStackFrameContents(
}
}
+const StackFrameContext *
+CXXInheritedConstructorCall::getInheritingStackFrame() const {
+ const StackFrameContext *SFC = getLocationContext()->getStackFrame();
+ while (isa<CXXInheritedCtorInitExpr>(SFC->getCallSite()))
+ SFC = SFC->getParent()->getStackFrame();
+ return SFC;
+}
+
SVal CXXDestructorCall::getCXXThisVal() const {
if (Data)
return loc::MemRegionVal(DtorDataTy::getFromOpaqueValue(Data).getPointer());
@@ -967,14 +968,6 @@ void ObjCMethodCall::getExtraInvalidatedValues(
Values.push_back(getReceiverSVal());
}
-SVal ObjCMethodCall::getSelfSVal() const {
- const LocationContext *LCtx = getLocationContext();
- const ImplicitParamDecl *SelfDecl = LCtx->getSelfDecl();
- if (!SelfDecl)
- return SVal();
- return getState()->getSVal(getState()->getRegion(SelfDecl, LCtx));
-}
-
SVal ObjCMethodCall::getReceiverSVal() const {
// FIXME: Is this the best way to handle class receivers?
if (!isInstanceMessage())
@@ -986,7 +979,7 @@ SVal ObjCMethodCall::getReceiverSVal() const {
// An instance message with no expression means we are sending to super.
// In this case the object reference is the same as 'self'.
assert(getOriginExpr()->getReceiverKind() == ObjCMessageExpr::SuperInstance);
- SVal SelfVal = getSelfSVal();
+ SVal SelfVal = getState()->getSelfSVal(getLocationContext());
assert(SelfVal.isValid() && "Calling super but not in ObjC method");
return SelfVal;
}
@@ -1000,8 +993,9 @@ bool ObjCMethodCall::isReceiverSelfOrSuper() const {
return false;
SVal RecVal = getSVal(getOriginExpr()->getInstanceReceiver());
+ SVal SelfVal = getState()->getSelfSVal(getLocationContext());
- return (RecVal == getSelfSVal());
+ return (RecVal == SelfVal);
}
SourceRange ObjCMethodCall::getSourceRange() const {
@@ -1168,23 +1162,77 @@ static const ObjCMethodDecl *findDefiningRedecl(const ObjCMethodDecl *MD) {
return MD;
}
-static bool isCallToSelfClass(const ObjCMessageExpr *ME) {
- const Expr* InstRec = ME->getInstanceReceiver();
- if (!InstRec)
- return false;
- const auto *InstRecIg = dyn_cast<DeclRefExpr>(InstRec->IgnoreParenImpCasts());
+struct PrivateMethodKey {
+ const ObjCInterfaceDecl *Interface;
+ Selector LookupSelector;
+ bool IsClassMethod;
+};
- // Check that receiver is called 'self'.
- if (!InstRecIg || !InstRecIg->getFoundDecl() ||
- !InstRecIg->getFoundDecl()->getName().equals("self"))
- return false;
+namespace llvm {
+template <> struct DenseMapInfo<PrivateMethodKey> {
+ using InterfaceInfo = DenseMapInfo<const ObjCInterfaceDecl *>;
+ using SelectorInfo = DenseMapInfo<Selector>;
- // Check that the method name is 'class'.
- if (ME->getSelector().getNumArgs() != 0 ||
- !ME->getSelector().getNameForSlot(0).equals("class"))
- return false;
+ static inline PrivateMethodKey getEmptyKey() {
+ return {InterfaceInfo::getEmptyKey(), SelectorInfo::getEmptyKey(), false};
+ }
- return true;
+ static inline PrivateMethodKey getTombstoneKey() {
+ return {InterfaceInfo::getTombstoneKey(), SelectorInfo::getTombstoneKey(),
+ true};
+ }
+
+ static unsigned getHashValue(const PrivateMethodKey &Key) {
+ return llvm::hash_combine(
+ llvm::hash_code(InterfaceInfo::getHashValue(Key.Interface)),
+ llvm::hash_code(SelectorInfo::getHashValue(Key.LookupSelector)),
+ Key.IsClassMethod);
+ }
+
+ static bool isEqual(const PrivateMethodKey &LHS,
+ const PrivateMethodKey &RHS) {
+ return InterfaceInfo::isEqual(LHS.Interface, RHS.Interface) &&
+ SelectorInfo::isEqual(LHS.LookupSelector, RHS.LookupSelector) &&
+ LHS.IsClassMethod == RHS.IsClassMethod;
+ }
+};
+} // end namespace llvm
+
+static const ObjCMethodDecl *
+lookupRuntimeDefinition(const ObjCInterfaceDecl *Interface,
+ Selector LookupSelector, bool InstanceMethod) {
+ // Repeatedly calling lookupPrivateMethod() is expensive, especially
+ // when in many cases it returns null. We cache the results so
+ // that repeated queries on the same ObjCIntefaceDecl and Selector
+ // don't incur the same cost. On some test cases, we can see the
+ // same query being issued thousands of times.
+ //
+ // NOTE: This cache is essentially a "global" variable, but it
+ // only gets lazily created when we get here. The value of the
+ // cache probably comes from it being global across ExprEngines,
+ // where the same queries may get issued. If we are worried about
+ // concurrency, or possibly loading/unloading ASTs, etc., we may
+ // need to revisit this someday. In terms of memory, this table
+ // stays around until clang quits, which also may be bad if we
+ // need to release memory.
+ using PrivateMethodCache =
+ llvm::DenseMap<PrivateMethodKey, Optional<const ObjCMethodDecl *>>;
+
+ static PrivateMethodCache PMC;
+ Optional<const ObjCMethodDecl *> &Val =
+ PMC[{Interface, LookupSelector, InstanceMethod}];
+
+ // Query lookupPrivateMethod() if the cache does not hit.
+ if (!Val.hasValue()) {
+ Val = Interface->lookupPrivateMethod(LookupSelector, InstanceMethod);
+
+ if (!*Val) {
+ // Query 'lookupMethod' as a backup.
+ Val = Interface->lookupMethod(LookupSelector, InstanceMethod);
+ }
+ }
+
+ return Val.getValue();
}
RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
@@ -1194,8 +1242,9 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
if (E->isInstanceMessage()) {
// Find the receiver type.
- const ObjCObjectPointerType *ReceiverT = nullptr;
+ const ObjCObjectType *ReceiverT = nullptr;
bool CanBeSubClassed = false;
+ bool LookingForInstanceMethod = true;
QualType SupersType = E->getSuperType();
const MemRegion *Receiver = nullptr;
@@ -1203,7 +1252,7 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
// The receiver is guaranteed to be 'super' in this case.
// Super always means the type of immediate predecessor to the method
// where the call occurs.
- ReceiverT = cast<ObjCObjectPointerType>(SupersType);
+ ReceiverT = cast<ObjCObjectPointerType>(SupersType)->getObjectType();
} else {
Receiver = getReceiverSVal().getAsRegion();
if (!Receiver)
@@ -1218,100 +1267,59 @@ RuntimeDefinition ObjCMethodCall::getRuntimeDefinition() const {
QualType DynType = DTI.getType();
CanBeSubClassed = DTI.canBeASubClass();
- ReceiverT = dyn_cast<ObjCObjectPointerType>(DynType.getCanonicalType());
- if (ReceiverT && CanBeSubClassed)
- if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl())
- if (!canBeOverridenInSubclass(IDecl, Sel))
- CanBeSubClassed = false;
- }
+ const auto *ReceiverDynT =
+ dyn_cast<ObjCObjectPointerType>(DynType.getCanonicalType());
+
+ if (ReceiverDynT) {
+ ReceiverT = ReceiverDynT->getObjectType();
- // Handle special cases of '[self classMethod]' and
- // '[[self class] classMethod]', which are treated by the compiler as
- // instance (not class) messages. We will statically dispatch to those.
- if (auto *PT = dyn_cast_or_null<ObjCObjectPointerType>(ReceiverT)) {
- // For [self classMethod], return the compiler visible declaration.
- if (PT->getObjectType()->isObjCClass() &&
- Receiver == getSelfSVal().getAsRegion())
- return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
-
- // Similarly, handle [[self class] classMethod].
- // TODO: We are currently doing a syntactic match for this pattern with is
- // limiting as the test cases in Analysis/inlining/InlineObjCClassMethod.m
- // shows. A better way would be to associate the meta type with the symbol
- // using the dynamic type info tracking and use it here. We can add a new
- // SVal for ObjC 'Class' values that know what interface declaration they
- // come from. Then 'self' in a class method would be filled in with
- // something meaningful in ObjCMethodCall::getReceiverSVal() and we could
- // do proper dynamic dispatch for class methods just like we do for
- // instance methods now.
- if (E->getInstanceReceiver())
- if (const auto *M = dyn_cast<ObjCMessageExpr>(E->getInstanceReceiver()))
- if (isCallToSelfClass(M))
+ // It can be actually class methods called with Class object as a
+ // receiver. This type of messages is treated by the compiler as
+ // instance (not class).
+ if (ReceiverT->isObjCClass()) {
+
+ SVal SelfVal = getState()->getSelfSVal(getLocationContext());
+ // For [self classMethod], return compiler visible declaration.
+ if (Receiver == SelfVal.getAsRegion()) {
return RuntimeDefinition(findDefiningRedecl(E->getMethodDecl()));
+ }
+
+ // Otherwise, let's check if we know something about the type
+ // inside of this class object.
+ if (SymbolRef ReceiverSym = getReceiverSVal().getAsSymbol()) {
+ DynamicTypeInfo DTI =
+ getClassObjectDynamicTypeInfo(getState(), ReceiverSym);
+ if (DTI.isValid()) {
+ // Let's use this type for lookup.
+ ReceiverT =
+ cast<ObjCObjectType>(DTI.getType().getCanonicalType());
+
+ CanBeSubClassed = DTI.canBeASubClass();
+ // And it should be a class method instead.
+ LookingForInstanceMethod = false;
+ }
+ }
+ }
+
+ if (CanBeSubClassed)
+ if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterface())
+ // Even if `DynamicTypeInfo` told us that it can be
+ // not necessarily this type, but its descendants, we still want
+ // to check again if this selector can be actually overridden.
+ CanBeSubClassed = canBeOverridenInSubclass(IDecl, Sel);
+ }
}
// Lookup the instance method implementation.
if (ReceiverT)
- if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterfaceDecl()) {
- // Repeatedly calling lookupPrivateMethod() is expensive, especially
- // when in many cases it returns null. We cache the results so
- // that repeated queries on the same ObjCIntefaceDecl and Selector
- // don't incur the same cost. On some test cases, we can see the
- // same query being issued thousands of times.
- //
- // NOTE: This cache is essentially a "global" variable, but it
- // only gets lazily created when we get here. The value of the
- // cache probably comes from it being global across ExprEngines,
- // where the same queries may get issued. If we are worried about
- // concurrency, or possibly loading/unloading ASTs, etc., we may
- // need to revisit this someday. In terms of memory, this table
- // stays around until clang quits, which also may be bad if we
- // need to release memory.
- using PrivateMethodKey = std::pair<const ObjCInterfaceDecl *, Selector>;
- using PrivateMethodCache =
- llvm::DenseMap<PrivateMethodKey, Optional<const ObjCMethodDecl *>>;
-
- static PrivateMethodCache PMC;
- Optional<const ObjCMethodDecl *> &Val = PMC[std::make_pair(IDecl, Sel)];
-
- // Query lookupPrivateMethod() if the cache does not hit.
- if (!Val.hasValue()) {
- Val = IDecl->lookupPrivateMethod(Sel);
-
- // If the method is a property accessor, we should try to "inline" it
- // even if we don't actually have an implementation.
- if (!*Val)
- if (const ObjCMethodDecl *CompileTimeMD = E->getMethodDecl())
- if (CompileTimeMD->isPropertyAccessor()) {
- if (!CompileTimeMD->getSelfDecl() &&
- isa<ObjCCategoryDecl>(CompileTimeMD->getDeclContext())) {
- // If the method is an accessor in a category, and it doesn't
- // have a self declaration, first
- // try to find the method in a class extension. This
- // works around a bug in Sema where multiple accessors
- // are synthesized for properties in class
- // extensions that are redeclared in a category and the
- // the implicit parameters are not filled in for
- // the method on the category.
- // This ensures we find the accessor in the extension, which
- // has the implicit parameters filled in.
- auto *ID = CompileTimeMD->getClassInterface();
- for (auto *CatDecl : ID->visible_extensions()) {
- Val = CatDecl->getMethod(Sel,
- CompileTimeMD->isInstanceMethod());
- if (*Val)
- break;
- }
- }
- if (!*Val)
- Val = IDecl->lookupInstanceMethod(Sel);
- }
- }
+ if (ObjCInterfaceDecl *IDecl = ReceiverT->getInterface()) {
+ const ObjCMethodDecl *MD =
+ lookupRuntimeDefinition(IDecl, Sel, LookingForInstanceMethod);
- const ObjCMethodDecl *MD = Val.getValue();
if (MD && !MD->hasBody())
MD = MD->getCanonicalDecl();
+
if (CanBeSubClassed)
return RuntimeDefinition(MD, Receiver);
else
@@ -1392,17 +1400,20 @@ CallEventManager::getCaller(const StackFrameContext *CalleeCtx,
if (CallEventRef<> Out = getCall(CallSite, State, CallerCtx))
return Out;
- // All other cases are handled by getCall.
- assert(isa<CXXConstructExpr>(CallSite) &&
- "This is not an inlineable statement");
-
SValBuilder &SVB = State->getStateManager().getSValBuilder();
const auto *Ctor = cast<CXXMethodDecl>(CalleeCtx->getDecl());
Loc ThisPtr = SVB.getCXXThis(Ctor, CalleeCtx);
SVal ThisVal = State->getSVal(ThisPtr);
- return getCXXConstructorCall(cast<CXXConstructExpr>(CallSite),
- ThisVal.getAsRegion(), State, CallerCtx);
+ if (const auto *CE = dyn_cast<CXXConstructExpr>(CallSite))
+ return getCXXConstructorCall(CE, ThisVal.getAsRegion(), State, CallerCtx);
+ else if (const auto *CIE = dyn_cast<CXXInheritedCtorInitExpr>(CallSite))
+ return getCXXInheritedConstructorCall(CIE, ThisVal.getAsRegion(), State,
+ CallerCtx);
+ else {
+ // All other cases are handled by getCall.
+ llvm_unreachable("This is not an inlineable statement");
+ }
}
// Fall back to the CFG. The only thing we haven't handled yet is