diff --git a/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h b/clang/include/clang/StaticAnalyzer/Checkers/SValExplainer.h --- 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 @@ 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(VD)) @@ -216,6 +217,39 @@ "' 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(Parent)) + OS << "function '" << FD->getQualifiedNameAsString() << "()'"; + else if (const auto *CD = dyn_cast(Parent)) + OS << "C++ constructor '" << CD->getQualifiedNameAsString() << "()'"; + else if (const auto *MD = dyn_cast(Parent)) { + if (MD->isClassMethod()) + OS << "Objective-C method '+" << MD->getQualifiedNameAsString() << "'"; + else + OS << "Objective-C method '-" << MD->getQualifiedNameAsString() << "'"; + } else if (isa(Parent)) { + if (cast(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 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h @@ -402,9 +402,10 @@ 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 diff --git a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h @@ -890,20 +890,12 @@ 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 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 @@ isa(sReg) || isa(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(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(sReg) || isa(sReg) || + isa(sReg) || isa(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 @@ 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(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 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(FieldRegionKind)); + ID.AddPointer(FD); + ID.AddPointer(superRegion); } public: - const FieldDecl *getDecl() const { return cast(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 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 @@ public: const ObjCIvarDecl *getDecl() const; + + void Profile(llvm::FoldingSetNodeID& ID) const override; + QualType getValueType() const override; bool canPrintPrettyAsExpr() const override; @@ -1312,11 +1386,18 @@ /// 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 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h @@ -308,6 +308,10 @@ 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 --- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def +++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/Regions.def @@ -73,9 +73,13 @@ 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 --- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp +++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp @@ -709,7 +709,8 @@ 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 @@ } 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 @@ os << "a C++ temp object of type " << cast(MR)->getValueType().getAsString(); return true; - case MemRegion::VarRegionKind: + case MemRegion::NonParamVarRegionKind: os << "a variable of type" << cast(MR)->getValueType().getAsString(); return true; + case MemRegion::ParamVarRegionKind: + os << "a parameter of type" + << cast(MR)->getValueType().getAsString(); + return true; case MemRegion::FieldRegionKind: os << "a field of type " << cast(MR)->getValueType().getAsString(); diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp --- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp +++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp @@ -222,39 +222,17 @@ 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(D)) - PVD = FD->parameters()[Index]; - else if (const auto *BD = dyn_cast(D)) - PVD = BD->parameters()[Index]; - else if (const auto *MD = dyn_cast(D)) - PVD = MD->parameters()[Index]; - else if (const auto *CD = dyn_cast(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 @@ 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 @@ // 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 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp @@ -218,7 +218,7 @@ 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 --- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp +++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp @@ -303,12 +303,12 @@ // 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(E)) { diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp --- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp +++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp @@ -160,11 +160,9 @@ } ObjCIvarRegion::ObjCIvarRegion(const ObjCIvarDecl *ivd, const SubRegion *sReg) - : DeclRegion(ivd, sReg, ObjCIvarRegionKind) {} + : DeclRegion(sReg, ObjCIvarRegionKind), IVD(ivd) {} -const ObjCIvarDecl *ObjCIvarRegion::getDecl() const { - return cast(D); -} +const ObjCIvarDecl *ObjCIvarRegion::getDecl() const { return IVD; } QualType ObjCIvarRegion::getValueType() const { return getDecl()->getType(); @@ -178,6 +176,33 @@ 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(D)) { + assert(Index < FD->param_size()); + return FD->parameters()[Index]; + } else if (const auto *BD = dyn_cast(D)) { + assert(Index < BD->param_size()); + return BD->parameters()[Index]; + } else if (const auto *MD = dyn_cast(D)) { + assert(Index < MD->param_size()); + return MD->parameters()[Index]; + } else if (const auto *CD = dyn_cast(D)) { + assert(Index < CD->param_size()); + return CD->parameters()[Index]; + } else { + llvm_unreachable("Unexpected Decl kind!"); + } +} + //===----------------------------------------------------------------------===// // FoldingSet profiling. //===----------------------------------------------------------------------===// @@ -249,25 +274,44 @@ 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(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(k)); - ID.AddPointer(D); +void NonParamVarRegion::ProfileRegion(llvm::FoldingSetNodeID &ID, + const VarDecl *VD, + const MemRegion *superRegion) { + ID.AddInteger(static_cast(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(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 @@ os << "SymRegion{" << sym << '}'; } -void VarRegion::dumpToStream(raw_ostream &os) const { - const auto *VD = cast(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 @@ 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 @@ 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 @@ 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(SR)->getDesugaredValueType(Ctx); @@ -847,9 +910,11 @@ 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(I.getCapturedRegion()); + const TypedValueRegion *OrigR = I.getOriginalRegion(); + if (const auto *VR = dyn_cast(OrigR)) { + if (VR->getDecl() == VD) + return cast(I.getCapturedRegion()); + } } } @@ -858,8 +923,30 @@ 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(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(D)) { + if (Index < FD->param_size() && FD->parameters()[Index] == PVD) + return getSubRegion(cast(CallSite), Index, + getStackArgumentsRegion(SFC)); + } else if (const auto *BD = dyn_cast(D)) { + if (Index < BD->param_size() && BD->parameters()[Index] == PVD) + return getSubRegion(cast(CallSite), Index, + getStackArgumentsRegion(SFC)); + } else { + return getSubRegion(cast(CallSite), Index, + getStackArgumentsRegion(SFC)); + } + } + } + D = D->getCanonicalDecl(); const MemRegion *sReg = nullptr; @@ -942,13 +1029,23 @@ } } - return getSubRegion(D, sReg); + return getSubRegion(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(D, superR); + return getSubRegion(D, superR); +} + +const ParamVarRegion * +MemRegionManager::getParamVarRegion(const Expr *OriginExpr, unsigned Index, + const LocationContext *LC) { + const StackFrameContext *SFC = LC->getStackFrame(); + assert(SFC); + return getSubRegion(OriginExpr, Index, + getStackArgumentsRegion(SFC)); } const BlockDataRegion * @@ -1341,7 +1438,8 @@ 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 @@ const VarRegion *OriginalVR = nullptr; if (!VD->hasAttr() && VD->hasLocalStorage()) { - VR = MemMgr.getVarRegion(VD, this); + VR = MemMgr.getNonParamVarRegion(VD, this); OriginalVR = MemMgr.getVarRegion(VD, LC); } else { @@ -1506,7 +1604,7 @@ 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 --- a/clang/lib/StaticAnalyzer/Core/Store.cpp +++ b/clang/lib/StaticAnalyzer/Core/Store.cpp @@ -134,7 +134,8 @@ 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 --- a/clang/test/Analysis/explain-svals.c +++ b/clang/test/Analysis/explain-svals.c @@ -27,3 +27,15 @@ 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 --- a/clang/test/Analysis/explain-svals.cpp +++ b/clang/test/Analysis/explain-svals.cpp @@ -19,6 +19,7 @@ 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 @@ 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 --- a/clang/test/Analysis/explain-svals.m +++ b/clang/test/Analysis/explain-svals.m @@ -28,3 +28,44 @@ }; 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); +} diff --git a/clang/unittests/StaticAnalyzer/CMakeLists.txt b/clang/unittests/StaticAnalyzer/CMakeLists.txt --- a/clang/unittests/StaticAnalyzer/CMakeLists.txt +++ b/clang/unittests/StaticAnalyzer/CMakeLists.txt @@ -7,6 +7,7 @@ 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 --- /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(D)) { + for (const auto *P : FD->parameters()) { + const TypedValueRegion *Reg = MRMgr.getVarRegion(P, SFC); + if (SFC->inTopFrame()) + assert(isa(Reg)); + else + assert(isa(Reg)); + } + } else if (const auto *CD = dyn_cast(D)) { + for (const auto *P : CD->parameters()) { + const TypedValueRegion *Reg = MRMgr.getVarRegion(P, SFC); + if (SFC->inTopFrame()) + assert(isa(Reg)); + else + assert(isa(Reg)); + } + } else if (const auto *MD = dyn_cast(D)) { + for (const auto *P : MD->parameters()) { + const TypedValueRegion *Reg = MRMgr.getVarRegion(P, SFC); + if (SFC->inTopFrame()) + assert(isa(Reg)); + else + assert(isa(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 CreateASTConsumer(CompilerInstance &Compiler, + StringRef File) override { + return std::make_unique(Compiler); + } +}; + +TEST(ParamRegion, ParamRegionTest) { + EXPECT_TRUE( + tooling::runToolOnCode(std::make_unique(), + 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(), + 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