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 //===----------------------------------------------------------------------===// 8 9 #include "clang/Analysis/FlowSensitive/RecordOps.h" 10 #include "TestingSupport.h" 11 #include "llvm/Testing/Support/Error.h" 12 #include "gtest/gtest.h" 13 14 namespace clang { 15 namespace dataflow { 16 namespace test { 17 namespace { 18 19 void runDataflow( 20 llvm::StringRef Code, 21 std::function<llvm::StringMap<QualType>(QualType)> SyntheticFieldCallback, 22 std::function< 23 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &, 24 ASTContext &)> 25 VerifyResults) { 26 ASSERT_THAT_ERROR(checkDataflowWithNoopAnalysis( 27 Code, ast_matchers::hasName("target"), VerifyResults, 28 {BuiltinOptions()}, LangStandard::lang_cxx17, 29 SyntheticFieldCallback), 30 llvm::Succeeded()); 31 } 32 33 const FieldDecl *getFieldNamed(RecordDecl *RD, llvm::StringRef Name) { 34 for (const FieldDecl *FD : RD->fields()) 35 if (FD->getName() == Name) 36 return FD; 37 assert(false); 38 return nullptr; 39 } 40 41 TEST(RecordOpsTest, CopyRecord) { 42 std::string Code = R"( 43 struct S { 44 int outer_int; 45 int &ref; 46 struct { 47 int inner_int; 48 } inner; 49 }; 50 void target(S s1, S s2) { 51 (void)s1.outer_int; 52 (void)s1.ref; 53 (void)s1.inner.inner_int; 54 // [[p]] 55 } 56 )"; 57 runDataflow( 58 Code, 59 [](QualType Ty) -> llvm::StringMap<QualType> { 60 if (Ty.getAsString() != "S") 61 return {}; 62 QualType IntTy = 63 getFieldNamed(Ty->getAsRecordDecl(), "outer_int")->getType(); 64 return {{"synth_int", IntTy}}; 65 }, 66 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 67 ASTContext &ASTCtx) { 68 Environment Env = getEnvironmentAtAnnotation(Results, "p").fork(); 69 70 const ValueDecl *OuterIntDecl = findValueDecl(ASTCtx, "outer_int"); 71 const ValueDecl *RefDecl = findValueDecl(ASTCtx, "ref"); 72 const ValueDecl *InnerDecl = findValueDecl(ASTCtx, "inner"); 73 const ValueDecl *InnerIntDecl = findValueDecl(ASTCtx, "inner_int"); 74 75 auto &S1 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s1"); 76 auto &S2 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s2"); 77 auto &Inner1 = *cast<RecordStorageLocation>(S1.getChild(*InnerDecl)); 78 auto &Inner2 = *cast<RecordStorageLocation>(S2.getChild(*InnerDecl)); 79 80 EXPECT_NE(getFieldValue(&S1, *OuterIntDecl, Env), 81 getFieldValue(&S2, *OuterIntDecl, Env)); 82 EXPECT_NE(S1.getChild(*RefDecl), S2.getChild(*RefDecl)); 83 EXPECT_NE(getFieldValue(&Inner1, *InnerIntDecl, Env), 84 getFieldValue(&Inner2, *InnerIntDecl, Env)); 85 EXPECT_NE(Env.getValue(S1.getSyntheticField("synth_int")), 86 Env.getValue(S2.getSyntheticField("synth_int"))); 87 88 auto *S1Val = cast<RecordValue>(Env.getValue(S1)); 89 auto *S2Val = cast<RecordValue>(Env.getValue(S2)); 90 EXPECT_NE(S1Val, S2Val); 91 92 S1Val->setProperty("prop", Env.getBoolLiteralValue(true)); 93 94 copyRecord(S1, S2, Env); 95 96 EXPECT_EQ(getFieldValue(&S1, *OuterIntDecl, Env), 97 getFieldValue(&S2, *OuterIntDecl, Env)); 98 EXPECT_EQ(S1.getChild(*RefDecl), S2.getChild(*RefDecl)); 99 EXPECT_EQ(getFieldValue(&Inner1, *InnerIntDecl, Env), 100 getFieldValue(&Inner2, *InnerIntDecl, Env)); 101 EXPECT_EQ(Env.getValue(S1.getSyntheticField("synth_int")), 102 Env.getValue(S2.getSyntheticField("synth_int"))); 103 104 S1Val = cast<RecordValue>(Env.getValue(S1)); 105 S2Val = cast<RecordValue>(Env.getValue(S2)); 106 EXPECT_NE(S1Val, S2Val); 107 108 EXPECT_EQ(S2Val->getProperty("prop"), &Env.getBoolLiteralValue(true)); 109 }); 110 } 111 112 TEST(RecordOpsTest, RecordsEqual) { 113 std::string Code = R"( 114 struct S { 115 int outer_int; 116 int &ref; 117 struct { 118 int inner_int; 119 } inner; 120 }; 121 void target(S s1, S s2) { 122 (void)s1.outer_int; 123 (void)s1.ref; 124 (void)s1.inner.inner_int; 125 // [[p]] 126 } 127 )"; 128 runDataflow( 129 Code, 130 [](QualType Ty) -> llvm::StringMap<QualType> { 131 if (Ty.getAsString() != "S") 132 return {}; 133 QualType IntTy = 134 getFieldNamed(Ty->getAsRecordDecl(), "outer_int")->getType(); 135 return {{"synth_int", IntTy}}; 136 }, 137 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 138 ASTContext &ASTCtx) { 139 Environment Env = getEnvironmentAtAnnotation(Results, "p").fork(); 140 141 const ValueDecl *OuterIntDecl = findValueDecl(ASTCtx, "outer_int"); 142 const ValueDecl *RefDecl = findValueDecl(ASTCtx, "ref"); 143 const ValueDecl *InnerDecl = findValueDecl(ASTCtx, "inner"); 144 const ValueDecl *InnerIntDecl = findValueDecl(ASTCtx, "inner_int"); 145 146 auto &S1 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s1"); 147 auto &S2 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s2"); 148 auto &Inner2 = *cast<RecordStorageLocation>(S2.getChild(*InnerDecl)); 149 150 Env.setValue(S1.getSyntheticField("synth_int"), 151 Env.create<IntegerValue>()); 152 153 cast<RecordValue>(Env.getValue(S1)) 154 ->setProperty("prop", Env.getBoolLiteralValue(true)); 155 156 // Strategy: Create two equal records, then verify each of the various 157 // ways in which records can differ causes recordsEqual to return false. 158 // changes we can make to the record. 159 160 // This test reuses the same objects for multiple checks, which isn't 161 // great, but seems better than duplicating the setup code for every 162 // check. 163 164 copyRecord(S1, S2, Env); 165 EXPECT_TRUE(recordsEqual(S1, S2, Env)); 166 167 // S2 has a different outer_int. 168 Env.setValue(*S2.getChild(*OuterIntDecl), Env.create<IntegerValue>()); 169 EXPECT_FALSE(recordsEqual(S1, S2, Env)); 170 copyRecord(S1, S2, Env); 171 EXPECT_TRUE(recordsEqual(S1, S2, Env)); 172 173 // S2 doesn't have outer_int at all. 174 Env.clearValue(*S2.getChild(*OuterIntDecl)); 175 EXPECT_FALSE(recordsEqual(S1, S2, Env)); 176 copyRecord(S1, S2, Env); 177 EXPECT_TRUE(recordsEqual(S1, S2, Env)); 178 179 // S2 has a different ref. 180 S2.setChild(*RefDecl, &Env.createStorageLocation( 181 RefDecl->getType().getNonReferenceType())); 182 EXPECT_FALSE(recordsEqual(S1, S2, Env)); 183 copyRecord(S1, S2, Env); 184 EXPECT_TRUE(recordsEqual(S1, S2, Env)); 185 186 // S2 as a different inner_int. 187 Env.setValue(*Inner2.getChild(*InnerIntDecl), 188 Env.create<IntegerValue>()); 189 EXPECT_FALSE(recordsEqual(S1, S2, Env)); 190 copyRecord(S1, S2, Env); 191 EXPECT_TRUE(recordsEqual(S1, S2, Env)); 192 193 // S2 has a different synth_int. 194 Env.setValue(S2.getSyntheticField("synth_int"), 195 Env.create<IntegerValue>()); 196 EXPECT_FALSE(recordsEqual(S1, S2, Env)); 197 copyRecord(S1, S2, Env); 198 EXPECT_TRUE(recordsEqual(S1, S2, Env)); 199 200 // S2 doesn't have a value for synth_int. 201 Env.clearValue(S2.getSyntheticField("synth_int")); 202 EXPECT_FALSE(recordsEqual(S1, S2, Env)); 203 copyRecord(S1, S2, Env); 204 EXPECT_TRUE(recordsEqual(S1, S2, Env)); 205 206 // S1 and S2 have the same property with different values. 207 cast<RecordValue>(Env.getValue(S2)) 208 ->setProperty("prop", Env.getBoolLiteralValue(false)); 209 EXPECT_FALSE(recordsEqual(S1, S2, Env)); 210 copyRecord(S1, S2, Env); 211 EXPECT_TRUE(recordsEqual(S1, S2, Env)); 212 213 // S1 has a property that S2 doesn't have. 214 cast<RecordValue>(Env.getValue(S1)) 215 ->setProperty("other_prop", Env.getBoolLiteralValue(false)); 216 EXPECT_FALSE(recordsEqual(S1, S2, Env)); 217 // We modified S1 this time, so need to copy back the other way. 218 copyRecord(S2, S1, Env); 219 EXPECT_TRUE(recordsEqual(S1, S2, Env)); 220 221 // S2 has a property that S1 doesn't have. 222 cast<RecordValue>(Env.getValue(S2)) 223 ->setProperty("other_prop", Env.getBoolLiteralValue(false)); 224 EXPECT_FALSE(recordsEqual(S1, S2, Env)); 225 copyRecord(S1, S2, Env); 226 EXPECT_TRUE(recordsEqual(S1, S2, Env)); 227 228 // S1 and S2 have the same number of properties, but with different 229 // names. 230 cast<RecordValue>(Env.getValue(S1)) 231 ->setProperty("prop1", Env.getBoolLiteralValue(false)); 232 cast<RecordValue>(Env.getValue(S2)) 233 ->setProperty("prop2", Env.getBoolLiteralValue(false)); 234 EXPECT_FALSE(recordsEqual(S1, S2, Env)); 235 }); 236 } 237 238 TEST(TransferTest, CopyRecordFromDerivedToBase) { 239 std::string Code = R"( 240 struct A { 241 int i; 242 }; 243 244 struct B : public A { 245 }; 246 247 void target(A a, B b) { 248 (void)a.i; 249 // [[p]] 250 } 251 )"; 252 runDataflow( 253 Code, /*SyntheticFieldCallback=*/{}, 254 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 255 ASTContext &ASTCtx) { 256 Environment Env = getEnvironmentAtAnnotation(Results, "p").fork(); 257 258 const ValueDecl *IDecl = findValueDecl(ASTCtx, "i"); 259 auto &A = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "a"); 260 auto &B = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "b"); 261 262 EXPECT_NE(Env.getValue(*A.getChild(*IDecl)), 263 Env.getValue(*B.getChild(*IDecl))); 264 265 copyRecord(B, A, Env); 266 267 EXPECT_EQ(Env.getValue(*A.getChild(*IDecl)), 268 Env.getValue(*B.getChild(*IDecl))); 269 }); 270 } 271 272 } // namespace 273 } // namespace test 274 } // namespace dataflow 275 } // namespace clang 276