xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/CachedConstAccessorsLatticeTest.cpp (revision 72a28a3bf0b539bcdfd8f41905675ce6a890c0ac)
187dd5dc8SJan Voung //===- unittests/Analysis/FlowSensitive/CachedConstAccessorsLatticeTest.cpp ==//
287dd5dc8SJan Voung //
387dd5dc8SJan Voung // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
487dd5dc8SJan Voung // See https://llvm.org/LICENSE.txt for license information.
587dd5dc8SJan Voung // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
687dd5dc8SJan Voung //
787dd5dc8SJan Voung //===----------------------------------------------------------------------===//
887dd5dc8SJan Voung 
987dd5dc8SJan Voung #include "clang/Analysis/FlowSensitive/CachedConstAccessorsLattice.h"
1087dd5dc8SJan Voung 
1187dd5dc8SJan Voung #include <cassert>
1287dd5dc8SJan Voung #include <memory>
1387dd5dc8SJan Voung 
1487dd5dc8SJan Voung #include "clang/AST/Decl.h"
1587dd5dc8SJan Voung #include "clang/AST/DeclBase.h"
1687dd5dc8SJan Voung #include "clang/AST/DeclCXX.h"
1787dd5dc8SJan Voung #include "clang/AST/Expr.h"
1887dd5dc8SJan Voung #include "clang/AST/Type.h"
1987dd5dc8SJan Voung #include "clang/ASTMatchers/ASTMatchFinder.h"
2087dd5dc8SJan Voung #include "clang/ASTMatchers/ASTMatchers.h"
2187dd5dc8SJan Voung #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
2287dd5dc8SJan Voung #include "clang/Analysis/FlowSensitive/DataflowLattice.h"
2387dd5dc8SJan Voung #include "clang/Analysis/FlowSensitive/NoopLattice.h"
2487dd5dc8SJan Voung #include "clang/Analysis/FlowSensitive/StorageLocation.h"
2587dd5dc8SJan Voung #include "clang/Analysis/FlowSensitive/Value.h"
2687dd5dc8SJan Voung #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
2787dd5dc8SJan Voung #include "clang/Basic/LLVM.h"
2887dd5dc8SJan Voung #include "clang/Testing/TestAST.h"
2987dd5dc8SJan Voung #include "gmock/gmock.h"
3087dd5dc8SJan Voung #include "gtest/gtest.h"
3187dd5dc8SJan Voung 
3287dd5dc8SJan Voung namespace clang::dataflow {
3387dd5dc8SJan Voung namespace {
3487dd5dc8SJan Voung 
3587dd5dc8SJan Voung using ast_matchers::BoundNodes;
3687dd5dc8SJan Voung using ast_matchers::callee;
3787dd5dc8SJan Voung using ast_matchers::cxxMemberCallExpr;
3887dd5dc8SJan Voung using ast_matchers::functionDecl;
3987dd5dc8SJan Voung using ast_matchers::hasName;
4087dd5dc8SJan Voung using ast_matchers::match;
4187dd5dc8SJan Voung using ast_matchers::selectFirst;
4287dd5dc8SJan Voung 
4387dd5dc8SJan Voung using dataflow::DataflowAnalysisContext;
4487dd5dc8SJan Voung using dataflow::Environment;
4587dd5dc8SJan Voung using dataflow::LatticeJoinEffect;
4687dd5dc8SJan Voung using dataflow::RecordStorageLocation;
4787dd5dc8SJan Voung using dataflow::Value;
4887dd5dc8SJan Voung using dataflow::WatchedLiteralsSolver;
4987dd5dc8SJan Voung 
5087dd5dc8SJan Voung using testing::SizeIs;
5187dd5dc8SJan Voung 
5287dd5dc8SJan Voung NamedDecl *lookup(StringRef Name, const DeclContext &DC) {
5387dd5dc8SJan Voung   auto Result = DC.lookup(&DC.getParentASTContext().Idents.get(Name));
5487dd5dc8SJan Voung   EXPECT_TRUE(Result.isSingleResult()) << Name;
5587dd5dc8SJan Voung   return Result.front();
5687dd5dc8SJan Voung }
5787dd5dc8SJan Voung 
5887dd5dc8SJan Voung class CachedConstAccessorsLatticeTest : public ::testing::Test {
5987dd5dc8SJan Voung protected:
6087dd5dc8SJan Voung   using LatticeT = CachedConstAccessorsLattice<NoopLattice>;
6187dd5dc8SJan Voung 
6287dd5dc8SJan Voung   DataflowAnalysisContext DACtx{std::make_unique<WatchedLiteralsSolver>()};
6387dd5dc8SJan Voung   Environment Env{DACtx};
6487dd5dc8SJan Voung };
6587dd5dc8SJan Voung 
6687dd5dc8SJan Voung // Basic test AST with two const methods (return a value, and return a ref).
6787dd5dc8SJan Voung struct CommonTestInputs {
6887dd5dc8SJan Voung   CommonTestInputs()
6987dd5dc8SJan Voung       : AST(R"cpp(
7087dd5dc8SJan Voung     struct S {
7187dd5dc8SJan Voung       int *valProperty() const;
7287dd5dc8SJan Voung       int &refProperty() const;
7387dd5dc8SJan Voung     };
7487dd5dc8SJan Voung     void target() {
7587dd5dc8SJan Voung       S s;
7687dd5dc8SJan Voung       s.valProperty();
7787dd5dc8SJan Voung       S s2;
7887dd5dc8SJan Voung       s2.refProperty();
7987dd5dc8SJan Voung     }
8087dd5dc8SJan Voung   )cpp") {
8187dd5dc8SJan Voung     auto *SDecl = cast<CXXRecordDecl>(
8287dd5dc8SJan Voung         lookup("S", *AST.context().getTranslationUnitDecl()));
8387dd5dc8SJan Voung     SType = AST.context().getRecordType(SDecl);
8487dd5dc8SJan Voung     CallVal = selectFirst<CallExpr>(
8587dd5dc8SJan Voung         "call",
8687dd5dc8SJan Voung         match(cxxMemberCallExpr(callee(functionDecl(hasName("valProperty"))))
8787dd5dc8SJan Voung                   .bind("call"),
8887dd5dc8SJan Voung               AST.context()));
8987dd5dc8SJan Voung     assert(CallVal != nullptr);
9087dd5dc8SJan Voung 
9187dd5dc8SJan Voung     CallRef = selectFirst<CallExpr>(
9287dd5dc8SJan Voung         "call",
9387dd5dc8SJan Voung         match(cxxMemberCallExpr(callee(functionDecl(hasName("refProperty"))))
9487dd5dc8SJan Voung                   .bind("call"),
9587dd5dc8SJan Voung               AST.context()));
9687dd5dc8SJan Voung     assert(CallRef != nullptr);
9787dd5dc8SJan Voung   }
9887dd5dc8SJan Voung 
9987dd5dc8SJan Voung   TestAST AST;
10087dd5dc8SJan Voung   QualType SType;
10187dd5dc8SJan Voung   const CallExpr *CallVal;
10287dd5dc8SJan Voung   const CallExpr *CallRef;
10387dd5dc8SJan Voung };
10487dd5dc8SJan Voung 
10587dd5dc8SJan Voung TEST_F(CachedConstAccessorsLatticeTest,
10687dd5dc8SJan Voung        SamePrimitiveValBeforeClearOrDiffAfterClear) {
10787dd5dc8SJan Voung   CommonTestInputs Inputs;
10887dd5dc8SJan Voung   auto *CE = Inputs.CallVal;
10987dd5dc8SJan Voung   RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(),
11087dd5dc8SJan Voung                             {});
11187dd5dc8SJan Voung 
11287dd5dc8SJan Voung   LatticeT Lattice;
11387dd5dc8SJan Voung   Value *Val1 = Lattice.getOrCreateConstMethodReturnValue(Loc, CE, Env);
11487dd5dc8SJan Voung   Value *Val2 = Lattice.getOrCreateConstMethodReturnValue(Loc, CE, Env);
11587dd5dc8SJan Voung 
11687dd5dc8SJan Voung   EXPECT_EQ(Val1, Val2);
11787dd5dc8SJan Voung 
11887dd5dc8SJan Voung   Lattice.clearConstMethodReturnValues(Loc);
11987dd5dc8SJan Voung   Value *Val3 = Lattice.getOrCreateConstMethodReturnValue(Loc, CE, Env);
12087dd5dc8SJan Voung 
12187dd5dc8SJan Voung   EXPECT_NE(Val3, Val1);
12287dd5dc8SJan Voung   EXPECT_NE(Val3, Val2);
12387dd5dc8SJan Voung }
12487dd5dc8SJan Voung 
12587dd5dc8SJan Voung TEST_F(CachedConstAccessorsLatticeTest, SameLocBeforeClearOrDiffAfterClear) {
12687dd5dc8SJan Voung   CommonTestInputs Inputs;
12787dd5dc8SJan Voung   auto *CE = Inputs.CallRef;
12887dd5dc8SJan Voung   RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(),
12987dd5dc8SJan Voung                             {});
13087dd5dc8SJan Voung 
13187dd5dc8SJan Voung   LatticeT Lattice;
13287dd5dc8SJan Voung   auto NopInit = [](StorageLocation &) {};
13387dd5dc8SJan Voung   StorageLocation *Loc1 = Lattice.getOrCreateConstMethodReturnStorageLocation(
13487dd5dc8SJan Voung       Loc, CE, Env, NopInit);
13587dd5dc8SJan Voung   auto NotCalled = [](StorageLocation &) {
13687dd5dc8SJan Voung     ASSERT_TRUE(false) << "Not reached";
13787dd5dc8SJan Voung   };
13887dd5dc8SJan Voung   StorageLocation *Loc2 = Lattice.getOrCreateConstMethodReturnStorageLocation(
13987dd5dc8SJan Voung       Loc, CE, Env, NotCalled);
14087dd5dc8SJan Voung 
14187dd5dc8SJan Voung   EXPECT_EQ(Loc1, Loc2);
14287dd5dc8SJan Voung 
14387dd5dc8SJan Voung   Lattice.clearConstMethodReturnStorageLocations(Loc);
14487dd5dc8SJan Voung   StorageLocation *Loc3 = Lattice.getOrCreateConstMethodReturnStorageLocation(
14587dd5dc8SJan Voung       Loc, CE, Env, NopInit);
14687dd5dc8SJan Voung 
14787dd5dc8SJan Voung   EXPECT_NE(Loc3, Loc1);
14887dd5dc8SJan Voung   EXPECT_NE(Loc3, Loc2);
14987dd5dc8SJan Voung }
15087dd5dc8SJan Voung 
15187dd5dc8SJan Voung TEST_F(CachedConstAccessorsLatticeTest,
152*72a28a3bSJan Voung        SameLocBeforeClearOrDiffAfterClearWithCallee) {
153*72a28a3bSJan Voung   CommonTestInputs Inputs;
154*72a28a3bSJan Voung   auto *CE = Inputs.CallRef;
155*72a28a3bSJan Voung   RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(),
156*72a28a3bSJan Voung                             {});
157*72a28a3bSJan Voung 
158*72a28a3bSJan Voung   LatticeT Lattice;
159*72a28a3bSJan Voung   auto NopInit = [](StorageLocation &) {};
160*72a28a3bSJan Voung   const FunctionDecl *Callee = CE->getDirectCallee();
161*72a28a3bSJan Voung   ASSERT_NE(Callee, nullptr);
162*72a28a3bSJan Voung   StorageLocation &Loc1 = Lattice.getOrCreateConstMethodReturnStorageLocation(
163*72a28a3bSJan Voung       Loc, Callee, Env, NopInit);
164*72a28a3bSJan Voung   auto NotCalled = [](StorageLocation &) {
165*72a28a3bSJan Voung     ASSERT_TRUE(false) << "Not reached";
166*72a28a3bSJan Voung   };
167*72a28a3bSJan Voung   StorageLocation &Loc2 = Lattice.getOrCreateConstMethodReturnStorageLocation(
168*72a28a3bSJan Voung       Loc, Callee, Env, NotCalled);
169*72a28a3bSJan Voung 
170*72a28a3bSJan Voung   EXPECT_EQ(&Loc1, &Loc2);
171*72a28a3bSJan Voung 
172*72a28a3bSJan Voung   Lattice.clearConstMethodReturnStorageLocations(Loc);
173*72a28a3bSJan Voung   StorageLocation &Loc3 = Lattice.getOrCreateConstMethodReturnStorageLocation(
174*72a28a3bSJan Voung       Loc, Callee, Env, NopInit);
175*72a28a3bSJan Voung 
176*72a28a3bSJan Voung   EXPECT_NE(&Loc3, &Loc1);
177*72a28a3bSJan Voung   EXPECT_NE(&Loc3, &Loc2);
178*72a28a3bSJan Voung }
179*72a28a3bSJan Voung 
180*72a28a3bSJan Voung TEST_F(CachedConstAccessorsLatticeTest,
18187dd5dc8SJan Voung        SameStructValBeforeClearOrDiffAfterClear) {
18287dd5dc8SJan Voung   TestAST AST(R"cpp(
18387dd5dc8SJan Voung     struct S {
18487dd5dc8SJan Voung       S structValProperty() const;
18587dd5dc8SJan Voung     };
18687dd5dc8SJan Voung     void target() {
18787dd5dc8SJan Voung       S s;
18887dd5dc8SJan Voung       s.structValProperty();
18987dd5dc8SJan Voung     }
19087dd5dc8SJan Voung   )cpp");
19187dd5dc8SJan Voung   auto *SDecl =
19287dd5dc8SJan Voung       cast<CXXRecordDecl>(lookup("S", *AST.context().getTranslationUnitDecl()));
19387dd5dc8SJan Voung   QualType SType = AST.context().getRecordType(SDecl);
19487dd5dc8SJan Voung   const CallExpr *CE = selectFirst<CallExpr>(
19587dd5dc8SJan Voung       "call", match(cxxMemberCallExpr(
19687dd5dc8SJan Voung                         callee(functionDecl(hasName("structValProperty"))))
19787dd5dc8SJan Voung                         .bind("call"),
19887dd5dc8SJan Voung                     AST.context()));
19987dd5dc8SJan Voung   ASSERT_NE(CE, nullptr);
20087dd5dc8SJan Voung 
20187dd5dc8SJan Voung   RecordStorageLocation Loc(SType, RecordStorageLocation::FieldToLoc(), {});
20287dd5dc8SJan Voung 
20387dd5dc8SJan Voung   LatticeT Lattice;
20487dd5dc8SJan Voung   // Accessors that return a record by value are modeled by a record storage
20587dd5dc8SJan Voung   // location (instead of a Value).
20687dd5dc8SJan Voung   auto NopInit = [](StorageLocation &) {};
20787dd5dc8SJan Voung   StorageLocation *Loc1 = Lattice.getOrCreateConstMethodReturnStorageLocation(
20887dd5dc8SJan Voung       Loc, CE, Env, NopInit);
20987dd5dc8SJan Voung   auto NotCalled = [](StorageLocation &) {
21087dd5dc8SJan Voung     ASSERT_TRUE(false) << "Not reached";
21187dd5dc8SJan Voung   };
21287dd5dc8SJan Voung   StorageLocation *Loc2 = Lattice.getOrCreateConstMethodReturnStorageLocation(
21387dd5dc8SJan Voung       Loc, CE, Env, NotCalled);
21487dd5dc8SJan Voung 
21587dd5dc8SJan Voung   EXPECT_EQ(Loc1, Loc2);
21687dd5dc8SJan Voung 
21787dd5dc8SJan Voung   Lattice.clearConstMethodReturnStorageLocations(Loc);
21887dd5dc8SJan Voung   StorageLocation *Loc3 = Lattice.getOrCreateConstMethodReturnStorageLocation(
21987dd5dc8SJan Voung       Loc, CE, Env, NopInit);
22087dd5dc8SJan Voung 
22187dd5dc8SJan Voung   EXPECT_NE(Loc3, Loc1);
22287dd5dc8SJan Voung   EXPECT_NE(Loc3, Loc1);
22387dd5dc8SJan Voung }
22487dd5dc8SJan Voung 
22587dd5dc8SJan Voung TEST_F(CachedConstAccessorsLatticeTest, ClearDifferentLocs) {
22687dd5dc8SJan Voung   CommonTestInputs Inputs;
22787dd5dc8SJan Voung   auto *CE = Inputs.CallRef;
22887dd5dc8SJan Voung   RecordStorageLocation LocS1(Inputs.SType, RecordStorageLocation::FieldToLoc(),
22987dd5dc8SJan Voung                               {});
23087dd5dc8SJan Voung   RecordStorageLocation LocS2(Inputs.SType, RecordStorageLocation::FieldToLoc(),
23187dd5dc8SJan Voung                               {});
23287dd5dc8SJan Voung 
23387dd5dc8SJan Voung   LatticeT Lattice;
23487dd5dc8SJan Voung   auto NopInit = [](StorageLocation &) {};
23587dd5dc8SJan Voung   StorageLocation *RetLoc1 =
23687dd5dc8SJan Voung       Lattice.getOrCreateConstMethodReturnStorageLocation(LocS1, CE, Env,
23787dd5dc8SJan Voung                                                           NopInit);
23887dd5dc8SJan Voung   Lattice.clearConstMethodReturnStorageLocations(LocS2);
23987dd5dc8SJan Voung   auto NotCalled = [](StorageLocation &) {
24087dd5dc8SJan Voung     ASSERT_TRUE(false) << "Not reached";
24187dd5dc8SJan Voung   };
24287dd5dc8SJan Voung   StorageLocation *RetLoc2 =
24387dd5dc8SJan Voung       Lattice.getOrCreateConstMethodReturnStorageLocation(LocS1, CE, Env,
24487dd5dc8SJan Voung                                                           NotCalled);
24587dd5dc8SJan Voung 
24687dd5dc8SJan Voung   EXPECT_EQ(RetLoc1, RetLoc2);
24787dd5dc8SJan Voung }
24887dd5dc8SJan Voung 
24987dd5dc8SJan Voung TEST_F(CachedConstAccessorsLatticeTest, DifferentValsFromDifferentLocs) {
25087dd5dc8SJan Voung   TestAST AST(R"cpp(
25187dd5dc8SJan Voung     struct S {
25287dd5dc8SJan Voung       int *valProperty() const;
25387dd5dc8SJan Voung     };
25487dd5dc8SJan Voung     void target() {
25587dd5dc8SJan Voung       S s1;
25687dd5dc8SJan Voung       s1.valProperty();
25787dd5dc8SJan Voung       S s2;
25887dd5dc8SJan Voung       s2.valProperty();
25987dd5dc8SJan Voung     }
26087dd5dc8SJan Voung   )cpp");
26187dd5dc8SJan Voung   auto *SDecl =
26287dd5dc8SJan Voung       cast<CXXRecordDecl>(lookup("S", *AST.context().getTranslationUnitDecl()));
26387dd5dc8SJan Voung   QualType SType = AST.context().getRecordType(SDecl);
26487dd5dc8SJan Voung   SmallVector<BoundNodes, 1> valPropertyCalls =
26587dd5dc8SJan Voung       match(cxxMemberCallExpr(callee(functionDecl(hasName("valProperty"))))
26687dd5dc8SJan Voung                 .bind("call"),
26787dd5dc8SJan Voung             AST.context());
26887dd5dc8SJan Voung   ASSERT_THAT(valPropertyCalls, SizeIs(2));
26987dd5dc8SJan Voung 
27087dd5dc8SJan Voung   const CallExpr *CE1 = selectFirst<CallExpr>("call", valPropertyCalls);
27187dd5dc8SJan Voung   ASSERT_NE(CE1, nullptr);
27287dd5dc8SJan Voung 
27387dd5dc8SJan Voung   valPropertyCalls.erase(valPropertyCalls.begin());
27487dd5dc8SJan Voung   const CallExpr *CE2 = selectFirst<CallExpr>("call", valPropertyCalls);
27587dd5dc8SJan Voung   ASSERT_NE(CE2, nullptr);
27687dd5dc8SJan Voung   ASSERT_NE(CE1, CE2);
27787dd5dc8SJan Voung 
27887dd5dc8SJan Voung   RecordStorageLocation LocS1(SType, RecordStorageLocation::FieldToLoc(), {});
27987dd5dc8SJan Voung   RecordStorageLocation LocS2(SType, RecordStorageLocation::FieldToLoc(), {});
28087dd5dc8SJan Voung 
28187dd5dc8SJan Voung   LatticeT Lattice;
28287dd5dc8SJan Voung   Value *Val1 = Lattice.getOrCreateConstMethodReturnValue(LocS1, CE1, Env);
28387dd5dc8SJan Voung   Value *Val2 = Lattice.getOrCreateConstMethodReturnValue(LocS2, CE2, Env);
28487dd5dc8SJan Voung 
28587dd5dc8SJan Voung   EXPECT_NE(Val1, Val2);
28687dd5dc8SJan Voung }
28787dd5dc8SJan Voung 
28887dd5dc8SJan Voung TEST_F(CachedConstAccessorsLatticeTest, JoinSameNoop) {
28987dd5dc8SJan Voung   CommonTestInputs Inputs;
29087dd5dc8SJan Voung   auto *CE = Inputs.CallVal;
29187dd5dc8SJan Voung   RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(),
29287dd5dc8SJan Voung                             {});
29387dd5dc8SJan Voung 
29487dd5dc8SJan Voung   LatticeT EmptyLattice;
29587dd5dc8SJan Voung   LatticeT EmptyLattice2;
29687dd5dc8SJan Voung   EXPECT_EQ(EmptyLattice.join(EmptyLattice2), LatticeJoinEffect::Unchanged);
29787dd5dc8SJan Voung 
29887dd5dc8SJan Voung   LatticeT Lattice1;
29987dd5dc8SJan Voung   Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env);
30087dd5dc8SJan Voung   EXPECT_EQ(Lattice1.join(Lattice1), LatticeJoinEffect::Unchanged);
30187dd5dc8SJan Voung }
30287dd5dc8SJan Voung 
30387dd5dc8SJan Voung TEST_F(CachedConstAccessorsLatticeTest, ProducesNewValueAfterJoinDistinct) {
30487dd5dc8SJan Voung   CommonTestInputs Inputs;
30587dd5dc8SJan Voung   auto *CE = Inputs.CallVal;
30687dd5dc8SJan Voung   RecordStorageLocation Loc(Inputs.SType, RecordStorageLocation::FieldToLoc(),
30787dd5dc8SJan Voung                             {});
30887dd5dc8SJan Voung 
30987dd5dc8SJan Voung   // L1 w/ v vs L2 empty
31087dd5dc8SJan Voung   LatticeT Lattice1;
31187dd5dc8SJan Voung   Value *Val1 = Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env);
31287dd5dc8SJan Voung 
31387dd5dc8SJan Voung   LatticeT EmptyLattice;
31487dd5dc8SJan Voung 
31587dd5dc8SJan Voung   EXPECT_EQ(Lattice1.join(EmptyLattice), LatticeJoinEffect::Changed);
31687dd5dc8SJan Voung   Value *ValAfterJoin =
31787dd5dc8SJan Voung       Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env);
31887dd5dc8SJan Voung 
31987dd5dc8SJan Voung   EXPECT_NE(ValAfterJoin, Val1);
32087dd5dc8SJan Voung 
32187dd5dc8SJan Voung   // L1 w/ v1 vs L3 w/ v2
32287dd5dc8SJan Voung   LatticeT Lattice3;
32387dd5dc8SJan Voung   Value *Val3 = Lattice3.getOrCreateConstMethodReturnValue(Loc, CE, Env);
32487dd5dc8SJan Voung 
32587dd5dc8SJan Voung   EXPECT_EQ(Lattice1.join(Lattice3), LatticeJoinEffect::Changed);
32687dd5dc8SJan Voung   Value *ValAfterJoin2 =
32787dd5dc8SJan Voung       Lattice1.getOrCreateConstMethodReturnValue(Loc, CE, Env);
32887dd5dc8SJan Voung 
32987dd5dc8SJan Voung   EXPECT_NE(ValAfterJoin2, ValAfterJoin);
33087dd5dc8SJan Voung   EXPECT_NE(ValAfterJoin2, Val3);
33187dd5dc8SJan Voung }
33287dd5dc8SJan Voung 
33387dd5dc8SJan Voung } // namespace
33487dd5dc8SJan Voung } // namespace clang::dataflow
335