forked from OSchip/llvm-project
[analyzer] Allow bindings of the CompoundLiteralRegion
Summary: CompoundLiteralRegions have been properly modeled before, but 'getBindingForElement` was not changed to accommodate this change properly. rdar://problem/46144644 Differential Revision: https://reviews.llvm.org/D78990
This commit is contained in:
parent
ad07d5f394
commit
bd06c417e6
|
@ -1628,10 +1628,6 @@ RegionStoreManager::findLazyBinding(RegionBindingsConstRef B,
|
|||
|
||||
SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
|
||||
const ElementRegion* R) {
|
||||
// We do not currently model bindings of the CompoundLiteralregion.
|
||||
if (isa<CompoundLiteralRegion>(R->getBaseRegion()))
|
||||
return UnknownVal();
|
||||
|
||||
// Check if the region has a binding.
|
||||
if (const Optional<SVal> &V = B.getDirectBinding(R))
|
||||
return *V;
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
// RUN: %clang_cc1 -triple=i386-apple-darwin10 -analyze -analyzer-checker=debug.ExprInspection -verify %s
|
||||
// RUN: %clang_cc1 -triple=i386-apple-darwin10 -verify %s -analyze \
|
||||
// RUN: -analyzer-checker=debug.ExprInspection
|
||||
|
||||
#define NULL 0
|
||||
void clang_analyzer_eval(int);
|
||||
|
||||
// pr28449: Used to crash.
|
||||
|
@ -6,3 +9,15 @@ void foo(void) {
|
|||
static const unsigned short array[] = (const unsigned short[]){0x0F00};
|
||||
clang_analyzer_eval(array[0] == 0x0F00); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
||||
// check that we propagate info through compound literal regions
|
||||
void bar() {
|
||||
int *integers = (int[]){1, 2, 3};
|
||||
clang_analyzer_eval(integers[0] == 1); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(integers[1] == 2); // expected-warning{{TRUE}}
|
||||
clang_analyzer_eval(integers[2] == 3); // expected-warning{{TRUE}}
|
||||
|
||||
int **pointers = (int *[]){&integers[0], NULL};
|
||||
clang_analyzer_eval(pointers[0] == NULL); // expected-warning{{FALSE}}
|
||||
clang_analyzer_eval(pointers[1] == NULL); // expected-warning{{TRUE}}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
// RUN: %clang_analyze_cc1 -verify -Wno-objc-root-class %s \
|
||||
// RUN: -analyzer-checker=core,osx.cocoa.RetainCount
|
||||
|
||||
#define NULL 0
|
||||
#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
|
||||
#define CF_CONSUMED __attribute__((cf_consumed))
|
||||
|
||||
void clang_analyzer_eval(int);
|
||||
|
||||
typedef const void *CFTypeRef;
|
||||
|
||||
extern CFTypeRef CFCreate() CF_RETURNS_RETAINED;
|
||||
extern CFTypeRef CFRetain(CFTypeRef cf);
|
||||
extern void CFRelease(CFTypeRef cf);
|
||||
|
||||
void bar(CFTypeRef *v) {}
|
||||
|
||||
void test1() {
|
||||
CFTypeRef *values = (CFTypeRef[]){
|
||||
CFCreate(), // no-warning
|
||||
CFCreate(), // expected-warning{{leak}}
|
||||
CFCreate()}; // no-warning
|
||||
CFRelease(values[0]);
|
||||
CFRelease(values[2]);
|
||||
}
|
|
@ -15,89 +15,139 @@ namespace clang {
|
|||
namespace ento {
|
||||
namespace {
|
||||
|
||||
// Test that we can put a value into an int-type variable and load it
|
||||
// back from that variable. Test what happens if default bindings are used.
|
||||
class VariableBindConsumer : public ExprEngineConsumer {
|
||||
void performTest(const Decl *D) {
|
||||
StoreManager &StMgr = Eng.getStoreManager();
|
||||
SValBuilder &SVB = Eng.getSValBuilder();
|
||||
MemRegionManager &MRMgr = StMgr.getRegionManager();
|
||||
const ASTContext &ACtx = Eng.getContext();
|
||||
|
||||
const auto *VDX0 = findDeclByName<VarDecl>(D, "x0");
|
||||
const auto *VDY0 = findDeclByName<VarDecl>(D, "y0");
|
||||
const auto *VDZ0 = findDeclByName<VarDecl>(D, "z0");
|
||||
const auto *VDX1 = findDeclByName<VarDecl>(D, "x1");
|
||||
const auto *VDY1 = findDeclByName<VarDecl>(D, "y1");
|
||||
assert(VDX0 && VDY0 && VDZ0 && VDX1 && VDY1);
|
||||
|
||||
const StackFrameContext *SFC =
|
||||
Eng.getAnalysisDeclContextManager().getStackFrame(D);
|
||||
|
||||
Loc LX0 = loc::MemRegionVal(MRMgr.getVarRegion(VDX0, SFC));
|
||||
Loc LY0 = loc::MemRegionVal(MRMgr.getVarRegion(VDY0, SFC));
|
||||
Loc LZ0 = loc::MemRegionVal(MRMgr.getVarRegion(VDZ0, SFC));
|
||||
Loc LX1 = loc::MemRegionVal(MRMgr.getVarRegion(VDX1, SFC));
|
||||
Loc LY1 = loc::MemRegionVal(MRMgr.getVarRegion(VDY1, SFC));
|
||||
|
||||
Store StInit = StMgr.getInitialStore(SFC).getStore();
|
||||
SVal Zero = SVB.makeZeroVal(ACtx.IntTy);
|
||||
SVal One = SVB.makeIntVal(1, ACtx.IntTy);
|
||||
SVal NarrowZero = SVB.makeZeroVal(ACtx.CharTy);
|
||||
|
||||
// Bind(Zero)
|
||||
Store StX0 =
|
||||
StMgr.Bind(StInit, LX0, Zero).getStore();
|
||||
ASSERT_EQ(Zero, StMgr.getBinding(StX0, LX0, ACtx.IntTy));
|
||||
|
||||
// BindDefaultInitial(Zero)
|
||||
Store StY0 =
|
||||
StMgr.BindDefaultInitial(StInit, LY0.getAsRegion(), Zero).getStore();
|
||||
ASSERT_EQ(Zero, StMgr.getBinding(StY0, LY0, ACtx.IntTy));
|
||||
ASSERT_EQ(Zero, *StMgr.getDefaultBinding(StY0, LY0.getAsRegion()));
|
||||
|
||||
// BindDefaultZero()
|
||||
Store StZ0 =
|
||||
StMgr.BindDefaultZero(StInit, LZ0.getAsRegion()).getStore();
|
||||
// BindDefaultZero wipes the region with '0 S8b', not with out Zero.
|
||||
// Direct load, however, does give us back the object of the type
|
||||
// that we specify for loading.
|
||||
ASSERT_EQ(Zero, StMgr.getBinding(StZ0, LZ0, ACtx.IntTy));
|
||||
ASSERT_EQ(NarrowZero, *StMgr.getDefaultBinding(StZ0, LZ0.getAsRegion()));
|
||||
|
||||
// Bind(One)
|
||||
Store StX1 =
|
||||
StMgr.Bind(StInit, LX1, One).getStore();
|
||||
ASSERT_EQ(One, StMgr.getBinding(StX1, LX1, ACtx.IntTy));
|
||||
|
||||
// BindDefaultInitial(One)
|
||||
Store StY1 =
|
||||
StMgr.BindDefaultInitial(StInit, LY1.getAsRegion(), One).getStore();
|
||||
ASSERT_EQ(One, StMgr.getBinding(StY1, LY1, ACtx.IntTy));
|
||||
ASSERT_EQ(One, *StMgr.getDefaultBinding(StY1, LY1.getAsRegion()));
|
||||
}
|
||||
|
||||
class StoreTestConsumer : public ExprEngineConsumer {
|
||||
public:
|
||||
VariableBindConsumer(CompilerInstance &C) : ExprEngineConsumer(C) {}
|
||||
StoreTestConsumer(CompilerInstance &C) : ExprEngineConsumer(C) {}
|
||||
|
||||
bool HandleTopLevelDecl(DeclGroupRef DG) override {
|
||||
for (const auto *D : DG)
|
||||
performTest(D);
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
virtual void performTest(const Decl *D) = 0;
|
||||
};
|
||||
|
||||
class VariableBindAction : public ASTFrontendAction {
|
||||
template <class ConsumerTy> class TestAction : public ASTFrontendAction {
|
||||
public:
|
||||
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
|
||||
StringRef File) override {
|
||||
return std::make_unique<VariableBindConsumer>(Compiler);
|
||||
return std::make_unique<ConsumerTy>(Compiler);
|
||||
}
|
||||
};
|
||||
|
||||
// Test that we can put a value into an int-type variable and load it
|
||||
// back from that variable. Test what happens if default bindings are used.
|
||||
class VariableBindConsumer : public StoreTestConsumer {
|
||||
void performTest(const Decl *D) override {
|
||||
StoreManager &SManager = Eng.getStoreManager();
|
||||
SValBuilder &Builder = Eng.getSValBuilder();
|
||||
MemRegionManager &MRManager = SManager.getRegionManager();
|
||||
const ASTContext &ASTCtxt = Eng.getContext();
|
||||
|
||||
const auto *VDX0 = findDeclByName<VarDecl>(D, "x0");
|
||||
const auto *VDY0 = findDeclByName<VarDecl>(D, "y0");
|
||||
const auto *VDZ0 = findDeclByName<VarDecl>(D, "z0");
|
||||
const auto *VDX1 = findDeclByName<VarDecl>(D, "x1");
|
||||
const auto *VDY1 = findDeclByName<VarDecl>(D, "y1");
|
||||
|
||||
ASSERT_TRUE(VDX0 && VDY0 && VDZ0 && VDX1 && VDY1);
|
||||
|
||||
const StackFrameContext *SFC =
|
||||
Eng.getAnalysisDeclContextManager().getStackFrame(D);
|
||||
|
||||
Loc LX0 = loc::MemRegionVal(MRManager.getVarRegion(VDX0, SFC));
|
||||
Loc LY0 = loc::MemRegionVal(MRManager.getVarRegion(VDY0, SFC));
|
||||
Loc LZ0 = loc::MemRegionVal(MRManager.getVarRegion(VDZ0, SFC));
|
||||
Loc LX1 = loc::MemRegionVal(MRManager.getVarRegion(VDX1, SFC));
|
||||
Loc LY1 = loc::MemRegionVal(MRManager.getVarRegion(VDY1, SFC));
|
||||
|
||||
Store StInit = SManager.getInitialStore(SFC).getStore();
|
||||
SVal Zero = Builder.makeZeroVal(ASTCtxt.IntTy);
|
||||
SVal One = Builder.makeIntVal(1, ASTCtxt.IntTy);
|
||||
SVal NarrowZero = Builder.makeZeroVal(ASTCtxt.CharTy);
|
||||
|
||||
// Bind(Zero)
|
||||
Store StX0 = SManager.Bind(StInit, LX0, Zero).getStore();
|
||||
EXPECT_EQ(Zero, SManager.getBinding(StX0, LX0, ASTCtxt.IntTy));
|
||||
|
||||
// BindDefaultInitial(Zero)
|
||||
Store StY0 =
|
||||
SManager.BindDefaultInitial(StInit, LY0.getAsRegion(), Zero).getStore();
|
||||
EXPECT_EQ(Zero, SManager.getBinding(StY0, LY0, ASTCtxt.IntTy));
|
||||
EXPECT_EQ(Zero, *SManager.getDefaultBinding(StY0, LY0.getAsRegion()));
|
||||
|
||||
// BindDefaultZero()
|
||||
Store StZ0 = SManager.BindDefaultZero(StInit, LZ0.getAsRegion()).getStore();
|
||||
// BindDefaultZero wipes the region with '0 S8b', not with out Zero.
|
||||
// Direct load, however, does give us back the object of the type
|
||||
// that we specify for loading.
|
||||
EXPECT_EQ(Zero, SManager.getBinding(StZ0, LZ0, ASTCtxt.IntTy));
|
||||
EXPECT_EQ(NarrowZero, *SManager.getDefaultBinding(StZ0, LZ0.getAsRegion()));
|
||||
|
||||
// Bind(One)
|
||||
Store StX1 = SManager.Bind(StInit, LX1, One).getStore();
|
||||
EXPECT_EQ(One, SManager.getBinding(StX1, LX1, ASTCtxt.IntTy));
|
||||
|
||||
// BindDefaultInitial(One)
|
||||
Store StY1 =
|
||||
SManager.BindDefaultInitial(StInit, LY1.getAsRegion(), One).getStore();
|
||||
EXPECT_EQ(One, SManager.getBinding(StY1, LY1, ASTCtxt.IntTy));
|
||||
EXPECT_EQ(One, *SManager.getDefaultBinding(StY1, LY1.getAsRegion()));
|
||||
}
|
||||
|
||||
public:
|
||||
using StoreTestConsumer::StoreTestConsumer;
|
||||
};
|
||||
|
||||
TEST(Store, VariableBind) {
|
||||
EXPECT_TRUE(tooling::runToolOnCode(std::make_unique<VariableBindAction>(),
|
||||
"void foo() { int x0, y0, z0, x1, y1; }"));
|
||||
EXPECT_TRUE(tooling::runToolOnCode(
|
||||
std::make_unique<TestAction<VariableBindConsumer>>(),
|
||||
"void foo() { int x0, y0, z0, x1, y1; }"));
|
||||
}
|
||||
|
||||
class LiteralCompoundConsumer : public StoreTestConsumer {
|
||||
void performTest(const Decl *D) override {
|
||||
StoreManager &SManager = Eng.getStoreManager();
|
||||
SValBuilder &Builder = Eng.getSValBuilder();
|
||||
MemRegionManager &MRManager = SManager.getRegionManager();
|
||||
ASTContext &ASTCtxt = Eng.getContext();
|
||||
|
||||
using namespace ast_matchers;
|
||||
|
||||
const auto *CL = findNode<CompoundLiteralExpr>(D, compoundLiteralExpr());
|
||||
|
||||
const StackFrameContext *SFC =
|
||||
Eng.getAnalysisDeclContextManager().getStackFrame(D);
|
||||
|
||||
QualType Int = ASTCtxt.IntTy;
|
||||
|
||||
// Get region for 'test'
|
||||
const SubRegion *CLRegion = MRManager.getCompoundLiteralRegion(CL, SFC);
|
||||
|
||||
// Get value for 'test[0]'
|
||||
NonLoc Zero = Builder.makeIntVal(0, false);
|
||||
loc::MemRegionVal ZeroElement(
|
||||
MRManager.getElementRegion(ASTCtxt.IntTy, Zero, CLRegion, ASTCtxt));
|
||||
|
||||
Store StInit = SManager.getInitialStore(SFC).getStore();
|
||||
// Let's bind constant 1 to 'test[0]'
|
||||
SVal One = Builder.makeIntVal(1, Int);
|
||||
Store StX = SManager.Bind(StInit, ZeroElement, One).getStore();
|
||||
|
||||
// And make sure that we can read this binding back as it was
|
||||
EXPECT_EQ(One, SManager.getBinding(StX, ZeroElement, Int));
|
||||
}
|
||||
|
||||
public:
|
||||
using StoreTestConsumer::StoreTestConsumer;
|
||||
};
|
||||
|
||||
TEST(Store, LiteralCompound) {
|
||||
EXPECT_TRUE(tooling::runToolOnCode(
|
||||
std::make_unique<TestAction<LiteralCompoundConsumer>>(),
|
||||
"void foo() { int *test = (int[]){ 1, 2, 3 }; }", "input.c"));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
|
Loading…
Reference in New Issue