344 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9#include "clang/Analysis/FlowSensitive/RecordOps.h"10#include "TestingSupport.h"11#include "clang/AST/Type.h"12#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"13#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"14#include "clang/Analysis/FlowSensitive/NoopLattice.h"15#include "clang/Analysis/FlowSensitive/StorageLocation.h"16#include "llvm/ADT/StringMap.h"17#include "llvm/Testing/Support/Error.h"18#include "gtest/gtest.h"19#include <string>20 21namespace clang {22namespace dataflow {23namespace test {24namespace {25 26void runDataflow(27 llvm::StringRef Code,28 std::function<llvm::StringMap<QualType>(QualType)> SyntheticFieldCallback,29 std::function<30 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,31 ASTContext &)>32 VerifyResults) {33 ASSERT_THAT_ERROR(checkDataflowWithNoopAnalysis(34 Code, ast_matchers::hasName("target"), VerifyResults,35 {BuiltinOptions()}, LangStandard::lang_cxx17,36 SyntheticFieldCallback),37 llvm::Succeeded());38}39 40const FieldDecl *getFieldNamed(RecordDecl *RD, llvm::StringRef Name) {41 for (const FieldDecl *FD : RD->fields())42 if (FD->getName() == Name)43 return FD;44 assert(false);45 return nullptr;46}47 48TEST(RecordOpsTest, CopyRecord) {49 std::string Code = R"(50 struct S {51 int outer_int;52 int &ref;53 struct {54 int inner_int;55 } inner;56 };57 void target(S s1, S s2) {58 (void)s1.outer_int;59 (void)s1.ref;60 (void)s1.inner.inner_int;61 // [[p]]62 }63 )";64 runDataflow(65 Code,66 [](QualType Ty) -> llvm::StringMap<QualType> {67 std::string TypeAsString = Ty.getAsString();68 if (TypeAsString != "S" && TypeAsString != "struct S")69 return {};70 QualType IntTy =71 getFieldNamed(Ty->getAsRecordDecl(), "outer_int")->getType();72 return {{"synth_int", IntTy}};73 },74 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,75 ASTContext &ASTCtx) {76 Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();77 78 const ValueDecl *OuterIntDecl = findValueDecl(ASTCtx, "outer_int");79 const ValueDecl *RefDecl = findValueDecl(ASTCtx, "ref");80 const ValueDecl *InnerDecl = findValueDecl(ASTCtx, "inner");81 const ValueDecl *InnerIntDecl = findValueDecl(ASTCtx, "inner_int");82 83 auto &S1 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s1");84 auto &S2 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s2");85 auto &Inner1 = *cast<RecordStorageLocation>(S1.getChild(*InnerDecl));86 auto &Inner2 = *cast<RecordStorageLocation>(S2.getChild(*InnerDecl));87 88 EXPECT_NE(getFieldValue(&S1, *OuterIntDecl, Env),89 getFieldValue(&S2, *OuterIntDecl, Env));90 EXPECT_NE(S1.getChild(*RefDecl), S2.getChild(*RefDecl));91 EXPECT_NE(getFieldValue(&Inner1, *InnerIntDecl, Env),92 getFieldValue(&Inner2, *InnerIntDecl, Env));93 EXPECT_NE(Env.getValue(S1.getSyntheticField("synth_int")),94 Env.getValue(S2.getSyntheticField("synth_int")));95 96 copyRecord(S1, S2, Env);97 98 EXPECT_EQ(getFieldValue(&S1, *OuterIntDecl, Env),99 getFieldValue(&S2, *OuterIntDecl, Env));100 EXPECT_EQ(S1.getChild(*RefDecl), S2.getChild(*RefDecl));101 EXPECT_EQ(getFieldValue(&Inner1, *InnerIntDecl, Env),102 getFieldValue(&Inner2, *InnerIntDecl, Env));103 EXPECT_EQ(Env.getValue(S1.getSyntheticField("synth_int")),104 Env.getValue(S2.getSyntheticField("synth_int")));105 });106}107 108TEST(RecordOpsTest, RecordsEqual) {109 std::string Code = R"(110 struct S {111 int outer_int;112 int &ref;113 struct {114 int inner_int;115 } inner;116 };117 void target(S s1, S s2) {118 (void)s1.outer_int;119 (void)s1.ref;120 (void)s1.inner.inner_int;121 // [[p]]122 }123 )";124 runDataflow(125 Code,126 [](QualType Ty) -> llvm::StringMap<QualType> {127 std::string TypeAsString = Ty.getAsString();128 if (TypeAsString != "S" && TypeAsString != "struct S")129 return {};130 QualType IntTy =131 getFieldNamed(Ty->getAsRecordDecl(), "outer_int")->getType();132 return {{"synth_int", IntTy}};133 },134 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,135 ASTContext &ASTCtx) {136 Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();137 138 const ValueDecl *OuterIntDecl = findValueDecl(ASTCtx, "outer_int");139 const ValueDecl *RefDecl = findValueDecl(ASTCtx, "ref");140 const ValueDecl *InnerDecl = findValueDecl(ASTCtx, "inner");141 const ValueDecl *InnerIntDecl = findValueDecl(ASTCtx, "inner_int");142 143 auto &S1 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s1");144 auto &S2 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s2");145 auto &Inner2 = *cast<RecordStorageLocation>(S2.getChild(*InnerDecl));146 147 Env.setValue(S1.getSyntheticField("synth_int"),148 Env.create<IntegerValue>());149 150 // Strategy: Create two equal records, then verify each of the various151 // ways in which records can differ causes recordsEqual to return false.152 // changes we can make to the record.153 154 // This test reuses the same objects for multiple checks, which isn't155 // great, but seems better than duplicating the setup code for every156 // check.157 158 copyRecord(S1, S2, Env);159 EXPECT_TRUE(recordsEqual(S1, S2, Env));160 161 // S2 has a different outer_int.162 Env.setValue(*S2.getChild(*OuterIntDecl), Env.create<IntegerValue>());163 EXPECT_FALSE(recordsEqual(S1, S2, Env));164 copyRecord(S1, S2, Env);165 EXPECT_TRUE(recordsEqual(S1, S2, Env));166 167 // S2 doesn't have outer_int at all.168 Env.clearValue(*S2.getChild(*OuterIntDecl));169 EXPECT_FALSE(recordsEqual(S1, S2, Env));170 copyRecord(S1, S2, Env);171 EXPECT_TRUE(recordsEqual(S1, S2, Env));172 173 // S2 has a different ref.174 S2.setChild(*RefDecl, &Env.createStorageLocation(175 RefDecl->getType().getNonReferenceType()));176 EXPECT_FALSE(recordsEqual(S1, S2, Env));177 copyRecord(S1, S2, Env);178 EXPECT_TRUE(recordsEqual(S1, S2, Env));179 180 // S2 as a different inner_int.181 Env.setValue(*Inner2.getChild(*InnerIntDecl),182 Env.create<IntegerValue>());183 EXPECT_FALSE(recordsEqual(S1, S2, Env));184 copyRecord(S1, S2, Env);185 EXPECT_TRUE(recordsEqual(S1, S2, Env));186 187 // S2 has a different synth_int.188 Env.setValue(S2.getSyntheticField("synth_int"),189 Env.create<IntegerValue>());190 EXPECT_FALSE(recordsEqual(S1, S2, Env));191 copyRecord(S1, S2, Env);192 EXPECT_TRUE(recordsEqual(S1, S2, Env));193 194 // S2 doesn't have a value for synth_int.195 Env.clearValue(S2.getSyntheticField("synth_int"));196 EXPECT_FALSE(recordsEqual(S1, S2, Env));197 copyRecord(S1, S2, Env);198 EXPECT_TRUE(recordsEqual(S1, S2, Env));199 });200}201 202TEST(RecordOpsTest, CopyRecordBetweenDerivedAndBase) {203 std::string Code = R"(204 struct A {205 int i;206 };207 208 struct B : public A {209 };210 211 void target(A a, B b) {212 (void)a.i;213 // [[p]]214 }215 )";216 auto SyntheticFieldCallback = [](QualType Ty) -> llvm::StringMap<QualType> {217 CXXRecordDecl *ADecl = nullptr;218 std::string TypeAsString = Ty.getAsString();219 if (TypeAsString == "A" || TypeAsString == "struct A")220 ADecl = Ty->getAsCXXRecordDecl();221 else if (TypeAsString == "B" || TypeAsString == "struct B")222 ADecl = Ty->getAsCXXRecordDecl()223 ->bases_begin()224 ->getType()225 ->getAsCXXRecordDecl();226 else227 return {};228 QualType IntTy = getFieldNamed(ADecl, "i")->getType();229 return {{"synth_int", IntTy}};230 };231 // Test copying derived to base class.232 runDataflow(233 Code, SyntheticFieldCallback,234 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,235 ASTContext &ASTCtx) {236 Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();237 238 const ValueDecl *IDecl = findValueDecl(ASTCtx, "i");239 auto &A = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "a");240 auto &B = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "b");241 242 EXPECT_NE(Env.getValue(*A.getChild(*IDecl)),243 Env.getValue(*B.getChild(*IDecl)));244 EXPECT_NE(Env.getValue(A.getSyntheticField("synth_int")),245 Env.getValue(B.getSyntheticField("synth_int")));246 247 copyRecord(B, A, Env);248 249 EXPECT_EQ(Env.getValue(*A.getChild(*IDecl)),250 Env.getValue(*B.getChild(*IDecl)));251 EXPECT_EQ(Env.getValue(A.getSyntheticField("synth_int")),252 Env.getValue(B.getSyntheticField("synth_int")));253 });254 // Test copying base to derived class.255 runDataflow(256 Code, SyntheticFieldCallback,257 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,258 ASTContext &ASTCtx) {259 Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();260 261 const ValueDecl *IDecl = findValueDecl(ASTCtx, "i");262 auto &A = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "a");263 auto &B = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "b");264 265 EXPECT_NE(Env.getValue(*A.getChild(*IDecl)),266 Env.getValue(*B.getChild(*IDecl)));267 EXPECT_NE(Env.getValue(A.getSyntheticField("synth_int")),268 Env.getValue(B.getSyntheticField("synth_int")));269 270 copyRecord(A, B, Env);271 272 EXPECT_EQ(Env.getValue(*A.getChild(*IDecl)),273 Env.getValue(*B.getChild(*IDecl)));274 EXPECT_EQ(Env.getValue(A.getSyntheticField("synth_int")),275 Env.getValue(B.getSyntheticField("synth_int")));276 });277}278 279TEST(RecordOpsTest, CopyRecordWithExplicitSharedBaseTypeToCopy) {280 std::string Code = R"(281 struct Base {282 bool BaseField;283 char UnmodeledField;284 };285 286 struct DerivedOne : public Base {287 int DerivedOneField;288 };289 290 struct DerivedTwo : public Base {291 int DerivedTwoField;292 };293 294 void target(Base B, DerivedOne D1, DerivedTwo D2) {295 (void) B.BaseField;296 // [[p]]297 }298 )";299 auto SyntheticFieldCallback = [](QualType Ty) -> llvm::StringMap<QualType> {300 CXXRecordDecl *BaseDecl = nullptr;301 std::string TypeAsString = Ty.getAsString();302 if (TypeAsString == "Base")303 BaseDecl = Ty->getAsCXXRecordDecl();304 else if (TypeAsString == "DerivedOne" || TypeAsString == "DerivedTwo")305 BaseDecl = Ty->getAsCXXRecordDecl()306 ->bases_begin()307 ->getType()308 ->getAsCXXRecordDecl();309 else310 return {};311 QualType FieldType = getFieldNamed(BaseDecl, "BaseField")->getType();312 return {{"synth_field", FieldType}};313 };314 // Test copying derived to base class.315 runDataflow(316 Code, SyntheticFieldCallback,317 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,318 ASTContext &ASTCtx) {319 Environment Env = getEnvironmentAtAnnotation(Results, "p").fork();320 321 const ValueDecl *BaseFieldDecl = findValueDecl(ASTCtx, "BaseField");322 auto &B = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "B");323 auto &D1 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "D1");324 auto &D2 = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "D2");325 326 EXPECT_NE(Env.getValue(*D1.getChild(*BaseFieldDecl)),327 Env.getValue(*D2.getChild(*BaseFieldDecl)));328 EXPECT_NE(Env.getValue(D1.getSyntheticField("synth_field")),329 Env.getValue(D2.getSyntheticField("synth_field")));330 331 copyRecord(D1, D2, Env, B.getType());332 333 EXPECT_EQ(Env.getValue(*D1.getChild(*BaseFieldDecl)),334 Env.getValue(*D2.getChild(*BaseFieldDecl)));335 EXPECT_EQ(Env.getValue(D1.getSyntheticField("synth_field")),336 Env.getValue(D2.getSyntheticField("synth_field")));337 });338}339 340} // namespace341} // namespace test342} // namespace dataflow343} // namespace clang344