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< 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()); 32 } 33 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; 42 }; 43 void target(S s1, S s2) { 44 (void)s1.outer_int; 45 (void)s1.ref; 46 (void)s1.inner.inner_int; 47 // [[p]] 48 } 49 )"; 50 runDataflow( 51 Code, 52 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 53 ASTContext &ASTCtx) { 54 Environment Env = getEnvironmentAtAnnotation(Results, "p").fork(); 55 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"); 60 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)); 65 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)); 71 72 auto *S1Val = cast<RecordValue>(Env.getValue(S1)); 73 auto *S2Val = cast<RecordValue>(Env.getValue(S2)); 74 EXPECT_NE(S1Val, S2Val); 75 76 S1Val->setProperty("prop", Env.getBoolLiteralValue(true)); 77 78 copyRecord(S1, S2, Env); 79 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)); 85 86 S1Val = cast<RecordValue>(Env.getValue(S1)); 87 S2Val = cast<RecordValue>(Env.getValue(S2)); 88 EXPECT_NE(S1Val, S2Val); 89 90 EXPECT_EQ(S2Val->getProperty("prop"), &Env.getBoolLiteralValue(true)); 91 }); 92 } 93 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; 102 }; 103 void target(S s1, S s2) { 104 (void)s1.outer_int; 105 (void)s1.ref; 106 (void)s1.inner.inner_int; 107 // [[p]] 108 } 109 )"; 110 runDataflow( 111 Code, 112 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 113 ASTContext &ASTCtx) { 114 Environment Env = getEnvironmentAtAnnotation(Results, "p").fork(); 115 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"); 120 121 auto &S1 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s1"); 122 auto &S2 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s2"); 123 auto &Inner2 = *cast<RecordStorageLocation>(S2.getChild(*InnerDecl)); 124 125 cast<RecordValue>(Env.getValue(S1)) 126 ->setProperty("prop", Env.getBoolLiteralValue(true)); 127 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. 131 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. 135 136 copyRecord(S1, S2, Env); 137 EXPECT_TRUE(recordsEqual(S1, S2, Env)); 138 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)); 144 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)); 150 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)); 157 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)); 164 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)); 171 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)); 179 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)); 186 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)); 194 }); 195 } 196 197 TEST(TransferTest, CopyRecordFromDerivedToBase) { 198 std::string Code = R"( 199 struct A { 200 int i; 201 }; 202 203 struct B : public A { 204 }; 205 206 void target(A a, B b) { 207 (void)a.i; 208 // [[p]] 209 } 210 )"; 211 runDataflow( 212 Code, 213 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results, 214 ASTContext &ASTCtx) { 215 Environment Env = getEnvironmentAtAnnotation(Results, "p").fork(); 216 217 const ValueDecl *IDecl = findValueDecl(ASTCtx, "i"); 218 auto &A = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "a"); 219 auto &B = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "b"); 220 221 EXPECT_NE(Env.getValue(*A.getChild(*IDecl)), 222 Env.getValue(*B.getChild(*IDecl))); 223 224 copyRecord(B, A, Env); 225 226 EXPECT_EQ(Env.getValue(*A.getChild(*IDecl)), 227 Env.getValue(*B.getChild(*IDecl))); 228 }); 229 } 230 231 } // namespace 232 } // namespace test 233 } // namespace dataflow 234 } // namespace clang 235