xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/TransferTest.cpp (revision af7bc39ba17d8c5250830e96881fb7211c7576bb)
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 = Env.getStorageLocation(*FooDecl);
86         ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
87 
88         const Value *FooVal = Env.getValue(*FooLoc);
89         ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
90       });
91 }
92 
93 TEST_F(TransferTest, StructVarDecl) {
94   std::string Code = R"(
95     struct Foo {
96       int Bar;
97     };
98 
99     void target() {
100       Foo foo;
101       // [[p]]
102     }
103   )";
104   runDataflow(
105       Code, [](llvm::ArrayRef<
106                    std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
107                    Results,
108                ASTContext &ASTCtx) {
109         ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
110         const Environment &Env = Results[0].second.Env;
111 
112         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
113         ASSERT_THAT(FooDecl, NotNull());
114 
115         ASSERT_TRUE(FooDecl->getType()->isStructureType());
116         auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
117 
118         FieldDecl *BarDecl = nullptr;
119         for (FieldDecl *Field : FooFields) {
120           if (Field->getNameAsString() == "Bar") {
121             BarDecl = Field;
122           } else {
123             FAIL() << "Unexpected field: " << Field->getNameAsString();
124           }
125         }
126         ASSERT_THAT(BarDecl, NotNull());
127 
128         const auto *FooLoc =
129             cast<AggregateStorageLocation>(Env.getStorageLocation(*FooDecl));
130         const auto *BarLoc =
131             cast<ScalarStorageLocation>(&FooLoc->getChild(*BarDecl));
132 
133         const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
134         const auto *BarVal = cast<IntegerValue>(&FooVal->getChild(*BarDecl));
135         ASSERT_EQ(Env.getValue(*BarLoc), BarVal);
136       });
137 }
138 
139 TEST_F(TransferTest, ClassVarDecl) {
140   std::string Code = R"(
141     class Foo {
142       int Bar;
143     };
144 
145     void target() {
146       Foo foo;
147       // [[p]]
148     }
149   )";
150   runDataflow(
151       Code, [](llvm::ArrayRef<
152                    std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
153                    Results,
154                ASTContext &ASTCtx) {
155         ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
156         const Environment &Env = Results[0].second.Env;
157 
158         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
159         ASSERT_THAT(FooDecl, NotNull());
160 
161         ASSERT_TRUE(FooDecl->getType()->isClassType());
162         auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
163 
164         FieldDecl *BarDecl = nullptr;
165         for (FieldDecl *Field : FooFields) {
166           if (Field->getNameAsString() == "Bar") {
167             BarDecl = Field;
168           } else {
169             FAIL() << "Unexpected field: " << Field->getNameAsString();
170           }
171         }
172         ASSERT_THAT(BarDecl, NotNull());
173 
174         const auto *FooLoc =
175             cast<AggregateStorageLocation>(Env.getStorageLocation(*FooDecl));
176         const auto *BarLoc =
177             cast<ScalarStorageLocation>(&FooLoc->getChild(*BarDecl));
178 
179         const auto *FooVal = cast<StructValue>(Env.getValue(*FooLoc));
180         const auto *BarVal = cast<IntegerValue>(&FooVal->getChild(*BarDecl));
181         ASSERT_EQ(Env.getValue(*BarLoc), BarVal);
182       });
183 }
184 
185 TEST_F(TransferTest, ReferenceVarDecl) {
186   std::string Code = R"(
187     struct Foo {};
188 
189     Foo& getFoo();
190 
191     void target() {
192       Foo& foo = getFoo();
193       // [[p]]
194     }
195   )";
196   runDataflow(
197       Code, [](llvm::ArrayRef<
198                    std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
199                    Results,
200                ASTContext &ASTCtx) {
201         ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
202         const Environment &Env = Results[0].second.Env;
203 
204         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
205         ASSERT_THAT(FooDecl, NotNull());
206 
207         const StorageLocation *FooLoc = Env.getStorageLocation(*FooDecl);
208         ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
209 
210         const ReferenceValue *FooVal =
211             cast<ReferenceValue>(Env.getValue(*FooLoc));
212         const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc();
213         ASSERT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
214 
215         const Value *FooPointeeVal = Env.getValue(FooPointeeLoc);
216         ASSERT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal));
217       });
218 }
219 
220 TEST_F(TransferTest, SelfReferentialReferenceVarDecl) {
221   std::string Code = R"(
222     struct Foo;
223 
224     struct Baz {};
225 
226     struct Bar {
227       Foo& FooRef;
228       Foo* FooPtr;
229       Baz& BazRef;
230       Baz* BazPtr;
231     };
232 
233     struct Foo {
234       Bar& Bar;
235     };
236 
237     Foo& getFoo();
238 
239     void target() {
240       Foo& foo = getFoo();
241       // [[p]]
242     }
243   )";
244   runDataflow(Code, [](llvm::ArrayRef<std::pair<
245                            std::string, DataflowAnalysisState<NoopLattice>>>
246                            Results,
247                        ASTContext &ASTCtx) {
248     ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
249     const Environment &Env = Results[0].second.Env;
250 
251     const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
252     ASSERT_THAT(FooDecl, NotNull());
253 
254     ASSERT_TRUE(FooDecl->getType()->isReferenceType());
255     ASSERT_TRUE(FooDecl->getType().getNonReferenceType()->isStructureType());
256     const auto FooFields =
257         FooDecl->getType().getNonReferenceType()->getAsRecordDecl()->fields();
258 
259     FieldDecl *BarDecl = nullptr;
260     for (FieldDecl *Field : FooFields) {
261       if (Field->getNameAsString() == "Bar") {
262         BarDecl = Field;
263       } else {
264         FAIL() << "Unexpected field: " << Field->getNameAsString();
265       }
266     }
267     ASSERT_THAT(BarDecl, NotNull());
268 
269     ASSERT_TRUE(BarDecl->getType()->isReferenceType());
270     ASSERT_TRUE(BarDecl->getType().getNonReferenceType()->isStructureType());
271     const auto BarFields =
272         BarDecl->getType().getNonReferenceType()->getAsRecordDecl()->fields();
273 
274     FieldDecl *FooRefDecl = nullptr;
275     FieldDecl *FooPtrDecl = nullptr;
276     FieldDecl *BazRefDecl = nullptr;
277     FieldDecl *BazPtrDecl = nullptr;
278     for (FieldDecl *Field : BarFields) {
279       if (Field->getNameAsString() == "FooRef") {
280         FooRefDecl = Field;
281       } else if (Field->getNameAsString() == "FooPtr") {
282         FooPtrDecl = Field;
283       } else if (Field->getNameAsString() == "BazRef") {
284         BazRefDecl = Field;
285       } else if (Field->getNameAsString() == "BazPtr") {
286         BazPtrDecl = Field;
287       } else {
288         FAIL() << "Unexpected field: " << Field->getNameAsString();
289       }
290     }
291     ASSERT_THAT(FooRefDecl, NotNull());
292     ASSERT_THAT(FooPtrDecl, NotNull());
293     ASSERT_THAT(BazRefDecl, NotNull());
294     ASSERT_THAT(BazPtrDecl, NotNull());
295 
296     const auto *FooLoc =
297         cast<ScalarStorageLocation>(Env.getStorageLocation(*FooDecl));
298     const auto *FooVal = cast<ReferenceValue>(Env.getValue(*FooLoc));
299     const auto *FooPointeeVal =
300         cast<StructValue>(Env.getValue(FooVal->getPointeeLoc()));
301 
302     const auto *BarVal =
303         cast<ReferenceValue>(&FooPointeeVal->getChild(*BarDecl));
304     const auto *BarPointeeVal =
305         cast<StructValue>(Env.getValue(BarVal->getPointeeLoc()));
306 
307     const auto *FooRefVal =
308         cast<ReferenceValue>(&BarPointeeVal->getChild(*FooRefDecl));
309     const StorageLocation &FooRefPointeeLoc = FooRefVal->getPointeeLoc();
310     ASSERT_THAT(Env.getValue(FooRefPointeeLoc), IsNull());
311 
312     const auto *FooPtrVal =
313         cast<PointerValue>(&BarPointeeVal->getChild(*FooPtrDecl));
314     const StorageLocation &FooPtrPointeeLoc = FooPtrVal->getPointeeLoc();
315     ASSERT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull());
316 
317     const auto *BazRefVal =
318         cast<ReferenceValue>(&BarPointeeVal->getChild(*BazRefDecl));
319     const StorageLocation &BazRefPointeeLoc = BazRefVal->getPointeeLoc();
320     ASSERT_THAT(Env.getValue(BazRefPointeeLoc), NotNull());
321 
322     const auto *BazPtrVal =
323         cast<PointerValue>(&BarPointeeVal->getChild(*BazPtrDecl));
324     const StorageLocation &BazPtrPointeeLoc = BazPtrVal->getPointeeLoc();
325     ASSERT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull());
326   });
327 }
328 
329 TEST_F(TransferTest, PointerVarDecl) {
330   std::string Code = R"(
331     struct Foo {};
332 
333     Foo* getFoo();
334 
335     void target() {
336       Foo* foo = getFoo();
337       // [[p]]
338     }
339   )";
340   runDataflow(
341       Code, [](llvm::ArrayRef<
342                    std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
343                    Results,
344                ASTContext &ASTCtx) {
345         ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
346         const Environment &Env = Results[0].second.Env;
347 
348         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
349         ASSERT_THAT(FooDecl, NotNull());
350 
351         const StorageLocation *FooLoc = Env.getStorageLocation(*FooDecl);
352         ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
353 
354         const PointerValue *FooVal = cast<PointerValue>(Env.getValue(*FooLoc));
355         const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc();
356         ASSERT_TRUE(isa<AggregateStorageLocation>(&FooPointeeLoc));
357 
358         const Value *FooPointeeVal = Env.getValue(FooPointeeLoc);
359         ASSERT_TRUE(isa_and_nonnull<StructValue>(FooPointeeVal));
360       });
361 }
362 
363 TEST_F(TransferTest, SelfReferentialPointerVarDecl) {
364   std::string Code = R"(
365     struct Foo;
366 
367     struct Baz {};
368 
369     struct Bar {
370       Foo& FooRef;
371       Foo* FooPtr;
372       Baz& BazRef;
373       Baz* BazPtr;
374     };
375 
376     struct Foo {
377       Bar* Bar;
378     };
379 
380     Foo* getFoo();
381 
382     void target() {
383       Foo* foo = getFoo();
384       // [[p]]
385     }
386   )";
387   runDataflow(
388       Code, [](llvm::ArrayRef<
389                    std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
390                    Results,
391                ASTContext &ASTCtx) {
392         ASSERT_THAT(Results, ElementsAre(Pair("p", _)));
393         const Environment &Env = Results[0].second.Env;
394 
395         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
396         ASSERT_THAT(FooDecl, NotNull());
397 
398         ASSERT_TRUE(FooDecl->getType()->isPointerType());
399         ASSERT_TRUE(FooDecl->getType()
400                         ->getAs<PointerType>()
401                         ->getPointeeType()
402                         ->isStructureType());
403         const auto FooFields = FooDecl->getType()
404                                    ->getAs<PointerType>()
405                                    ->getPointeeType()
406                                    ->getAsRecordDecl()
407                                    ->fields();
408 
409         FieldDecl *BarDecl = nullptr;
410         for (FieldDecl *Field : FooFields) {
411           if (Field->getNameAsString() == "Bar") {
412             BarDecl = Field;
413           } else {
414             FAIL() << "Unexpected field: " << Field->getNameAsString();
415           }
416         }
417         ASSERT_THAT(BarDecl, NotNull());
418 
419         ASSERT_TRUE(BarDecl->getType()->isPointerType());
420         ASSERT_TRUE(BarDecl->getType()
421                         ->getAs<PointerType>()
422                         ->getPointeeType()
423                         ->isStructureType());
424         const auto BarFields = BarDecl->getType()
425                                    ->getAs<PointerType>()
426                                    ->getPointeeType()
427                                    ->getAsRecordDecl()
428                                    ->fields();
429 
430         FieldDecl *FooRefDecl = nullptr;
431         FieldDecl *FooPtrDecl = nullptr;
432         FieldDecl *BazRefDecl = nullptr;
433         FieldDecl *BazPtrDecl = nullptr;
434         for (FieldDecl *Field : BarFields) {
435           if (Field->getNameAsString() == "FooRef") {
436             FooRefDecl = Field;
437           } else if (Field->getNameAsString() == "FooPtr") {
438             FooPtrDecl = Field;
439           } else if (Field->getNameAsString() == "BazRef") {
440             BazRefDecl = Field;
441           } else if (Field->getNameAsString() == "BazPtr") {
442             BazPtrDecl = Field;
443           } else {
444             FAIL() << "Unexpected field: " << Field->getNameAsString();
445           }
446         }
447         ASSERT_THAT(FooRefDecl, NotNull());
448         ASSERT_THAT(FooPtrDecl, NotNull());
449         ASSERT_THAT(BazRefDecl, NotNull());
450         ASSERT_THAT(BazPtrDecl, NotNull());
451 
452         const auto *FooLoc =
453             cast<ScalarStorageLocation>(Env.getStorageLocation(*FooDecl));
454         const auto *FooVal = cast<PointerValue>(Env.getValue(*FooLoc));
455         const auto *FooPointeeVal =
456             cast<StructValue>(Env.getValue(FooVal->getPointeeLoc()));
457 
458         const auto *BarVal =
459             cast<PointerValue>(&FooPointeeVal->getChild(*BarDecl));
460         const auto *BarPointeeVal =
461             cast<StructValue>(Env.getValue(BarVal->getPointeeLoc()));
462 
463         const auto *FooRefVal =
464             cast<ReferenceValue>(&BarPointeeVal->getChild(*FooRefDecl));
465         const StorageLocation &FooRefPointeeLoc = FooRefVal->getPointeeLoc();
466         ASSERT_THAT(Env.getValue(FooRefPointeeLoc), IsNull());
467 
468         const auto *FooPtrVal =
469             cast<PointerValue>(&BarPointeeVal->getChild(*FooPtrDecl));
470         const StorageLocation &FooPtrPointeeLoc = FooPtrVal->getPointeeLoc();
471         ASSERT_THAT(Env.getValue(FooPtrPointeeLoc), IsNull());
472 
473         const auto *BazRefVal =
474             cast<ReferenceValue>(&BarPointeeVal->getChild(*BazRefDecl));
475         const StorageLocation &BazRefPointeeLoc = BazRefVal->getPointeeLoc();
476         ASSERT_THAT(Env.getValue(BazRefPointeeLoc), NotNull());
477 
478         const auto *BazPtrVal =
479             cast<PointerValue>(&BarPointeeVal->getChild(*BazPtrDecl));
480         const StorageLocation &BazPtrPointeeLoc = BazPtrVal->getPointeeLoc();
481         ASSERT_THAT(Env.getValue(BazPtrPointeeLoc), NotNull());
482       });
483 }
484 
485 TEST_F(TransferTest, JoinVarDecl) {
486   std::string Code = R"(
487     void target(bool b) {
488       int foo;
489       // [[p1]]
490       if (b) {
491         int bar;
492         // [[p2]]
493       } else {
494         int baz;
495         // [[p3]]
496       }
497       (void)0;
498       // [[p4]]
499     }
500   )";
501   runDataflow(
502       Code, [](llvm::ArrayRef<
503                    std::pair<std::string, DataflowAnalysisState<NoopLattice>>>
504                    Results,
505                ASTContext &ASTCtx) {
506         ASSERT_THAT(Results, ElementsAre(Pair("p4", _), Pair("p3", _),
507                                          Pair("p2", _), Pair("p1", _)));
508         const ValueDecl *FooDecl = findValueDecl(ASTCtx, "foo");
509         ASSERT_THAT(FooDecl, NotNull());
510 
511         const ValueDecl *BarDecl = findValueDecl(ASTCtx, "bar");
512         ASSERT_THAT(BarDecl, NotNull());
513 
514         const ValueDecl *BazDecl = findValueDecl(ASTCtx, "baz");
515         ASSERT_THAT(BazDecl, NotNull());
516 
517         const Environment &Env1 = Results[3].second.Env;
518         const StorageLocation *FooLoc = Env1.getStorageLocation(*FooDecl);
519         ASSERT_THAT(FooLoc, NotNull());
520         ASSERT_THAT(Env1.getStorageLocation(*BarDecl), IsNull());
521         ASSERT_THAT(Env1.getStorageLocation(*BazDecl), IsNull());
522 
523         const Environment &Env2 = Results[2].second.Env;
524         ASSERT_EQ(Env2.getStorageLocation(*FooDecl), FooLoc);
525         ASSERT_THAT(Env2.getStorageLocation(*BarDecl), NotNull());
526         ASSERT_THAT(Env2.getStorageLocation(*BazDecl), IsNull());
527 
528         const Environment &Env3 = Results[1].second.Env;
529         ASSERT_EQ(Env3.getStorageLocation(*FooDecl), FooLoc);
530         ASSERT_THAT(Env3.getStorageLocation(*BarDecl), IsNull());
531         ASSERT_THAT(Env3.getStorageLocation(*BazDecl), NotNull());
532 
533         const Environment &Env4 = Results[0].second.Env;
534         ASSERT_EQ(Env4.getStorageLocation(*FooDecl), FooLoc);
535         ASSERT_THAT(Env4.getStorageLocation(*BarDecl), IsNull());
536         ASSERT_THAT(Env4.getStorageLocation(*BazDecl), IsNull());
537       });
538 }
539 
540 } // namespace
541