Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / clang / unittests / Analysis / FlowSensitive / RecordOpsTest.cpp
blob6ba1c2ebd833d4756c569ebc7e61be58228cf2a5
1 //===- unittests/Analysis/FlowSensitive/RecordOpsTest.cpp -----------------===//
2 //
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
6 //
7 //===----------------------------------------------------------------------===//
9 #include "clang/Analysis/FlowSensitive/RecordOps.h"
10 #include "TestingSupport.h"
11 #include "llvm/Testing/Support/Error.h"
12 #include "gtest/gtest.h"
14 namespace clang {
15 namespace dataflow {
16 namespace test {
17 namespace {
19 void runDataflow(
20 llvm::StringRef Code,
21 std::function<
22 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
23 ASTContext &)>
24 VerifyResults,
25 LangStandard::Kind Std = LangStandard::lang_cxx17,
26 llvm::StringRef TargetFun = "target") {
27 ASSERT_THAT_ERROR(
28 checkDataflowWithNoopAnalysis(Code, VerifyResults,
29 DataflowAnalysisOptions{BuiltinOptions{}},
30 Std, TargetFun),
31 llvm::Succeeded());
34 TEST(RecordOpsTest, CopyRecord) {
35 std::string Code = R"(
36 struct S {
37 int outer_int;
38 int &ref;
39 struct {
40 int inner_int;
41 } inner;
43 void target(S s1, S s2) {
44 (void)s1.outer_int;
45 (void)s1.ref;
46 (void)s1.inner.inner_int;
47 // [[p]]
49 )";
50 runDataflow(
51 Code,
52 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
53 ASTContext &ASTCtx) {
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));
91 });
94 TEST(RecordOpsTest, RecordsEqual) {
95 std::string Code = R"(
96 struct S {
97 int outer_int;
98 int &ref;
99 struct {
100 int inner_int;
101 } inner;
103 void target(S s1, S s2) {
104 (void)s1.outer_int;
105 (void)s1.ref;
106 (void)s1.inner.inner_int;
107 // [[p]]
110 runDataflow(
111 Code,
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
134 // check.
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
188 // names.
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"(
199 struct A {
200 int i;
203 struct B : public A {
206 void target(A a, B b) {
207 (void)a.i;
208 // [[p]]
211 runDataflow(
212 Code,
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)));
231 } // namespace
232 } // namespace test
233 } // namespace dataflow
234 } // namespace clang