xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp (revision e7481f6ee591678e3a70569c2e87597f148adb3e)
1 //===- unittests/Analysis/FlowSensitive/TransferTest.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 "NoopAnalysis.h"
10 #include "TestingSupport.h"
11 #include "clang/AST/ASTContext.h"
12 #include "clang/AST/Decl.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
14 #include "clang/ASTMatchers/ASTMatchers.h"
15 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
16 #include "clang/Analysis/FlowSensitive/StorageLocation.h"
17 #include "clang/Analysis/FlowSensitive/Value.h"
18 #include "llvm/ADT/ArrayRef.h"
19 #include "llvm/ADT/StringRef.h"
20 #include "llvm/Support/Casting.h"
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
23 #include <cassert>
24 #include <string>
25 #include <utility>
26 
27 namespace {
28 
29 using namespace clang;
30 using namespace dataflow;
31 using ::testing::_;
32 using ::testing::ElementsAre;
33 using ::testing::IsNull;
34 using ::testing::NotNull;
35 using ::testing::Pair;
36 
37 class TransferTest : public ::testing::Test {
38 protected:
39   template <typename Matcher>
40   void runDataflow(llvm::StringRef Code, Matcher Match) {
41     test::checkDataflow<NoopAnalysis>(
42         Code, "target",
43         [](ASTContext &C, Environment &) { return NoopAnalysis(C); },
44         [&Match](llvm::ArrayRef<
45                      std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
46                      Results,
47                  ASTContext &ASTCtx) { Match(Results, ASTCtx); },
48         {"-fsyntax-only", "-std=c++17"});
49   }
50 };
51 
52 /// Returns the `ValueDecl` for the given identifier.
53 ///
54 /// Requirements:
55 ///
56 ///  `Name` must be unique in `ASTCtx`.
57 static const ValueDecl *findValueDecl(ASTContext &ASTCtx,
58                                       llvm::StringRef Name) {
59   auto TargetNodes = ast_matchers::match(
60       ast_matchers::valueDecl(ast_matchers::hasName(Name)).bind("v"), ASTCtx);
61   assert(TargetNodes.size() == 1 && "Name must be unique");
62   auto *const Result = ast_matchers::selectFirst<ValueDecl>("v", TargetNodes);
63   assert(Result != nullptr);
64   return Result;
65 }
66 
67 TEST_F(TransferTest, IntVarDecl) {
68   std::string Code = R"(
69     void target() {
70       int foo;
71       // [[p]]
72     }
73   )";
74   runDataflow(
75       Code, [](llvm::ArrayRef<
76                    std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
77                    Results,
78                ASTContext &ASTCtx) {
79         ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
80         const Environment &Env = Results[0].second.Env;
81 
82         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
83         ASSERT_THAT(FooDecl, NotNull());
84 
85         const StorageLocation *FooLoc =
86             Env.getStorageLocation(*FooDecl, SkipPast::None);
87         ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
88 
89         const Value *FooVal = Env.getValue(*FooLoc);
90         ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
91       });
92 }
93 
94 TEST_F(TransferTest, StructVarDecl) {
95   std::string Code = R"(
96     struct Foo {
97       int Bar;
98     };
99 
100     void target() {
101       Foo foo;
102       // [[p]]
103     }
104   )";
105   runDataflow(
106       Code, [](llvm::ArrayRef<
107                    std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
108                    Results,
109                ASTContext &ASTCtx) {
110         ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
111         const Environment &Env = Results[0].second.Env;
112 
113         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
114         ASSERT_THAT(FooDecl, NotNull());
115 
116         ASSERT_TRUE(FooDecl->getType()->isStructureType());
117         auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
118 
119         FieldDecl *BarDecl = nullptr;
120         for (FieldDecl *Field : FooFields) {
121           if (Field->getNameAsString() == "Bar") {
122             BarDecl = Field;
123           } else {
124             FAIL() << "Unexpected field: " << Field->getNameAsString();
125           }
126         }
127         ASSERT_THAT(BarDecl, NotNull());
128 
129         const auto *FooLoc = cast<AggregateStorageLocation>(
130             Env.getStorageLocation(*FooDecl, SkipPast::None));
131         const auto *BarLoc =
132             cast<ScalarStorageLocation>(&FooLoc->getChild(*BarDecl));
133 
134         const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
135         const auto *BarVal = cast<IntegerValue>(&FooVal->getChild(*BarDecl));
136         ASSERT_EQ(Env.getValue(*BarLoc), BarVal);
137       });
138 }
139 
140 TEST_F(TransferTest, ClassVarDecl) {
141   std::string Code = R"(
142     class Foo {
143       int Bar;
144     };
145 
146     void target() {
147       Foo foo;
148       // [[p]]
149     }
150   )";
151   runDataflow(
152       Code, [](llvm::ArrayRef<
153                    std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
154                    Results,
155                ASTContext &ASTCtx) {
156         ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
157         const Environment &Env = Results[0].second.Env;
158 
159         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
160         ASSERT_THAT(FooDecl, NotNull());
161 
162         ASSERT_TRUE(FooDecl->getType()->isClassType());
163         auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
164 
165         FieldDecl *BarDecl = nullptr;
166         for (FieldDecl *Field : FooFields) {
167           if (Field->getNameAsString() == "Bar") {
168             BarDecl = Field;
169           } else {
170             FAIL() << "Unexpected field: " << Field->getNameAsString();
171           }
172         }
173         ASSERT_THAT(BarDecl, NotNull());
174 
175         const auto *FooLoc = cast<AggregateStorageLocation>(
176             Env.getStorageLocation(*FooDecl, SkipPast::None));
177         const auto *BarLoc =
178             cast<ScalarStorageLocation>(&FooLoc->getChild(*BarDecl));
179 
180         const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
181         const auto *BarVal = cast<IntegerValue>(&FooVal->getChild(*BarDecl));
182         ASSERT_EQ(Env.getValue(*BarLoc), BarVal);
183       });
184 }
185 
186 TEST_F(TransferTest, ReferenceVarDecl) {
187   std::string Code = R"(
188     struct Foo {};
189 
190     Foo& getFoo();
191 
192     void target() {
193       Foo& foo = getFoo();
194       // [[p]]
195     }
196   )";
197   runDataflow(
198       Code, [](llvm::ArrayRef<
199                    std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
200                    Results,
201                ASTContext &ASTCtx) {
202         ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
203         const Environment &Env = Results[0].second.Env;
204 
205         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
206         ASSERT_THAT(FooDecl, NotNull());
207 
208         const StorageLocation *FooLoc =
209             Env.getStorageLocation(*FooDecl, SkipPast::None);
210         ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
211 
212         const ReferenceValue *FooVal =
213             cast<ReferenceValue>(Env.getValue(*FooLoc));
214         const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc();
215         ASSERT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
216 
217         const Value *FooPointeeVal = Env.getValue(FooPointeeLoc);
218         ASSERT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal));
219       });
220 }
221 
222 TEST_F(TransferTest, SelfReferentialReferenceVarDecl) {
223   std::string Code = R"(
224     struct Foo;
225 
226     struct Baz {};
227 
228     struct Bar {
229       Foo& FooRef;
230       Foo* FooPtr;
231       Baz& BazRef;
232       Baz* BazPtr;
233     };
234 
235     struct Foo {
236       Bar& Bar;
237     };
238 
239     Foo& getFoo();
240 
241     void target() {
242       Foo& foo = getFoo();
243       // [[p]]
244     }
245   )";
246   runDataflow(Code, [](llvm::ArrayRef<std::pair<
247                            std::string, DataflowAnalysisState<NoopLattice>>>
248                            Results,
249                        ASTContext &ASTCtx) {
250     ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
251     const Environment &Env = Results[0].second.Env;
252 
253     const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
254     ASSERT_THAT(FooDecl, NotNull());
255 
256     ASSERT_TRUE(FooDecl->getType()->isReferenceType());
257     ASSERT_TRUE(FooDecl->getType().getNonReferenceType()->isStructureType());
258     const auto FooFields =
259         FooDecl->getType().getNonReferenceType()->getAsRecordDecl()->fields();
260 
261     FieldDecl *BarDecl = nullptr;
262     for (FieldDecl *Field : FooFields) {
263       if (Field->getNameAsString() == "Bar") {
264         BarDecl = Field;
265       } else {
266         FAIL() << "Unexpected field: " << Field->getNameAsString();
267       }
268     }
269     ASSERT_THAT(BarDecl, NotNull());
270 
271     ASSERT_TRUE(BarDecl->getType()->isReferenceType());
272     ASSERT_TRUE(BarDecl->getType().getNonReferenceType()->isStructureType());
273     const auto BarFields =
274         BarDecl->getType().getNonReferenceType()->getAsRecordDecl()->fields();
275 
276     FieldDecl *FooRefDecl = nullptr;
277     FieldDecl *FooPtrDecl = nullptr;
278     FieldDecl *BazRefDecl = nullptr;
279     FieldDecl *BazPtrDecl = nullptr;
280     for (FieldDecl *Field : BarFields) {
281       if (Field->getNameAsString() == "FooRef") {
282         FooRefDecl = Field;
283       } else if (Field->getNameAsString() == "FooPtr") {
284         FooPtrDecl = Field;
285       } else if (Field->getNameAsString() == "BazRef") {
286         BazRefDecl = Field;
287       } else if (Field->getNameAsString() == "BazPtr") {
288         BazPtrDecl = Field;
289       } else {
290         FAIL() << "Unexpected field: " << Field->getNameAsString();
291       }
292     }
293     ASSERT_THAT(FooRefDecl, NotNull());
294     ASSERT_THAT(FooPtrDecl, NotNull());
295     ASSERT_THAT(BazRefDecl, NotNull());
296     ASSERT_THAT(BazPtrDecl, NotNull());
297 
298     const auto *FooLoc = cast<ScalarStorageLocation>(
299         Env.getStorageLocation(*FooDecl, SkipPast::None));
300     const auto *FooVal = cast<ReferenceValue>(Env.getValue(*FooLoc));
301     const auto *FooPointeeVal =
302         cast<StructValue>(Env.getValue(FooVal->getPointeeLoc()));
303 
304     const auto *BarVal =
305         cast<ReferenceValue>(&FooPointeeVal->getChild(*BarDecl));
306     const auto *BarPointeeVal =
307         cast<StructValue>(Env.getValue(BarVal->getPointeeLoc()));
308 
309     const auto *FooRefVal =
310         cast<ReferenceValue>(&BarPointeeVal->getChild(*FooRefDecl));
311     const StorageLocation &FooRefPointeeLoc = FooRefVal->getPointeeLoc();
312     ASSERT_THAT(Env.getValue(FooRefPointeeLoc), IsNull());
313 
314     const auto *FooPtrVal =
315         cast<PointerValue>(&BarPointeeVal->getChild(*FooPtrDecl));
316     const StorageLocation &FooPtrPointeeLoc = FooPtrVal->getPointeeLoc();
317     ASSERT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull());
318 
319     const auto *BazRefVal =
320         cast<ReferenceValue>(&BarPointeeVal->getChild(*BazRefDecl));
321     const StorageLocation &BazRefPointeeLoc = BazRefVal->getPointeeLoc();
322     ASSERT_THAT(Env.getValue(BazRefPointeeLoc), NotNull());
323 
324     const auto *BazPtrVal =
325         cast<PointerValue>(&BarPointeeVal->getChild(*BazPtrDecl));
326     const StorageLocation &BazPtrPointeeLoc = BazPtrVal->getPointeeLoc();
327     ASSERT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull());
328   });
329 }
330 
331 TEST_F(TransferTest, PointerVarDecl) {
332   std::string Code = R"(
333     struct Foo {};
334 
335     Foo* getFoo();
336 
337     void target() {
338       Foo* foo = getFoo();
339       // [[p]]
340     }
341   )";
342   runDataflow(
343       Code, [](llvm::ArrayRef<
344                    std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
345                    Results,
346                ASTContext &ASTCtx) {
347         ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
348         const Environment &Env = Results[0].second.Env;
349 
350         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
351         ASSERT_THAT(FooDecl, NotNull());
352 
353         const StorageLocation *FooLoc =
354             Env.getStorageLocation(*FooDecl, SkipPast::None);
355         ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
356 
357         const PointerValue *FooVal = cast<PointerValue>(Env.getValue(*FooLoc));
358         const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc();
359         ASSERT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
360 
361         const Value *FooPointeeVal = Env.getValue(FooPointeeLoc);
362         ASSERT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal));
363       });
364 }
365 
366 TEST_F(TransferTest, SelfReferentialPointerVarDecl) {
367   std::string Code = R"(
368     struct Foo;
369 
370     struct Baz {};
371 
372     struct Bar {
373       Foo& FooRef;
374       Foo* FooPtr;
375       Baz& BazRef;
376       Baz* BazPtr;
377     };
378 
379     struct Foo {
380       Bar* Bar;
381     };
382 
383     Foo* getFoo();
384 
385     void target() {
386       Foo* foo = getFoo();
387       // [[p]]
388     }
389   )";
390   runDataflow(
391       Code, [](llvm::ArrayRef<
392                    std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
393                    Results,
394                ASTContext &ASTCtx) {
395         ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
396         const Environment &Env = Results[0].second.Env;
397 
398         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
399         ASSERT_THAT(FooDecl, NotNull());
400 
401         ASSERT_TRUE(FooDecl->getType()->isPointerType());
402         ASSERT_TRUE(FooDecl->getType()
403                         ->getAs<PointerType>()
404                         ->getPointeeType()
405                         ->isStructureType());
406         const auto FooFields = FooDecl->getType()
407                                    ->getAs<PointerType>()
408                                    ->getPointeeType()
409                                    ->getAsRecordDecl()
410                                    ->fields();
411 
412         FieldDecl *BarDecl = nullptr;
413         for (FieldDecl *Field : FooFields) {
414           if (Field->getNameAsString() == "Bar") {
415             BarDecl = Field;
416           } else {
417             FAIL() << "Unexpected field: " << Field->getNameAsString();
418           }
419         }
420         ASSERT_THAT(BarDecl, NotNull());
421 
422         ASSERT_TRUE(BarDecl->getType()->isPointerType());
423         ASSERT_TRUE(BarDecl->getType()
424                         ->getAs<PointerType>()
425                         ->getPointeeType()
426                         ->isStructureType());
427         const auto BarFields = BarDecl->getType()
428                                    ->getAs<PointerType>()
429                                    ->getPointeeType()
430                                    ->getAsRecordDecl()
431                                    ->fields();
432 
433         FieldDecl *FooRefDecl = nullptr;
434         FieldDecl *FooPtrDecl = nullptr;
435         FieldDecl *BazRefDecl = nullptr;
436         FieldDecl *BazPtrDecl = nullptr;
437         for (FieldDecl *Field : BarFields) {
438           if (Field->getNameAsString() == "FooRef") {
439             FooRefDecl = Field;
440           } else if (Field->getNameAsString() == "FooPtr") {
441             FooPtrDecl = Field;
442           } else if (Field->getNameAsString() == "BazRef") {
443             BazRefDecl = Field;
444           } else if (Field->getNameAsString() == "BazPtr") {
445             BazPtrDecl = Field;
446           } else {
447             FAIL() << "Unexpected field: " << Field->getNameAsString();
448           }
449         }
450         ASSERT_THAT(FooRefDecl, NotNull());
451         ASSERT_THAT(FooPtrDecl, NotNull());
452         ASSERT_THAT(BazRefDecl, NotNull());
453         ASSERT_THAT(BazPtrDecl, NotNull());
454 
455         const auto *FooLoc = cast<ScalarStorageLocation>(
456             Env.getStorageLocation(*FooDecl, SkipPast::None));
457         const auto *FooVal = cast<PointerValue>(Env.getValue(*FooLoc));
458         const auto *FooPointeeVal =
459             cast<StructValue>(Env.getValue(FooVal->getPointeeLoc()));
460 
461         const auto *BarVal =
462             cast<PointerValue>(&FooPointeeVal->getChild(*BarDecl));
463         const auto *BarPointeeVal =
464             cast<StructValue>(Env.getValue(BarVal->getPointeeLoc()));
465 
466         const auto *FooRefVal =
467             cast<ReferenceValue>(&BarPointeeVal->getChild(*FooRefDecl));
468         const StorageLocation &FooRefPointeeLoc = FooRefVal->getPointeeLoc();
469         ASSERT_THAT(Env.getValue(FooRefPointeeLoc), IsNull());
470 
471         const auto *FooPtrVal =
472             cast<PointerValue>(&BarPointeeVal->getChild(*FooPtrDecl));
473         const StorageLocation &FooPtrPointeeLoc = FooPtrVal->getPointeeLoc();
474         ASSERT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull());
475 
476         const auto *BazRefVal =
477             cast<ReferenceValue>(&BarPointeeVal->getChild(*BazRefDecl));
478         const StorageLocation &BazRefPointeeLoc = BazRefVal->getPointeeLoc();
479         ASSERT_THAT(Env.getValue(BazRefPointeeLoc), NotNull());
480 
481         const auto *BazPtrVal =
482             cast<PointerValue>(&BarPointeeVal->getChild(*BazPtrDecl));
483         const StorageLocation &BazPtrPointeeLoc = BazPtrVal->getPointeeLoc();
484         ASSERT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull());
485       });
486 }
487 
488 TEST_F(TransferTest, JoinVarDecl) {
489   std::string Code = R"(
490     void target(bool b) {
491       int foo;
492       // [[p1]]
493       if (b) {
494         int bar;
495         // [[p2]]
496       } else {
497         int baz;
498         // [[p3]]
499       }
500       (void)0;
501       // [[p4]]
502     }
503   )";
504   runDataflow(Code, [](llvm::ArrayRef<std::pair<
505                            std::string, DataflowAnalysisState<NoopLattice>>>
506                            Results,
507                        ASTContext &ASTCtx) {
508     ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
509                                      Pair("p2", _), Pair("p1", _)));
510     const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
511     ASSERT_THAT(FooDecl, NotNull());
512 
513     const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
514     ASSERT_THAT(BarDecl, NotNull());
515 
516     const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz");
517     ASSERT_THAT(BazDecl, NotNull());
518 
519     const Environment &Env1 = Results[3].second.Env;
520     const StorageLocation *FooLoc =
521         Env1.getStorageLocation(*FooDecl, SkipPast::None);
522     ASSERT_THAT(FooLoc, NotNull());
523     ASSERT_THAT(Env1.getStorageLocation(*BarDecl, SkipPast::None), IsNull());
524     ASSERT_THAT(Env1.getStorageLocation(*BazDecl, SkipPast::None), IsNull());
525 
526     const Environment &Env2 = Results[2].second.Env;
527     ASSERT_EQ(Env2.getStorageLocation(*FooDecl, SkipPast::None), FooLoc);
528     ASSERT_THAT(Env2.getStorageLocation(*BarDecl, SkipPast::None), NotNull());
529     ASSERT_THAT(Env2.getStorageLocation(*BazDecl, SkipPast::None), IsNull());
530 
531     const Environment &Env3 = Results[1].second.Env;
532     ASSERT_EQ(Env3.getStorageLocation(*FooDecl, SkipPast::None), FooLoc);
533     ASSERT_THAT(Env3.getStorageLocation(*BarDecl, SkipPast::None), IsNull());
534     ASSERT_THAT(Env3.getStorageLocation(*BazDecl, SkipPast::None), NotNull());
535 
536     const Environment &Env4 = Results[0].second.Env;
537     ASSERT_EQ(Env4.getStorageLocation(*FooDecl, SkipPast::None), FooLoc);
538     ASSERT_THAT(Env4.getStorageLocation(*BarDecl, SkipPast::None), IsNull());
539     ASSERT_THAT(Env4.getStorageLocation(*BazDecl, SkipPast::None), IsNull());
540   });
541 }
542 
543 TEST_F(TransferTest, BinaryOperatorAssign) {
544   std::string Code = R"(
545     void target() {
546       int foo;
547       int bar;
548       (bar) = (foo);
549       // [[p]]
550     }
551   )";
552   runDataflow(Code,
553               [](llvm::ArrayRef<
554                      std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
555                      Results,
556                  ASTContext &ASTCtx) {
557                 ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
558                 const Environment &Env = Results[0].second.Env;
559 
560                 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
561                 ASSERT_THAT(FooDecl, NotNull());
562 
563                 const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None);
564                 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
565 
566                 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
567                 ASSERT_THAT(BarDecl, NotNull());
568 
569                 EXPECT_EQ(Env.getValue(*BarDecl, SkipPast::None), FooVal);
570               });
571 }
572 
573 TEST_F(TransferTest, VarDeclInitAssign) {
574   std::string Code = R"(
575     void target() {
576       int foo;
577       int bar = foo;
578       // [[p]]
579     }
580   )";
581   runDataflow(Code,
582               [](llvm::ArrayRef<
583                      std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
584                      Results,
585                  ASTContext &ASTCtx) {
586                 ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
587                 const Environment &Env = Results[0].second.Env;
588 
589                 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
590                 ASSERT_THAT(FooDecl, NotNull());
591 
592                 const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None);
593                 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
594 
595                 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
596                 ASSERT_THAT(BarDecl, NotNull());
597 
598                 EXPECT_EQ(Env.getValue(*BarDecl, SkipPast::None), FooVal);
599               });
600 }
601 
602 TEST_F(TransferTest, VarDeclInitAssignChained) {
603   std::string Code = R"(
604     void target() {
605       int foo;
606       int bar;
607       int baz = (bar = foo);
608       // [[p]]
609     }
610   )";
611   runDataflow(Code,
612               [](llvm::ArrayRef<
613                      std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
614                      Results,
615                  ASTContext &ASTCtx) {
616                 ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
617                 const Environment &Env = Results[0].second.Env;
618 
619                 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
620                 ASSERT_THAT(FooDecl, NotNull());
621 
622                 const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None);
623                 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
624 
625                 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
626                 ASSERT_THAT(BarDecl, NotNull());
627 
628                 const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz");
629                 ASSERT_THAT(BazDecl, NotNull());
630 
631                 EXPECT_EQ(Env.getValue(*BarDecl, SkipPast::None), FooVal);
632                 EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), FooVal);
633               });
634 }
635 
636 TEST_F(TransferTest, VarDeclInitAssignPtrDeref) {
637   std::string Code = R"(
638     void target() {
639       int foo;
640       int *bar;
641       *(bar) = foo;
642       int baz = *(bar);
643       // [[p]]
644     }
645   )";
646   runDataflow(Code,
647               [](llvm::ArrayRef<
648                      std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
649                      Results,
650                  ASTContext &ASTCtx) {
651                 ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
652                 const Environment &Env = Results[0].second.Env;
653 
654                 const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
655                 ASSERT_THAT(FooDecl, NotNull());
656 
657                 const Value *FooVal = Env.getValue(*FooDecl, SkipPast::None);
658                 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
659 
660                 const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
661                 ASSERT_THAT(BarDecl, NotNull());
662 
663                 const auto *BarVal =
664                     cast<PointerValue>(Env.getValue(*BarDecl, SkipPast::None));
665                 EXPECT_EQ(Env.getValue(BarVal->getPointeeLoc()), FooVal);
666 
667                 const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz");
668                 ASSERT_THAT(BazDecl, NotNull());
669 
670                 EXPECT_EQ(Env.getValue(*BazDecl, SkipPast::None), FooVal);
671               });
672 }
673 
674 TEST_F(TransferTest, AssignToAndFromReference) {
675   std::string Code = R"(
676     void target() {
677       int foo;
678       int bar;
679       int& baz = foo;
680       // [[p1]]
681       baz = bar;
682       int qux = baz;
683       int& quux = baz;
684       // [[p2]]
685     }
686   )";
687   runDataflow(
688       Code, [](llvm::ArrayRef<
689                    std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
690                    Results,
691                ASTContext &ASTCtx) {
692         ASSERT_THAT(Results, ElementsAre(Pair("p1", _), Pair("p2", _)));
693         const Environment &Env1 = Results[0].second.Env;
694         const Environment &Env2 = Results[1].second.Env;
695 
696         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
697         ASSERT_THAT(FooDecl, NotNull());
698 
699         const Value *FooVal = Env1.getValue(*FooDecl, SkipPast::None);
700         ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
701 
702         const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
703         ASSERT_THAT(BarDecl, NotNull());
704 
705         const Value *BarVal = Env1.getValue(*BarDecl, SkipPast::None);
706         ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
707 
708         const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz");
709         ASSERT_THAT(BazDecl, NotNull());
710 
711         EXPECT_EQ(Env1.getValue(*BazDecl, SkipPast::Reference), FooVal);
712 
713         EXPECT_EQ(Env2.getValue(*BazDecl, SkipPast::Reference), BarVal);
714         EXPECT_EQ(Env2.getValue(*FooDecl, SkipPast::None), BarVal);
715 
716         const ValueDecl *QuxDecl = findValueDecl(ASTCtx, "qux");
717         ASSERT_THAT(QuxDecl, NotNull());
718         EXPECT_EQ(Env2.getValue(*QuxDecl, SkipPast::None), BarVal);
719 
720         const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, "quux");
721         ASSERT_THAT(QuuxDecl, NotNull());
722         EXPECT_EQ(Env2.getValue(*QuuxDecl, SkipPast::Reference), BarVal);
723       });
724 }
725 
726 } // namespace
727