Skip to content

Commit bd06c41

Browse files
[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
1 parent ad07d5f commit bd06c41

File tree

4 files changed

+137
-51
lines changed

4 files changed

+137
-51
lines changed

clang/lib/StaticAnalyzer/Core/RegionStore.cpp

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1628,10 +1628,6 @@ RegionStoreManager::findLazyBinding(RegionBindingsConstRef B,
16281628

16291629
SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
16301630
const ElementRegion* R) {
1631-
// We do not currently model bindings of the CompoundLiteralregion.
1632-
if (isa<CompoundLiteralRegion>(R->getBaseRegion()))
1633-
return UnknownVal();
1634-
16351631
// Check if the region has a binding.
16361632
if (const Optional<SVal> &V = B.getDirectBinding(R))
16371633
return *V;
Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,23 @@
1-
// RUN: %clang_cc1 -triple=i386-apple-darwin10 -analyze -analyzer-checker=debug.ExprInspection -verify %s
1+
// RUN: %clang_cc1 -triple=i386-apple-darwin10 -verify %s -analyze \
2+
// RUN: -analyzer-checker=debug.ExprInspection
3+
4+
#define NULL 0
25
void clang_analyzer_eval(int);
36

47
// pr28449: Used to crash.
58
void foo(void) {
69
static const unsigned short array[] = (const unsigned short[]){0x0F00};
710
clang_analyzer_eval(array[0] == 0x0F00); // expected-warning{{TRUE}}
811
}
12+
13+
// check that we propagate info through compound literal regions
14+
void bar() {
15+
int *integers = (int[]){1, 2, 3};
16+
clang_analyzer_eval(integers[0] == 1); // expected-warning{{TRUE}}
17+
clang_analyzer_eval(integers[1] == 2); // expected-warning{{TRUE}}
18+
clang_analyzer_eval(integers[2] == 3); // expected-warning{{TRUE}}
19+
20+
int **pointers = (int *[]){&integers[0], NULL};
21+
clang_analyzer_eval(pointers[0] == NULL); // expected-warning{{FALSE}}
22+
clang_analyzer_eval(pointers[1] == NULL); // expected-warning{{TRUE}}
23+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// RUN: %clang_analyze_cc1 -verify -Wno-objc-root-class %s \
2+
// RUN: -analyzer-checker=core,osx.cocoa.RetainCount
3+
4+
#define NULL 0
5+
#define CF_RETURNS_RETAINED __attribute__((cf_returns_retained))
6+
#define CF_CONSUMED __attribute__((cf_consumed))
7+
8+
void clang_analyzer_eval(int);
9+
10+
typedef const void *CFTypeRef;
11+
12+
extern CFTypeRef CFCreate() CF_RETURNS_RETAINED;
13+
extern CFTypeRef CFRetain(CFTypeRef cf);
14+
extern void CFRelease(CFTypeRef cf);
15+
16+
void bar(CFTypeRef *v) {}
17+
18+
void test1() {
19+
CFTypeRef *values = (CFTypeRef[]){
20+
CFCreate(), // no-warning
21+
CFCreate(), // expected-warning{{leak}}
22+
CFCreate()}; // no-warning
23+
CFRelease(values[0]);
24+
CFRelease(values[2]);
25+
}

clang/unittests/StaticAnalyzer/StoreTest.cpp

Lines changed: 96 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -15,89 +15,139 @@ namespace clang {
1515
namespace ento {
1616
namespace {
1717

18+
class StoreTestConsumer : public ExprEngineConsumer {
19+
public:
20+
StoreTestConsumer(CompilerInstance &C) : ExprEngineConsumer(C) {}
21+
22+
bool HandleTopLevelDecl(DeclGroupRef DG) override {
23+
for (const auto *D : DG)
24+
performTest(D);
25+
return true;
26+
}
27+
28+
private:
29+
virtual void performTest(const Decl *D) = 0;
30+
};
31+
32+
template <class ConsumerTy> class TestAction : public ASTFrontendAction {
33+
public:
34+
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
35+
StringRef File) override {
36+
return std::make_unique<ConsumerTy>(Compiler);
37+
}
38+
};
39+
1840
// Test that we can put a value into an int-type variable and load it
1941
// back from that variable. Test what happens if default bindings are used.
20-
class VariableBindConsumer : public ExprEngineConsumer {
21-
void performTest(const Decl *D) {
22-
StoreManager &StMgr = Eng.getStoreManager();
23-
SValBuilder &SVB = Eng.getSValBuilder();
24-
MemRegionManager &MRMgr = StMgr.getRegionManager();
25-
const ASTContext &ACtx = Eng.getContext();
42+
class VariableBindConsumer : public StoreTestConsumer {
43+
void performTest(const Decl *D) override {
44+
StoreManager &SManager = Eng.getStoreManager();
45+
SValBuilder &Builder = Eng.getSValBuilder();
46+
MemRegionManager &MRManager = SManager.getRegionManager();
47+
const ASTContext &ASTCtxt = Eng.getContext();
2648

2749
const auto *VDX0 = findDeclByName<VarDecl>(D, "x0");
2850
const auto *VDY0 = findDeclByName<VarDecl>(D, "y0");
2951
const auto *VDZ0 = findDeclByName<VarDecl>(D, "z0");
3052
const auto *VDX1 = findDeclByName<VarDecl>(D, "x1");
3153
const auto *VDY1 = findDeclByName<VarDecl>(D, "y1");
32-
assert(VDX0 && VDY0 && VDZ0 && VDX1 && VDY1);
54+
55+
ASSERT_TRUE(VDX0 && VDY0 && VDZ0 && VDX1 && VDY1);
3356

3457
const StackFrameContext *SFC =
3558
Eng.getAnalysisDeclContextManager().getStackFrame(D);
3659

37-
Loc LX0 = loc::MemRegionVal(MRMgr.getVarRegion(VDX0, SFC));
38-
Loc LY0 = loc::MemRegionVal(MRMgr.getVarRegion(VDY0, SFC));
39-
Loc LZ0 = loc::MemRegionVal(MRMgr.getVarRegion(VDZ0, SFC));
40-
Loc LX1 = loc::MemRegionVal(MRMgr.getVarRegion(VDX1, SFC));
41-
Loc LY1 = loc::MemRegionVal(MRMgr.getVarRegion(VDY1, SFC));
60+
Loc LX0 = loc::MemRegionVal(MRManager.getVarRegion(VDX0, SFC));
61+
Loc LY0 = loc::MemRegionVal(MRManager.getVarRegion(VDY0, SFC));
62+
Loc LZ0 = loc::MemRegionVal(MRManager.getVarRegion(VDZ0, SFC));
63+
Loc LX1 = loc::MemRegionVal(MRManager.getVarRegion(VDX1, SFC));
64+
Loc LY1 = loc::MemRegionVal(MRManager.getVarRegion(VDY1, SFC));
4265

43-
Store StInit = StMgr.getInitialStore(SFC).getStore();
44-
SVal Zero = SVB.makeZeroVal(ACtx.IntTy);
45-
SVal One = SVB.makeIntVal(1, ACtx.IntTy);
46-
SVal NarrowZero = SVB.makeZeroVal(ACtx.CharTy);
66+
Store StInit = SManager.getInitialStore(SFC).getStore();
67+
SVal Zero = Builder.makeZeroVal(ASTCtxt.IntTy);
68+
SVal One = Builder.makeIntVal(1, ASTCtxt.IntTy);
69+
SVal NarrowZero = Builder.makeZeroVal(ASTCtxt.CharTy);
4770

4871
// Bind(Zero)
49-
Store StX0 =
50-
StMgr.Bind(StInit, LX0, Zero).getStore();
51-
ASSERT_EQ(Zero, StMgr.getBinding(StX0, LX0, ACtx.IntTy));
72+
Store StX0 = SManager.Bind(StInit, LX0, Zero).getStore();
73+
EXPECT_EQ(Zero, SManager.getBinding(StX0, LX0, ASTCtxt.IntTy));
5274

5375
// BindDefaultInitial(Zero)
5476
Store StY0 =
55-
StMgr.BindDefaultInitial(StInit, LY0.getAsRegion(), Zero).getStore();
56-
ASSERT_EQ(Zero, StMgr.getBinding(StY0, LY0, ACtx.IntTy));
57-
ASSERT_EQ(Zero, *StMgr.getDefaultBinding(StY0, LY0.getAsRegion()));
77+
SManager.BindDefaultInitial(StInit, LY0.getAsRegion(), Zero).getStore();
78+
EXPECT_EQ(Zero, SManager.getBinding(StY0, LY0, ASTCtxt.IntTy));
79+
EXPECT_EQ(Zero, *SManager.getDefaultBinding(StY0, LY0.getAsRegion()));
5880

5981
// BindDefaultZero()
60-
Store StZ0 =
61-
StMgr.BindDefaultZero(StInit, LZ0.getAsRegion()).getStore();
82+
Store StZ0 = SManager.BindDefaultZero(StInit, LZ0.getAsRegion()).getStore();
6283
// BindDefaultZero wipes the region with '0 S8b', not with out Zero.
6384
// Direct load, however, does give us back the object of the type
6485
// that we specify for loading.
65-
ASSERT_EQ(Zero, StMgr.getBinding(StZ0, LZ0, ACtx.IntTy));
66-
ASSERT_EQ(NarrowZero, *StMgr.getDefaultBinding(StZ0, LZ0.getAsRegion()));
86+
EXPECT_EQ(Zero, SManager.getBinding(StZ0, LZ0, ASTCtxt.IntTy));
87+
EXPECT_EQ(NarrowZero, *SManager.getDefaultBinding(StZ0, LZ0.getAsRegion()));
6788

6889
// Bind(One)
69-
Store StX1 =
70-
StMgr.Bind(StInit, LX1, One).getStore();
71-
ASSERT_EQ(One, StMgr.getBinding(StX1, LX1, ACtx.IntTy));
90+
Store StX1 = SManager.Bind(StInit, LX1, One).getStore();
91+
EXPECT_EQ(One, SManager.getBinding(StX1, LX1, ASTCtxt.IntTy));
7292

7393
// BindDefaultInitial(One)
7494
Store StY1 =
75-
StMgr.BindDefaultInitial(StInit, LY1.getAsRegion(), One).getStore();
76-
ASSERT_EQ(One, StMgr.getBinding(StY1, LY1, ACtx.IntTy));
77-
ASSERT_EQ(One, *StMgr.getDefaultBinding(StY1, LY1.getAsRegion()));
95+
SManager.BindDefaultInitial(StInit, LY1.getAsRegion(), One).getStore();
96+
EXPECT_EQ(One, SManager.getBinding(StY1, LY1, ASTCtxt.IntTy));
97+
EXPECT_EQ(One, *SManager.getDefaultBinding(StY1, LY1.getAsRegion()));
7898
}
7999

80100
public:
81-
VariableBindConsumer(CompilerInstance &C) : ExprEngineConsumer(C) {}
101+
using StoreTestConsumer::StoreTestConsumer;
102+
};
82103

83-
bool HandleTopLevelDecl(DeclGroupRef DG) override {
84-
for (const auto *D : DG)
85-
performTest(D);
86-
return true;
104+
TEST(Store, VariableBind) {
105+
EXPECT_TRUE(tooling::runToolOnCode(
106+
std::make_unique<TestAction<VariableBindConsumer>>(),
107+
"void foo() { int x0, y0, z0, x1, y1; }"));
108+
}
109+
110+
class LiteralCompoundConsumer : public StoreTestConsumer {
111+
void performTest(const Decl *D) override {
112+
StoreManager &SManager = Eng.getStoreManager();
113+
SValBuilder &Builder = Eng.getSValBuilder();
114+
MemRegionManager &MRManager = SManager.getRegionManager();
115+
ASTContext &ASTCtxt = Eng.getContext();
116+
117+
using namespace ast_matchers;
118+
119+
const auto *CL = findNode<CompoundLiteralExpr>(D, compoundLiteralExpr());
120+
121+
const StackFrameContext *SFC =
122+
Eng.getAnalysisDeclContextManager().getStackFrame(D);
123+
124+
QualType Int = ASTCtxt.IntTy;
125+
126+
// Get region for 'test'
127+
const SubRegion *CLRegion = MRManager.getCompoundLiteralRegion(CL, SFC);
128+
129+
// Get value for 'test[0]'
130+
NonLoc Zero = Builder.makeIntVal(0, false);
131+
loc::MemRegionVal ZeroElement(
132+
MRManager.getElementRegion(ASTCtxt.IntTy, Zero, CLRegion, ASTCtxt));
133+
134+
Store StInit = SManager.getInitialStore(SFC).getStore();
135+
// Let's bind constant 1 to 'test[0]'
136+
SVal One = Builder.makeIntVal(1, Int);
137+
Store StX = SManager.Bind(StInit, ZeroElement, One).getStore();
138+
139+
// And make sure that we can read this binding back as it was
140+
EXPECT_EQ(One, SManager.getBinding(StX, ZeroElement, Int));
87141
}
88-
};
89142

90-
class VariableBindAction : public ASTFrontendAction {
91143
public:
92-
std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &Compiler,
93-
StringRef File) override {
94-
return std::make_unique<VariableBindConsumer>(Compiler);
95-
}
144+
using StoreTestConsumer::StoreTestConsumer;
96145
};
97146

98-
TEST(Store, VariableBind) {
99-
EXPECT_TRUE(tooling::runToolOnCode(std::make_unique<VariableBindAction>(),
100-
"void foo() { int x0, y0, z0, x1, y1; }"));
147+
TEST(Store, LiteralCompound) {
148+
EXPECT_TRUE(tooling::runToolOnCode(
149+
std::make_unique<TestAction<LiteralCompoundConsumer>>(),
150+
"void foo() { int *test = (int[]){ 1, 2, 3 }; }", "input.c"));
101151
}
102152

103153
} // namespace

0 commit comments

Comments
 (0)