#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
+#include "llvm/ADT/StringExtras.h"
namespace clang {
return OS.str();
}
- std::string VisitVarRegion(const VarRegion *R) {
+ std::string VisitNonParamVarRegion(const NonParamVarRegion *R) {
const VarDecl *VD = R->getDecl();
std::string Name = VD->getQualifiedNameAsString();
if (isa<ParmVarDecl>(VD))
"' inside " + Visit(R->getSuperRegion());
}
+ std::string VisitParamVarRegion(const ParamVarRegion *R) {
+ std::string Str;
+ llvm::raw_string_ostream OS(Str);
+
+ const ParmVarDecl *PVD = R->getDecl();
+ std::string Name = PVD->getQualifiedNameAsString();
+ if (!Name.empty()) {
+ OS << "parameter '" << Name << "'";
+ return std::string(OS.str());
+ }
+
+ unsigned Index = R->getIndex() + 1;
+ OS << Index << llvm::getOrdinalSuffix(Index) << " parameter of ";
+ const Decl *Parent = R->getStackFrame()->getDecl();
+ if (const auto *FD = dyn_cast<FunctionDecl>(Parent))
+ OS << "function '" << FD->getQualifiedNameAsString() << "()'";
+ else if (const auto *CD = dyn_cast<CXXConstructorDecl>(Parent))
+ OS << "C++ constructor '" << CD->getQualifiedNameAsString() << "()'";
+ else if (const auto *MD = dyn_cast<ObjCMethodDecl>(Parent)) {
+ if (MD->isClassMethod())
+ OS << "Objective-C method '+" << MD->getQualifiedNameAsString() << "'";
+ else
+ OS << "Objective-C method '-" << MD->getQualifiedNameAsString() << "'";
+ } else if (isa<BlockDecl>(Parent)) {
+ if (cast<BlockDecl>(Parent)->isConversionFromLambda())
+ OS << "lambda";
+ else
+ OS << "block";
+ }
+
+ return std::string(OS.str());
+ }
+
std::string VisitSVal(SVal V) {
std::string Str;
llvm::raw_string_ostream OS(Str);
const StackFrameContext *getCalleeStackFrame(unsigned BlockCount) const;
/// Returns memory location for a parameter variable within the callee stack
- /// frame. May fail; returns null on failure.
- const VarRegion *getParameterLocation(unsigned Index,
- unsigned BlockCount) const;
+ /// frame. The behavior is undefined if the block count is different from the
+ /// one that is there when call happens. May fail; returns null on failure.
+ const ParamVarRegion *getParameterLocation(unsigned Index,
+ unsigned BlockCount) 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
class DeclRegion : public TypedValueRegion {
protected:
- const ValueDecl *D;
-
- DeclRegion(const ValueDecl *d, const MemRegion *sReg, Kind k)
- : TypedValueRegion(sReg, k), D(d) {
+ DeclRegion(const MemRegion *sReg, Kind k) : TypedValueRegion(sReg, k) {
assert(classof(this));
- assert(d && d->isCanonicalDecl());
}
- static void ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D,
- const MemRegion* superRegion, Kind k);
-
public:
- const ValueDecl *getDecl() const { return D; }
- void Profile(llvm::FoldingSetNodeID& ID) const override;
+ virtual const ValueDecl *getDecl() const = 0;
static bool classof(const MemRegion* R) {
unsigned k = R->getKind();
class VarRegion : public DeclRegion {
friend class MemRegionManager;
- // Constructors and private methods.
- VarRegion(const VarDecl *vd, const MemRegion *sReg)
- : DeclRegion(vd, sReg, VarRegionKind) {
+protected:
+ // Constructors and protected methods.
+ VarRegion(const MemRegion *sReg, Kind k) : DeclRegion(sReg, k) {
// VarRegion appears in unknown space when it's a block variable as seen
// from a block using it, when this block is analyzed at top-level.
// Other block variables appear within block data regions,
isa<BlockDataRegion>(sReg) || isa<UnknownSpaceRegion>(sReg));
}
- static void ProfileRegion(llvm::FoldingSetNodeID& ID, const VarDecl *VD,
- const MemRegion *superRegion) {
- DeclRegion::ProfileRegion(ID, VD, superRegion, VarRegionKind);
+public:
+ const VarDecl *getDecl() const override = 0;
+
+ const StackFrameContext *getStackFrame() const;
+
+ QualType getValueType() const override {
+ // FIXME: We can cache this if needed.
+ return getDecl()->getType();
}
-public:
- void Profile(llvm::FoldingSetNodeID& ID) const override;
+ static bool classof(const MemRegion *R) {
+ unsigned k = R->getKind();
+ return k >= BEGIN_VAR_REGIONS && k <= END_VAR_REGIONS;
+ }
+};
- const VarDecl *getDecl() const { return cast<VarDecl>(D); }
+class NonParamVarRegion : public VarRegion {
+ friend class MemRegionManager;
- const StackFrameContext *getStackFrame() const;
+ const VarDecl *VD;
+
+ // Constructors and private methods.
+ NonParamVarRegion(const VarDecl *vd, const MemRegion *sReg)
+ : VarRegion(sReg, NonParamVarRegionKind), VD(vd) {
+ // VarRegion appears in unknown space when it's a block variable as seen
+ // from a block using it, when this block is analyzed at top-level.
+ // Other block variables appear within block data regions,
+ // which, unlike everything else on this list, are not memory spaces.
+ assert(isa<GlobalsSpaceRegion>(sReg) || isa<StackSpaceRegion>(sReg) ||
+ isa<BlockDataRegion>(sReg) || isa<UnknownSpaceRegion>(sReg));
+ }
+
+ static void ProfileRegion(llvm::FoldingSetNodeID &ID, const VarDecl *VD,
+ const MemRegion *superRegion);
+
+public:
+ void Profile(llvm::FoldingSetNodeID &ID) const override;
+
+ const VarDecl *getDecl() const override { return VD; }
QualType getValueType() const override {
// FIXME: We can cache this if needed.
void printPrettyAsExpr(raw_ostream &os) const override;
static bool classof(const MemRegion* R) {
- return R->getKind() == VarRegionKind;
+ return R->getKind() == NonParamVarRegionKind;
+ }
+};
+
+/// ParamVarRegion - Represents a region for paremters. Only parameters of the
+/// function in the current stack frame are represented as `ParamVarRegion`s.
+/// Parameters of top-level analyzed functions as well as captured paremeters
+/// by lambdas and blocks are repesented as `VarRegion`s.
+
+// FIXME: `ParamVarRegion` only supports parameters of functions, C++
+// constructors, blocks and Objective-C methods with existing `Decl`. Upon
+// implementing stack frame creations for functions without decl (functions
+// passed by unknown function pointer) methods of `ParamVarRegion` must be
+// updated.
+class ParamVarRegion : public VarRegion {
+ friend class MemRegionManager;
+
+ const Expr *OriginExpr;
+ unsigned Index;
+
+ ParamVarRegion(const Expr *OE, unsigned Idx, const MemRegion *SReg)
+ : VarRegion(SReg, ParamVarRegionKind), OriginExpr(OE), Index(Idx) {
+ assert(!cast<StackSpaceRegion>(SReg)->getStackFrame()->inTopFrame());
+ }
+
+ static void ProfileRegion(llvm::FoldingSetNodeID &ID, const Expr *OE,
+ unsigned Idx, const MemRegion *SReg);
+
+public:
+ const Expr *getOriginExpr() const { return OriginExpr; }
+ unsigned getIndex() const { return Index; }
+
+ void Profile(llvm::FoldingSetNodeID& ID) const override;
+
+ void dumpToStream(raw_ostream &os) const override;
+
+ QualType getValueType() const override;
+ const ParmVarDecl *getDecl() const override;
+
+ bool canPrintPrettyAsExpr() const override;
+ void printPrettyAsExpr(raw_ostream &os) const override;
+
+ static bool classof(const MemRegion *R) {
+ return R->getKind() == ParamVarRegionKind;
}
};
class FieldRegion : public DeclRegion {
friend class MemRegionManager;
- FieldRegion(const FieldDecl *fd, const SubRegion* sReg)
- : DeclRegion(fd, sReg, FieldRegionKind) {}
+ const FieldDecl *FD;
- static void ProfileRegion(llvm::FoldingSetNodeID& ID, const FieldDecl *FD,
+ FieldRegion(const FieldDecl *fd, const SubRegion *sReg)
+ : DeclRegion(sReg, FieldRegionKind), FD(fd) {}
+
+ static void ProfileRegion(llvm::FoldingSetNodeID &ID, const FieldDecl *FD,
const MemRegion* superRegion) {
- DeclRegion::ProfileRegion(ID, FD, superRegion, FieldRegionKind);
+ ID.AddInteger(static_cast<unsigned>(FieldRegionKind));
+ ID.AddPointer(FD);
+ ID.AddPointer(superRegion);
}
public:
- const FieldDecl *getDecl() const { return cast<FieldDecl>(D); }
+ const FieldDecl *getDecl() const override { return FD; }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override;
QualType getValueType() const override {
// FIXME: We can cache this if needed.
class ObjCIvarRegion : public DeclRegion {
friend class MemRegionManager;
+ const ObjCIvarDecl *IVD;
+
ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg);
static void ProfileRegion(llvm::FoldingSetNodeID& ID, const ObjCIvarDecl *ivd,
public:
const ObjCIvarDecl *getDecl() const;
+
+ void Profile(llvm::FoldingSetNodeID& ID) const override;
+
QualType getValueType() const override;
bool canPrintPrettyAsExpr() const override;
/// getVarRegion - Retrieve or create the memory region associated with
/// a specified VarDecl and LocationContext.
- const VarRegion* getVarRegion(const VarDecl *D, const LocationContext *LC);
+ const VarRegion *getVarRegion(const VarDecl *VD, const LocationContext *LC);
/// getVarRegion - Retrieve or create the memory region associated with
- /// a specified VarDecl and super region.
- const VarRegion *getVarRegion(const VarDecl *D, const MemRegion *superR);
+ /// a specified VarDecl and LocationContext.
+ const NonParamVarRegion *getNonParamVarRegion(const VarDecl *VD,
+ const MemRegion *superR);
+
+ /// getParamVarRegion - Retrieve or create the memory region
+ /// associated with a specified CallExpr, Index and LocationContext.
+ const ParamVarRegion *getParamVarRegion(const Expr *OriginExpr,
+ unsigned Index,
+ const LocationContext *LC);
/// getElementRegion - Retrieve the memory region associated with the
/// associated element type, index, and super region.
Loc getLValue(const CXXRecordDecl *BaseClass, const SubRegion *Super,
bool IsVirtual) const;
+ /// Get the lvalue for a parameter.
+ Loc getLValue(const Expr *Call, unsigned Index,
+ const LocationContext *LC) const;
+
/// Get the lvalue for a variable reference.
Loc getLValue(const VarDecl *D, const LocationContext *LC) const;
ABSTRACT_REGION(DeclRegion, TypedValueRegion)
REGION(FieldRegion, DeclRegion)
REGION(ObjCIvarRegion, DeclRegion)
- REGION(VarRegion, DeclRegion)
- REGION_RANGE(DECL_REGIONS, FieldRegionKind,
- VarRegionKind)
+ ABSTRACT_REGION(VarRegion, DeclRegion)
+ REGION(NonParamVarRegion, VarRegion)
+ REGION(ParamVarRegion, VarRegion)
+ REGION_RANGE(VAR_REGIONS, NonParamVarRegionKind,
+ ParamVarRegionKind)
+ REGION_RANGE(DECL_REGIONS, FieldRegionKind,
+ ParamVarRegionKind)
REGION(ElementRegion, TypedValueRegion)
REGION(ObjCStringRegion, TypedValueRegion)
REGION(StringRegion, TypedValueRegion)
case MemRegion::SymbolicRegionKind:
case MemRegion::AllocaRegionKind:
- case MemRegion::VarRegionKind:
+ case MemRegion::NonParamVarRegionKind:
+ case MemRegion::ParamVarRegionKind:
case MemRegion::FieldRegionKind:
case MemRegion::ObjCIvarRegionKind:
// These are the types we can currently track string lengths for.
}
case MemRegion::SymbolicRegionKind:
case MemRegion::AllocaRegionKind:
- case MemRegion::VarRegionKind:
+ case MemRegion::NonParamVarRegionKind:
+ case MemRegion::ParamVarRegionKind:
case MemRegion::FieldRegionKind:
case MemRegion::ObjCIvarRegionKind:
return getCStringLengthForRegion(C, state, Ex, MR, hypothetical);
os << "a C++ temp object of type "
<< cast<TypedValueRegion>(MR)->getValueType().getAsString();
return true;
- case MemRegion::VarRegionKind:
+ case MemRegion::NonParamVarRegionKind:
os << "a variable of type"
<< cast<TypedValueRegion>(MR)->getValueType().getAsString();
return true;
+ case MemRegion::ParamVarRegionKind:
+ os << "a parameter of type"
+ << cast<TypedValueRegion>(MR)->getValueType().getAsString();
+ return true;
case MemRegion::FieldRegionKind:
os << "a field of type "
<< cast<TypedValueRegion>(MR)->getValueType().getAsString();
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
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.
// 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));
}
}
auto CE = BD->capture_end();
for (; I != E; ++I) {
const VarRegion *capturedR = I.getCapturedRegion();
- const VarRegion *originalR = I.getOriginalRegion();
+ const TypedValueRegion *originalR = I.getOriginalRegion();
// If the capture had a copy expression, use the result of evaluating
// that expression, otherwise use the original value.
// Operator arguments do not correspond to operator parameters
// because this-argument is implemented as a normal argument in
// operator call expressions but not in operator declarations.
- const VarRegion *VR = Caller->getParameterLocation(
+ const TypedValueRegion *TVR = Caller->getParameterLocation(
*Caller->getAdjustedParameterIndex(Idx), currBldrCtx->blockCount());
- if (!VR)
+ if (!TVR)
return None;
- return loc::MemRegionVal(VR);
+ return loc::MemRegionVal(TVR);
};
if (const auto *CE = dyn_cast<CallExpr>(E)) {
}
ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg)
- : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {}
+ : DeclRegion(sReg, ObjCIvarRegionKind), IVD(ivd) {}
-const ObjCIvarDecl *ObjCIvarRegion::getDecl() const {
- return cast<ObjCIvarDecl>(D);
-}
+const ObjCIvarDecl *ObjCIvarRegion::getDecl() const { return IVD; }
QualType ObjCIvarRegion::getValueType() const {
return getDecl()->getType();
return QualType(getDecl()->getTypeForDecl(), 0);
}
+QualType ParamVarRegion::getValueType() const {
+ assert(getDecl() &&
+ "`ParamVarRegion` support functions without `Decl` not implemented"
+ " yet.");
+ return getDecl()->getType();
+}
+
+const ParmVarDecl *ParamVarRegion::getDecl() const {
+ const Decl *D = getStackFrame()->getDecl();
+
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ assert(Index < FD->param_size());
+ return FD->parameters()[Index];
+ } else if (const auto *BD = dyn_cast<BlockDecl>(D)) {
+ assert(Index < BD->param_size());
+ return BD->parameters()[Index];
+ } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
+ assert(Index < MD->param_size());
+ return MD->parameters()[Index];
+ } else if (const auto *CD = dyn_cast<CXXConstructorDecl>(D)) {
+ assert(Index < CD->param_size());
+ return CD->parameters()[Index];
+ } else {
+ llvm_unreachable("Unexpected Decl kind!");
+ }
+}
+
//===----------------------------------------------------------------------===//
// FoldingSet profiling.
//===----------------------------------------------------------------------===//
CXXThisRegion::ProfileRegion(ID, ThisPointerTy, superRegion);
}
+void FieldRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, getDecl(), superRegion);
+}
+
void ObjCIvarRegion::ProfileRegion(llvm::FoldingSetNodeID& ID,
const ObjCIvarDecl *ivd,
const MemRegion* superRegion) {
- DeclRegion::ProfileRegion(ID, ivd, superRegion, ObjCIvarRegionKind);
+ ID.AddInteger(static_cast<unsigned>(ObjCIvarRegionKind));
+ ID.AddPointer(ivd);
+ ID.AddPointer(superRegion);
+}
+
+void ObjCIvarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, getDecl(), superRegion);
}
-void DeclRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, const Decl *D,
- const MemRegion* superRegion, Kind k) {
- ID.AddInteger(static_cast<unsigned>(k));
- ID.AddPointer(D);
+void NonParamVarRegion::ProfileRegion(llvm::FoldingSetNodeID &ID,
+ const VarDecl *VD,
+ const MemRegion *superRegion) {
+ ID.AddInteger(static_cast<unsigned>(NonParamVarRegionKind));
+ ID.AddPointer(VD);
ID.AddPointer(superRegion);
}
-void DeclRegion::Profile(llvm::FoldingSetNodeID& ID) const {
- DeclRegion::ProfileRegion(ID, D, superRegion, getKind());
+void NonParamVarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, getDecl(), superRegion);
}
-void VarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
- VarRegion::ProfileRegion(ID, getDecl(), superRegion);
+void ParamVarRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, const Expr *OE,
+ unsigned Idx, const MemRegion *SReg) {
+ ID.AddInteger(static_cast<unsigned>(ParamVarRegionKind));
+ ID.AddPointer(OE);
+ ID.AddInteger(Idx);
+ ID.AddPointer(SReg);
+}
+
+void ParamVarRegion::Profile(llvm::FoldingSetNodeID &ID) const {
+ ProfileRegion(ID, getOriginExpr(), getIndex(), superRegion);
}
void SymbolicRegion::ProfileRegion(llvm::FoldingSetNodeID& ID, SymbolRef sym,
os << "SymRegion{" << sym << '}';
}
-void VarRegion::dumpToStream(raw_ostream &os) const {
- const auto *VD = cast<VarDecl>(D);
+void NonParamVarRegion::dumpToStream(raw_ostream &os) const {
if (const IdentifierInfo *ID = VD->getIdentifier())
os << ID->getName();
else
- os << "VarRegion{D" << VD->getID() << '}';
+ os << "NonParamVarRegion{D" << VD->getID() << '}';
}
LLVM_DUMP_METHOD void RegionRawOffset::dump() const {
os << "StackLocalsSpaceRegion";
}
+void ParamVarRegion::dumpToStream(raw_ostream &os) const {
+ const ParmVarDecl *PVD = getDecl();
+ assert(PVD &&
+ "`ParamVarRegion` support functions without `Decl` not implemented"
+ " yet.");
+ if (const IdentifierInfo *ID = PVD->getIdentifier()) {
+ os << ID->getName();
+ } else {
+ os << "ParamVarRegion{P" << PVD->getID() << '}';
+ }
+}
+
bool MemRegion::canPrintPretty() const {
return canPrintPrettyAsExpr();
}
llvm_unreachable("This region cannot be printed pretty.");
}
-bool VarRegion::canPrintPrettyAsExpr() const {
- return true;
+bool NonParamVarRegion::canPrintPrettyAsExpr() const { return true; }
+
+void NonParamVarRegion::printPrettyAsExpr(raw_ostream &os) const {
+ os << getDecl()->getName();
}
-void VarRegion::printPrettyAsExpr(raw_ostream &os) const {
+bool ParamVarRegion::canPrintPrettyAsExpr() const { return true; }
+
+void ParamVarRegion::printPrettyAsExpr(raw_ostream &os) const {
+ assert(getDecl() &&
+ "`ParamVarRegion` support functions without `Decl` not implemented"
+ " yet.");
os << getDecl()->getName();
}
case MemRegion::CXXTempObjectRegionKind:
case MemRegion::CXXThisRegionKind:
case MemRegion::ObjCIvarRegionKind:
- case MemRegion::VarRegionKind:
+ case MemRegion::NonParamVarRegionKind:
+ case MemRegion::ParamVarRegionKind:
case MemRegion::ElementRegionKind:
case MemRegion::ObjCStringRegionKind: {
QualType Ty = cast<TypedValueRegion>(SR)->getDesugaredValueType(Ctx);
for (BlockDataRegion::referenced_vars_iterator
I = BR->referenced_vars_begin(),
E = BR->referenced_vars_end(); I != E; ++I) {
- const VarRegion *VR = I.getOriginalRegion();
- if (VR->getDecl() == VD)
- return cast<VarRegion>(I.getCapturedRegion());
+ const TypedValueRegion *OrigR = I.getOriginalRegion();
+ if (const auto *VR = dyn_cast<VarRegion>(OrigR)) {
+ if (VR->getDecl() == VD)
+ return cast<VarRegion>(I.getCapturedRegion());
+ }
}
}
return (const StackFrameContext *)nullptr;
}
-const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
+const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
const LocationContext *LC) {
+ const auto *PVD = dyn_cast<ParmVarDecl>(D);
+ if (PVD) {
+ unsigned Index = PVD->getFunctionScopeIndex();
+ const StackFrameContext *SFC = LC->getStackFrame();
+ const Stmt *CallSite = SFC->getCallSite();
+ if (CallSite) {
+ const Decl *D = SFC->getDecl();
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ if (Index < FD->param_size() && FD->parameters()[Index] == PVD)
+ return getSubRegion<ParamVarRegion>(cast<Expr>(CallSite), Index,
+ getStackArgumentsRegion(SFC));
+ } else if (const auto *BD = dyn_cast<BlockDecl>(D)) {
+ if (Index < BD->param_size() && BD->parameters()[Index] == PVD)
+ return getSubRegion<ParamVarRegion>(cast<Expr>(CallSite), Index,
+ getStackArgumentsRegion(SFC));
+ } else {
+ return getSubRegion<ParamVarRegion>(cast<Expr>(CallSite), Index,
+ getStackArgumentsRegion(SFC));
+ }
+ }
+ }
+
D = D->getCanonicalDecl();
const MemRegion *sReg = nullptr;
}
}
- return getSubRegion<VarRegion>(D, sReg);
+ return getSubRegion<NonParamVarRegion>(D, sReg);
}
-const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
- const MemRegion *superR) {
+const NonParamVarRegion *
+MemRegionManager::getNonParamVarRegion(const VarDecl *D,
+ const MemRegion *superR) {
D = D->getCanonicalDecl();
- return getSubRegion<VarRegion>(D, superR);
+ return getSubRegion<NonParamVarRegion>(D, superR);
+}
+
+const ParamVarRegion *
+MemRegionManager::getParamVarRegion(const Expr *OriginExpr, unsigned Index,
+ const LocationContext *LC) {
+ const StackFrameContext *SFC = LC->getStackFrame();
+ assert(SFC);
+ return getSubRegion<ParamVarRegion>(OriginExpr, Index,
+ getStackArgumentsRegion(SFC));
}
const BlockDataRegion *
case MemRegion::CXXThisRegionKind:
case MemRegion::StringRegionKind:
case MemRegion::ObjCStringRegionKind:
- case MemRegion::VarRegionKind:
+ case MemRegion::NonParamVarRegionKind:
+ case MemRegion::ParamVarRegionKind:
case MemRegion::CXXTempObjectRegionKind:
// Usual base regions.
goto Finish;
const VarRegion *OriginalVR = nullptr;
if (!VD->hasAttr<BlocksAttr>() && VD->hasLocalStorage()) {
- VR = MemMgr.getVarRegion(VD, this);
+ VR = MemMgr.getNonParamVarRegion(VD, this);
OriginalVR = MemMgr.getVarRegion(VD, LC);
}
else {
OriginalVR = VR;
}
else {
- VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion());
+ VR = MemMgr.getNonParamVarRegion(VD, MemMgr.getUnknownRegion());
OriginalVR = MemMgr.getVarRegion(VD, LC);
}
}
case MemRegion::FieldRegionKind:
case MemRegion::ObjCIvarRegionKind:
case MemRegion::ObjCStringRegionKind:
- case MemRegion::VarRegionKind:
+ case MemRegion::NonParamVarRegionKind:
+ case MemRegion::ParamVarRegionKind:
case MemRegion::CXXTempObjectRegionKind:
case MemRegion::CXXBaseObjectRegionKind:
case MemRegion::CXXDerivedObjectRegionKind:
clang_analyzer_explain_voidp(&s); // expected-warning-re{{{{^pointer to parameter 's'$}}}}
clang_analyzer_explain_int(s.z); // expected-warning-re{{{{^initial value of field 'z' of parameter 's'$}}}}
}
+
+void test_3(int param) {
+ clang_analyzer_explain_voidp(¶m); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+}
+
+void test_non_top_level(int param) {
+ clang_analyzer_explain_voidp(¶m); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+}
+
+void test_4(int n) {
+ test_non_top_level(n);
+}
void clang_analyzer_explain(int);
void clang_analyzer_explain(void *);
+void clang_analyzer_explain(const int *);
void clang_analyzer_explain(S);
size_t clang_analyzer_getExtent(void *);
clang_analyzer_explain(conjure_S()); // expected-warning-re{{{{^lazily frozen compound value of temporary object constructed at statement 'conjure_S\(\)'$}}}}
clang_analyzer_explain(conjure_S().z); // expected-warning-re{{{{^value derived from \(symbol of type 'int' conjured at statement 'conjure_S\(\)'\) for field 'z' of temporary object constructed at statement 'conjure_S\(\)'$}}}}
}
+
+class C_top_level {
+public:
+ C_top_level(int param) {
+ clang_analyzer_explain(¶m); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+ }
+};
+
+class C_non_top_level {
+public:
+ C_non_top_level(int param) {
+ clang_analyzer_explain(¶m); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+ }
+};
+
+void test_7(int n) {
+ C_non_top_level c(n);
+
+ auto lambda_top_level = [n](int param) {
+ clang_analyzer_explain(¶m); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+ };
+ auto lambda_non_top_level = [n](int param) {
+ clang_analyzer_explain(¶m); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+ };
+
+ lambda_non_top_level(n);
+}
};
clang_analyzer_explain(&x); // expected-warning-re{{{{^pointer to block variable 'x'$}}}}
}
+
+@interface O
++ (instancetype)top_level_class_method:(int)param;
++ (instancetype)non_top_level_class_method:(int)param;
+- top_level_instance_method:(int)param;
+- non_top_level_instance_method:(int)param;
+@end
+
+@implementation O
++ (instancetype)top_level_class_method:(int)param {
+ clang_analyzer_explain(¶m); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+}
+
++ (instancetype)non_top_level_class_method:(int)param {
+ clang_analyzer_explain(¶m); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+}
+
+- top_level_instance_method:(int)param {
+ clang_analyzer_explain(¶m); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+}
+
+- non_top_level_instance_method:(int)param {
+ clang_analyzer_explain(¶m); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+}
+@end
+
+void test_3(int n, int m) {
+ O *o = [O non_top_level_class_method:n];
+ [o non_top_level_instance_method:m];
+
+ void (^block_top_level)(int) = ^(int param) {
+ clang_analyzer_explain(¶m); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+ clang_analyzer_explain(&n); // expected-warning-re{{{{^pointer to parameter 'n'$}}}}
+ };
+ void (^block_non_top_level)(int) = ^(int param) {
+ clang_analyzer_explain(¶m); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
+ clang_analyzer_explain(&n); // expected-warning-re{{{{^pointer to parameter 'n'$}}}}
+ };
+
+ block_non_top_level(n);
+}
AnalyzerOptionsTest.cpp
CallDescriptionTest.cpp
CallEventTest.cpp
+ ParamRegionTest.cpp
RangeSetTest.cpp
RegisterCustomCheckersTest.cpp
StoreTest.cpp
--- /dev/null
+//===- unittests/StaticAnalyzer/ParamRegionTest.cpp -----------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "Reusables.h"
+
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace ento {
+namespace {
+
+class ParamRegionTestConsumer : public ExprEngineConsumer {
+ void performTest(const Decl *D) {
+ StoreManager &StMgr = Eng.getStoreManager();
+ MemRegionManager &MRMgr = StMgr.getRegionManager();
+ const StackFrameContext *SFC =
+ Eng.getAnalysisDeclContextManager().getStackFrame(D);
+
+ if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
+ for (const auto *P : FD->parameters()) {
+ const TypedValueRegion *Reg = MRMgr.getVarRegion(P, SFC);
+ if (SFC->inTopFrame())
+ assert(isa<NonParamVarRegion>(Reg));
+ else
+ assert(isa<ParamVarRegion>(Reg));
+ }
+ } else if (const auto *CD = dyn_cast<CXXConstructorDecl>(D)) {
+ for (const auto *P : CD->parameters()) {
+ const TypedValueRegion *Reg = MRMgr.getVarRegion(P, SFC);
+ if (SFC->inTopFrame())
+ assert(isa<NonParamVarRegion>(Reg));
+ else
+ assert(isa<ParamVarRegion>(Reg));
+ }
+ } else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D)) {
+ for (const auto *P : MD->parameters()) {
+ const TypedValueRegion *Reg = MRMgr.getVarRegion(P, SFC);
+ if (SFC->inTopFrame())
+ assert(isa<NonParamVarRegion>(Reg));
+ else
+ assert(isa<ParamVarRegion>(Reg));
+ }
+ }
+ }
+
+public:
+ ParamRegionTestConsumer(CompilerInstance &C) : ExprEngineConsumer(C) {}
+
+ bool HandleTopLevelDecl(DeclGroupRef DG) override {
+ for (const auto *D : DG) {
+ performTest(D);
+ }
+ return true;
+ }
+};
+
+class ParamRegionTestAction : public ASTFrontendAction {
+public:
+ std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
+ StringRef File) override {
+ return std::make_unique<ParamRegionTestConsumer>(Compiler);
+ }
+};
+
+TEST(ParamRegion, ParamRegionTest) {
+ EXPECT_TRUE(
+ tooling::runToolOnCode(std::make_unique<ParamRegionTestAction>(),
+ R"(void foo(int n) {
+ auto lambda = [n](int m) {
+ return n + m;
+ };
+
+ int k = lambda(2);
+ }
+
+ void bar(int l) {
+ foo(l);
+ }
+
+ struct S {
+ int n;
+ S(int nn): n(nn) {}
+ };
+
+ void baz(int p) {
+ S s(p);
+ })"));
+ EXPECT_TRUE(
+ tooling::runToolOnCode(std::make_unique<ParamRegionTestAction>(),
+ R"(@interface O
+ + alloc;
+ - initWithInt:(int)q;
+ @end
+
+ void qix(int r) {
+ O *o = [[O alloc] initWithInt:r];
+ })",
+ "input.m"));
+}
+
+} // namespace
+} // namespace ento
+} // namespace clang