brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.4 KiB · 73390a8 Raw
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