[clang] 98db1f9 - [Analyzer] [NFC] Parameter Regions
Adam Balogh via cfe-commits
cfe-commits at lists.llvm.org
Tue Jun 9 03:07:23 PDT 2020
Author: Adam Balogh
Date: 2020-06-09T12:08:56+02:00
New Revision: 98db1f990fc273adc1ae36d4ce97ce66fd27ac30
URL: https://github.com/llvm/llvm-project/commit/98db1f990fc273adc1ae36d4ce97ce66fd27ac30
DIFF: https://github.com/llvm/llvm-project/commit/98db1f990fc273adc1ae36d4ce97ce66fd27ac30.diff
LOG: [Analyzer] [NFC] Parameter Regions
Currently, parameters of functions without their definition present cannot
be represented as regions because it would be difficult to ensure that the
same declaration is used in every case. To overcome this, we split
`VarRegion` to two subclasses: `NonParamVarRegion` and `ParamVarRegion`.
The latter does not store the `Decl` of the parameter variable. Instead it
stores the index of the parameter which enables retrieving the actual
`Decl` every time using the function declaration of the stack frame. To
achieve this we also removed storing of `Decl` from `DeclRegion` and made
`getDecl()` pure virtual. The individual `Decl`s are stored in the
appropriate subclasses, such as `FieldRegion`, `ObjCIvarRegion` and the
newly introduced `NonParamVarRegion`.
Differential Revision: https://reviews.llvm.org/D80522
Added:
clang/unittests/StaticAnalyzer/ParamRegionTest.cpp
Modified:
clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def
clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
clang/lib/StaticAnalyzer/Core/CallEvent.cpp
clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
clang/lib/StaticAnalyzer/Core/MemRegion.cpp
clang/lib/StaticAnalyzer/Core/Store.cpp
clang/test/Analysis/explain-svals.c
clang/test/Analysis/explain-svals.cpp
clang/test/Analysis/explain-svals.m
clang/unittests/StaticAnalyzer/CMakeLists.txt
Removed:
################################################################################
diff --git a/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h b/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h
index f7bd5b58aab5..0f33909daec0 100644
--- a/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h
+++ b/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h
@@ -18,6 +18,7 @@
#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
+#include "llvm/ADT/StringExtras.h"
namespace clang {
@@ -179,7 +180,7 @@ class SValExplainer : public FullSValVisitor<SValExplainer, std::string> {
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))
@@ -216,6 +217,39 @@ class SValExplainer : public FullSValVisitor<SValExplainer, std::string> {
"' 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);
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
index 2dd75fa23e2f..a71469e90ea2 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h
@@ -402,9 +402,10 @@ class CallEvent {
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
diff erent 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
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
index f2ba900fd05e..b0391bd042a8 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h
@@ -890,20 +890,12 @@ class CompoundLiteralRegion : public TypedValueRegion {
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();
@@ -914,9 +906,9 @@ class DeclRegion : public TypedValueRegion {
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,
@@ -925,17 +917,45 @@ class VarRegion : public DeclRegion {
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.
@@ -949,7 +969,50 @@ class VarRegion : public DeclRegion {
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;
}
};
@@ -991,16 +1054,22 @@ class CXXThisRegion : public TypedValueRegion {
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.
@@ -1022,6 +1091,8 @@ class FieldRegion : public DeclRegion {
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,
@@ -1029,6 +1100,9 @@ class ObjCIvarRegion : public DeclRegion {
public:
const ObjCIvarDecl *getDecl() const;
+
+ void Profile(llvm::FoldingSetNodeID& ID) const override;
+
QualType getValueType() const override;
bool canPrintPrettyAsExpr() const override;
@@ -1312,11 +1386,18 @@ class MemRegionManager {
/// 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.
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
index a0d7db6dd860..9a34639e2707 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h
@@ -308,6 +308,10 @@ class ProgramState : public llvm::FoldingSetNode {
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;
diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def
index 3c52c2bc7142..44ab31fc9f2e 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def
@@ -73,9 +73,13 @@ ABSTRACT_REGION(SubRegion, MemRegion)
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)
diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 40467b346f69..30fd62f887c4 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -709,7 +709,8 @@ ProgramStateRef CStringChecker::setCStringLength(ProgramStateRef state,
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.
@@ -814,7 +815,8 @@ SVal CStringChecker::getCStringLength(CheckerContext &C, ProgramStateRef &state,
}
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);
@@ -1009,10 +1011,14 @@ bool CStringChecker::SummarizeRegion(raw_ostream &os, ASTContext &Ctx,
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();
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index b133bde1cb60..dd6f78e7143f 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -222,39 +222,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
diff erent 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 +303,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.
@@ -527,7 +506,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));
}
}
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 2fea21bc1a70..c5e38cc7423d 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -218,7 +218,7 @@ void ExprEngine::VisitBlockExpr(const BlockExpr *BE, ExplodedNode *Pred,
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.
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index e14725ed4d17..ba2875aab754 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -303,12 +303,12 @@ SVal ExprEngine::computeObjectUnderConstruction(
// 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)) {
diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
index 77944ded3ae8..455adf53ac99 100644
--- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -160,11 +160,9 @@ const StackFrameContext *VarRegion::getStackFrame() const {
}
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();
@@ -178,6 +176,33 @@ QualType CXXDerivedObjectRegion::getValueType() const {
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.
//===----------------------------------------------------------------------===//
@@ -249,25 +274,44 @@ void CXXThisRegion::Profile(llvm::FoldingSetNodeID &ID) const {
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,
@@ -479,12 +523,11 @@ void SymbolicRegion::dumpToStream(raw_ostream &os) const {
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 {
@@ -531,6 +574,18 @@ void StackLocalsSpaceRegion::dumpToStream(raw_ostream &os) 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();
}
@@ -550,11 +605,18 @@ void MemRegion::printPrettyAsExpr(raw_ostream &) const {
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();
}
@@ -693,7 +755,8 @@ DefinedOrUnknownSVal MemRegionManager::getStaticSize(const MemRegion *MR,
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);
@@ -847,9 +910,11 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
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());
+ }
}
}
@@ -858,8 +923,30 @@ getStackOrCaptureRegionForDeclContext(const LocationContext *LC,
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;
@@ -942,13 +1029,23 @@ const VarRegion* MemRegionManager::getVarRegion(const VarDecl *D,
}
}
- 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 *
@@ -1341,7 +1438,8 @@ static RegionOffset calculateOffset(const MemRegion *R) {
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;
@@ -1497,7 +1595,7 @@ BlockDataRegion::getCaptureRegions(const VarDecl *VD) {
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 {
@@ -1506,7 +1604,7 @@ BlockDataRegion::getCaptureRegions(const VarDecl *VD) {
OriginalVR = VR;
}
else {
- VR = MemMgr.getVarRegion(VD, MemMgr.getUnknownRegion());
+ VR = MemMgr.getNonParamVarRegion(VD, MemMgr.getUnknownRegion());
OriginalVR = MemMgr.getVarRegion(VD, LC);
}
}
diff --git a/clang/lib/StaticAnalyzer/Core/Store.cpp b/clang/lib/StaticAnalyzer/Core/Store.cpp
index b33129c88cea..ea617bbeeba1 100644
--- a/clang/lib/StaticAnalyzer/Core/Store.cpp
+++ b/clang/lib/StaticAnalyzer/Core/Store.cpp
@@ -134,7 +134,8 @@ const MemRegion *StoreManager::castRegion(const MemRegion *R, QualType CastToTy)
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:
diff --git a/clang/test/Analysis/explain-svals.c b/clang/test/Analysis/explain-svals.c
index 204df7cd5c0a..4e095efbab77 100644
--- a/clang/test/Analysis/explain-svals.c
+++ b/clang/test/Analysis/explain-svals.c
@@ -27,3 +27,15 @@ void test_2(struct S s) {
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);
+}
diff --git a/clang/test/Analysis/explain-svals.cpp b/clang/test/Analysis/explain-svals.cpp
index f8ec23468af4..0510e4156f51 100644
--- a/clang/test/Analysis/explain-svals.cpp
+++ b/clang/test/Analysis/explain-svals.cpp
@@ -19,6 +19,7 @@ struct S {
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 *);
@@ -100,3 +101,30 @@ void test_6() {
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);
+}
diff --git a/clang/test/Analysis/explain-svals.m b/clang/test/Analysis/explain-svals.m
index b2efebf71f11..52f48c5e504f 100644
--- a/clang/test/Analysis/explain-svals.m
+++ b/clang/test/Analysis/explain-svals.m
@@ -28,3 +28,44 @@ void test_2() {
};
clang_analyzer_explain(&x); // expected-warning-re{{{{^pointer to block variable 'x'$}}}}
}
+
+ at 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;
+ at end
+
+ at 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'$}}}}
+}
+ at 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);
+}
diff --git a/clang/unittests/StaticAnalyzer/CMakeLists.txt b/clang/unittests/StaticAnalyzer/CMakeLists.txt
index 8eded655b3b7..564eba879f24 100644
--- a/clang/unittests/StaticAnalyzer/CMakeLists.txt
+++ b/clang/unittests/StaticAnalyzer/CMakeLists.txt
@@ -7,6 +7,7 @@ add_clang_unittest(StaticAnalysisTests
AnalyzerOptionsTest.cpp
CallDescriptionTest.cpp
CallEventTest.cpp
+ ParamRegionTest.cpp
RangeSetTest.cpp
RegisterCustomCheckersTest.cpp
StoreTest.cpp
diff --git a/clang/unittests/StaticAnalyzer/ParamRegionTest.cpp b/clang/unittests/StaticAnalyzer/ParamRegionTest.cpp
new file mode 100644
index 000000000000..9a666f7fb615
--- /dev/null
+++ b/clang/unittests/StaticAnalyzer/ParamRegionTest.cpp
@@ -0,0 +1,109 @@
+//===- 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
More information about the cfe-commits
mailing list