Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
12 changes: 9 additions & 3 deletions clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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();
Expand Down
87 changes: 42 additions & 45 deletions clang/lib/StaticAnalyzer/Core/CallEvent.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,23 +172,9 @@ AnalysisDeclContext *CallEvent::getCalleeAnalysisDeclContext() const {
if (!D)
return nullptr;

// TODO: For now we skip functions without definitions, even if we have
// our own getDecl(), because it's hard to find out which re-declaration
// is going to be used, and usually clients don't really care about this
// situation because there's a loss of precision anyway because we cannot
// inline the call.
RuntimeDefinition RD = getRuntimeDefinition();
if (!RD.getDecl())
return nullptr;

AnalysisDeclContext *ADC =
LCtx->getAnalysisDeclContext()->getManager()->getContext(D);

// TODO: For now we skip virtual functions, because this also rises
// the problem of which decl to use, but now it's across different classes.
if (RD.mayHaveOtherDefinitions() || RD.getDecl() != ADC->getDecl())
return nullptr;

return ADC;
}

Expand Down Expand Up @@ -222,39 +208,17 @@ CallEvent::getCalleeStackFrame(unsigned BlockCount) const {
return ADC->getManager()->getStackFrame(ADC, LCtx, E, B, BlockCount, Idx);
}

const VarRegion *CallEvent::getParameterLocation(unsigned Index,
unsigned BlockCount) const {
const ParamVarRegion
*CallEvent::getParameterLocation(unsigned Index, unsigned BlockCount) const {
const StackFrameContext *SFC = getCalleeStackFrame(BlockCount);
// We cannot construct a VarRegion without a stack frame.
if (!SFC)
return nullptr;

// Retrieve parameters of the definition, which are different from
// CallEvent's parameters() because getDecl() isn't necessarily
// the definition. SFC contains the definition that would be used
// during analysis.
const Decl *D = SFC->getDecl();

// TODO: Refactor into a virtual method of CallEvent, like parameters().
const ParmVarDecl *PVD = nullptr;
if (const auto *FD = dyn_cast<FunctionDecl>(D))
PVD = FD->parameters()[Index];
else if (const auto *BD = dyn_cast<BlockDecl>(D))
PVD = BD->parameters()[Index];
else if (const auto *MD = dyn_cast<ObjCMethodDecl>(D))
PVD = MD->parameters()[Index];
else if (const auto *CD = dyn_cast<CXXConstructorDecl>(D))
PVD = CD->parameters()[Index];
assert(PVD && "Unexpected Decl kind!");

const VarRegion *VR =
State->getStateManager().getRegionManager().getVarRegion(PVD, SFC);

// This sanity check would fail if our parameter declaration doesn't
// correspond to the stack frame's function declaration.
assert(VR->getStackFrame() == SFC);

return VR;
const ParamVarRegion *PVR =
State->getStateManager().getRegionManager().getParamVarRegion(
getOriginExpr(), Index, SFC);
return PVR;
}

/// Returns true if a type is a pointer-to-const or reference-to-const
Expand Down Expand Up @@ -325,8 +289,9 @@ ProgramStateRef CallEvent::invalidateRegions(unsigned BlockCount,
if (getKind() != CE_CXXAllocator)
if (isArgumentConstructedDirectly(Idx))
if (auto AdjIdx = getAdjustedParameterIndex(Idx))
if (const VarRegion *VR = getParameterLocation(*AdjIdx, BlockCount))
ValuesToInvalidate.push_back(loc::MemRegionVal(VR));
if (const TypedValueRegion *TVR =
getParameterLocation(*AdjIdx, BlockCount))
ValuesToInvalidate.push_back(loc::MemRegionVal(TVR));
}

// Invalidate designated regions using the batch invalidation API.
Expand Down Expand Up @@ -527,14 +492,46 @@ 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));
}
}

// FIXME: Variadic arguments are not handled at all right now.
}

const ConstructionContext *CallEvent::getConstructionContext() const {
const StackFrameContext *StackFrame = getCalleeStackFrame(0);
if (!StackFrame)
return nullptr;

const CFGElement Element = StackFrame->getCallSiteCFGElement();
if (const auto Ctor = Element.getAs<CFGConstructor>()) {
return Ctor->getConstructionContext();
}

if (const auto RecCall = Element.getAs<CFGCXXRecordTypedCall>()) {
return RecCall->getConstructionContext();
}

return nullptr;
}

Optional<SVal>
CallEvent::getReturnValueUnderConstruction() const {
const auto *CC = getConstructionContext();
if (!CC)
return None;

ExprEngine::EvalCallOptions CallOpts;
ExprEngine &Engine = getState()->getStateManager().getOwningEngine();
SVal RetVal =
Engine.computeObjectUnderConstruction(getOriginExpr(), getState(),
getLocationContext(), CC, CallOpts);
return RetVal;
}

ArrayRef<ParmVarDecl*> AnyFunctionCall::parameters() const {
const FunctionDecl *D = getDecl();
if (!D)
Expand Down
2 changes: 1 addition & 1 deletion clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
222 changes: 136 additions & 86 deletions clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp

Large diffs are not rendered by default.

160 changes: 129 additions & 31 deletions clang/lib/StaticAnalyzer/Core/MemRegion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand All @@ -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.
//===----------------------------------------------------------------------===//
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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();
}
Expand All @@ -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();
}

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -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());
}
}
}

Expand All @@ -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;

Expand Down Expand Up @@ -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 *
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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 {
Expand All @@ -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);
}
}
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/StaticAnalyzer/Core/Store.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
12 changes: 12 additions & 0 deletions clang/test/Analysis/explain-svals.c
Original file line number Diff line number Diff line change
Expand Up @@ -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(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
}

void test_non_top_level(int param) {
clang_analyzer_explain_voidp(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
}

void test_4(int n) {
test_non_top_level(n);
}
30 changes: 29 additions & 1 deletion clang/test/Analysis/explain-svals.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 *);
Expand Down Expand Up @@ -97,6 +98,33 @@ class C {
} // end of anonymous namespace

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()); // expected-warning-re{{{{^lazily frozen compound value of 1st parameter of function 'clang_analyzer_explain\(\)'$}}}}
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(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
}
};

class C_non_top_level {
public:
C_non_top_level(int param) {
clang_analyzer_explain(&param); // 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(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
};
auto lambda_non_top_level = [n](int param) {
clang_analyzer_explain(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
};

lambda_non_top_level(n);
}
41 changes: 41 additions & 0 deletions clang/test/Analysis/explain-svals.m
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,44 @@ void test_2() {
};
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(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
}

+ (instancetype)non_top_level_class_method:(int)param {
clang_analyzer_explain(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
}

- top_level_instance_method:(int)param {
clang_analyzer_explain(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
}

- non_top_level_instance_method:(int)param {
clang_analyzer_explain(&param); // 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(&param); // 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(&param); // expected-warning-re{{{{^pointer to parameter 'param'$}}}}
clang_analyzer_explain(&n); // expected-warning-re{{{{^pointer to parameter 'n'$}}}}
};

block_non_top_level(n);
}
14 changes: 3 additions & 11 deletions clang/test/Analysis/temporaries.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -890,12 +890,9 @@ class C {
public:
~C() {
glob = 1;
// FIXME: Why is destructor not inlined in C++17
clang_analyzer_checkInlined(true);
#ifdef TEMPORARY_DTORS
#if __cplusplus < 201703L
// expected-warning@-3{{TRUE}}
#endif
// expected-warning@-2{{TRUE}}
#endif
}
};
Expand All @@ -914,16 +911,11 @@ void test(int coin) {
// temporaries returned from functions, so we took the wrong branch.
coin && is(get()); // no-crash
if (coin) {
// FIXME: Why is destructor not inlined in C++17
clang_analyzer_eval(glob);
#ifdef TEMPORARY_DTORS
#if __cplusplus < 201703L
// expected-warning@-3{{TRUE}}
#else
// expected-warning@-5{{UNKNOWN}}
#endif
// expected-warning@-2{{TRUE}}
#else
// expected-warning@-8{{UNKNOWN}}
// expected-warning@-4{{UNKNOWN}}
#endif
} else {
// The destructor is not called on this branch.
Expand Down
6 changes: 4 additions & 2 deletions clang/unittests/StaticAnalyzer/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,12 @@ add_clang_unittest(StaticAnalysisTests
AnalyzerOptionsTest.cpp
CallDescriptionTest.cpp
CallEventTest.cpp
StoreTest.cpp
ParamRegionTest.cpp
RangeSetTest.cpp
RegisterCustomCheckersTest.cpp
StoreTest.cpp
SymbolReaperTest.cpp
RangeSetTest.cpp
TestReturnValueUnderConstruction.cpp
)

clang_target_link_libraries(StaticAnalysisTests
Expand Down
127 changes: 127 additions & 0 deletions clang/unittests/StaticAnalyzer/ParamRegionTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
//===- 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 checkForSameParamRegions(MemRegionManager &MRMgr,
const StackFrameContext *SFC,
const ParmVarDecl *PVD) {
for (const auto *D2: PVD->redecls()) {
const auto *PVD2 = cast<ParmVarDecl>(D2);
assert(MRMgr.getVarRegion(PVD, SFC) == MRMgr.getVarRegion(PVD2, SFC));
}
}

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));
checkForSameParamRegions(MRMgr, SFC, P);
}
} 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));
checkForSameParamRegions(MRMgr, SFC, P);
}
} 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));
checkForSameParamRegions(MRMgr, SFC, P);
}
}
}

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);
void baz(int p);
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);
}
void bar(int l);
void baz(int 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
//===- unittests/StaticAnalyzer/TestReturnValueUnderConstruction.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 "CheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Frontend/AnalysisConsumer.h"
#include "clang/StaticAnalyzer/Frontend/CheckerRegistry.h"
#include "clang/Tooling/Tooling.h"
#include "gtest/gtest.h"

namespace clang {
namespace ento {
namespace {

class TestReturnValueUnderConstructionChecker
: public Checker<check::PostCall> {
public:
void checkPostCall(const CallEvent &Call, CheckerContext &C) const {
// We are checking the invocation of `returnC` which returns an object
// by value.
const IdentifierInfo *ID = Call.getCalleeIdentifier();
if (ID->getName() != "returnC")
return;

// Since `returnC` returns an object by value, the invocation results
// in an object of type `C` constructed into variable `c`. Thus the
// return value of `CallEvent::getReturnValueUnderConstruction()` must
// be non-empty and has to be a `MemRegion`.
Optional<SVal> RetVal = Call.getReturnValueUnderConstruction();
ASSERT_TRUE(RetVal);
ASSERT_TRUE(RetVal->getAsRegion());
}
};

void addTestReturnValueUnderConstructionChecker(
AnalysisASTConsumer &AnalysisConsumer, AnalyzerOptions &AnOpts) {
AnOpts.CheckersAndPackages =
{{"test.TestReturnValueUnderConstruction", true}};
AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
Registry.addChecker<TestReturnValueUnderConstructionChecker>(
"test.TestReturnValueUnderConstruction", "", "");
});
}

TEST(TestReturnValueUnderConstructionChecker,
ReturnValueUnderConstructionChecker) {
EXPECT_TRUE(runCheckerOnCode<addTestReturnValueUnderConstructionChecker>(
R"(class C {
public:
C(int nn): n(nn) {}
virtual ~C() {}
private:
int n;
};
C returnC(int m) {
C c(m);
return c;
}
void foo() {
C c = returnC(1);
})"));
}

} // namespace
} // namespace ento
} // namespace clang