1 //===- unittests/Analysis/FlowSensitive/RecordOpsTest.cpp -----------------===//
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7 //===----------------------------------------------------------------------===//
9 #include "clang/Analysis/FlowSensitive/RecordOps.h"
10 #include "TestingSupport.h"
11 #include "llvm/Testing/Support/Error.h"
12 #include "gtest/gtest.h"
22 void(const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &,
25 LangStandard::Kind Std
= LangStandard::lang_cxx17
,
26 llvm::StringRef TargetFun
= "target") {
28 checkDataflowWithNoopAnalysis(Code
, VerifyResults
,
29 DataflowAnalysisOptions
{BuiltinOptions
{}},
34 TEST(RecordOpsTest
, CopyRecord
) {
35 std::string Code
= R
"(
43 void target(S s1, S s2) {
46 (void)s1.inner.inner_int;
52 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
54 Environment Env
= getEnvironmentAtAnnotation(Results
, "p").fork();
56 const ValueDecl
*OuterIntDecl
= findValueDecl(ASTCtx
, "outer_int");
57 const ValueDecl
*RefDecl
= findValueDecl(ASTCtx
, "ref");
58 const ValueDecl
*InnerDecl
= findValueDecl(ASTCtx
, "inner");
59 const ValueDecl
*InnerIntDecl
= findValueDecl(ASTCtx
, "inner_int");
61 auto &S1
= getLocForDecl
<RecordStorageLocation
>(ASTCtx
, Env
, "s1");
62 auto &S2
= getLocForDecl
<RecordStorageLocation
>(ASTCtx
, Env
, "s2");
63 auto &Inner1
= *cast
<RecordStorageLocation
>(S1
.getChild(*InnerDecl
));
64 auto &Inner2
= *cast
<RecordStorageLocation
>(S2
.getChild(*InnerDecl
));
66 EXPECT_NE(getFieldValue(&S1
, *OuterIntDecl
, Env
),
67 getFieldValue(&S2
, *OuterIntDecl
, Env
));
68 EXPECT_NE(S1
.getChild(*RefDecl
), S2
.getChild(*RefDecl
));
69 EXPECT_NE(getFieldValue(&Inner1
, *InnerIntDecl
, Env
),
70 getFieldValue(&Inner2
, *InnerIntDecl
, Env
));
72 auto *S1Val
= cast
<RecordValue
>(Env
.getValue(S1
));
73 auto *S2Val
= cast
<RecordValue
>(Env
.getValue(S2
));
74 EXPECT_NE(S1Val
, S2Val
);
76 S1Val
->setProperty("prop", Env
.getBoolLiteralValue(true));
78 copyRecord(S1
, S2
, Env
);
80 EXPECT_EQ(getFieldValue(&S1
, *OuterIntDecl
, Env
),
81 getFieldValue(&S2
, *OuterIntDecl
, Env
));
82 EXPECT_EQ(S1
.getChild(*RefDecl
), S2
.getChild(*RefDecl
));
83 EXPECT_EQ(getFieldValue(&Inner1
, *InnerIntDecl
, Env
),
84 getFieldValue(&Inner2
, *InnerIntDecl
, Env
));
86 S1Val
= cast
<RecordValue
>(Env
.getValue(S1
));
87 S2Val
= cast
<RecordValue
>(Env
.getValue(S2
));
88 EXPECT_NE(S1Val
, S2Val
);
90 EXPECT_EQ(S2Val
->getProperty("prop"), &Env
.getBoolLiteralValue(true));
94 TEST(RecordOpsTest
, RecordsEqual
) {
95 std::string Code
= R
"(
103 void target(S s1, S s2) {
106 (void)s1.inner.inner_int;
112 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
113 ASTContext
&ASTCtx
) {
114 Environment Env
= getEnvironmentAtAnnotation(Results
, "p").fork();
116 const ValueDecl
*OuterIntDecl
= findValueDecl(ASTCtx
, "outer_int");
117 const ValueDecl
*RefDecl
= findValueDecl(ASTCtx
, "ref");
118 const ValueDecl
*InnerDecl
= findValueDecl(ASTCtx
, "inner");
119 const ValueDecl
*InnerIntDecl
= findValueDecl(ASTCtx
, "inner_int");
121 auto &S1
= getLocForDecl
<RecordStorageLocation
>(ASTCtx
, Env
, "s1");
122 auto &S2
= getLocForDecl
<RecordStorageLocation
>(ASTCtx
, Env
, "s2");
123 auto &Inner2
= *cast
<RecordStorageLocation
>(S2
.getChild(*InnerDecl
));
125 cast
<RecordValue
>(Env
.getValue(S1
))
126 ->setProperty("prop", Env
.getBoolLiteralValue(true));
128 // Strategy: Create two equal records, then verify each of the various
129 // ways in which records can differ causes recordsEqual to return false.
130 // changes we can make to the record.
132 // This test reuses the same objects for multiple checks, which isn't
133 // great, but seems better than duplicating the setup code for every
136 copyRecord(S1
, S2
, Env
);
137 EXPECT_TRUE(recordsEqual(S1
, S2
, Env
));
139 // S2 has a different outer_int.
140 Env
.setValue(*S2
.getChild(*OuterIntDecl
), Env
.create
<IntegerValue
>());
141 EXPECT_FALSE(recordsEqual(S1
, S2
, Env
));
142 copyRecord(S1
, S2
, Env
);
143 EXPECT_TRUE(recordsEqual(S1
, S2
, Env
));
145 // S2 doesn't have outer_int at all.
146 Env
.clearValue(*S2
.getChild(*OuterIntDecl
));
147 EXPECT_FALSE(recordsEqual(S1
, S2
, Env
));
148 copyRecord(S1
, S2
, Env
);
149 EXPECT_TRUE(recordsEqual(S1
, S2
, Env
));
151 // S2 has a different ref.
152 S2
.setChild(*RefDecl
, &Env
.createStorageLocation(
153 RefDecl
->getType().getNonReferenceType()));
154 EXPECT_FALSE(recordsEqual(S1
, S2
, Env
));
155 copyRecord(S1
, S2
, Env
);
156 EXPECT_TRUE(recordsEqual(S1
, S2
, Env
));
158 // S2 as a different inner_int.
159 Env
.setValue(*Inner2
.getChild(*InnerIntDecl
),
160 Env
.create
<IntegerValue
>());
161 EXPECT_FALSE(recordsEqual(S1
, S2
, Env
));
162 copyRecord(S1
, S2
, Env
);
163 EXPECT_TRUE(recordsEqual(S1
, S2
, Env
));
165 // S1 and S2 have the same property with different values.
166 cast
<RecordValue
>(Env
.getValue(S2
))
167 ->setProperty("prop", Env
.getBoolLiteralValue(false));
168 EXPECT_FALSE(recordsEqual(S1
, S2
, Env
));
169 copyRecord(S1
, S2
, Env
);
170 EXPECT_TRUE(recordsEqual(S1
, S2
, Env
));
172 // S1 has a property that S2 doesn't have.
173 cast
<RecordValue
>(Env
.getValue(S1
))
174 ->setProperty("other_prop", Env
.getBoolLiteralValue(false));
175 EXPECT_FALSE(recordsEqual(S1
, S2
, Env
));
176 // We modified S1 this time, so need to copy back the other way.
177 copyRecord(S2
, S1
, Env
);
178 EXPECT_TRUE(recordsEqual(S1
, S2
, Env
));
180 // S2 has a property that S1 doesn't have.
181 cast
<RecordValue
>(Env
.getValue(S2
))
182 ->setProperty("other_prop", Env
.getBoolLiteralValue(false));
183 EXPECT_FALSE(recordsEqual(S1
, S2
, Env
));
184 copyRecord(S1
, S2
, Env
);
185 EXPECT_TRUE(recordsEqual(S1
, S2
, Env
));
187 // S1 and S2 have the same number of properties, but with different
189 cast
<RecordValue
>(Env
.getValue(S1
))
190 ->setProperty("prop1", Env
.getBoolLiteralValue(false));
191 cast
<RecordValue
>(Env
.getValue(S2
))
192 ->setProperty("prop2", Env
.getBoolLiteralValue(false));
193 EXPECT_FALSE(recordsEqual(S1
, S2
, Env
));
197 TEST(TransferTest
, CopyRecordFromDerivedToBase
) {
198 std::string Code
= R
"(
203 struct B : public A {
206 void target(A a, B b) {
213 [](const llvm::StringMap
<DataflowAnalysisState
<NoopLattice
>> &Results
,
214 ASTContext
&ASTCtx
) {
215 Environment Env
= getEnvironmentAtAnnotation(Results
, "p").fork();
217 const ValueDecl
*IDecl
= findValueDecl(ASTCtx
, "i");
218 auto &A
= getLocForDecl
<RecordStorageLocation
>(ASTCtx
, Env
, "a");
219 auto &B
= getLocForDecl
<RecordStorageLocation
>(ASTCtx
, Env
, "b");
221 EXPECT_NE(Env
.getValue(*A
.getChild(*IDecl
)),
222 Env
.getValue(*B
.getChild(*IDecl
)));
224 copyRecord(B
, A
, Env
);
226 EXPECT_EQ(Env
.getValue(*A
.getChild(*IDecl
)),
227 Env
.getValue(*B
.getChild(*IDecl
)));
233 } // namespace dataflow