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 "TestingSupport.h"
10#include "clang/AST/ASTContext.h"
11#include "clang/AST/Decl.h"
12#include "clang/ASTMatchers/ASTMatchers.h"
13#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
14#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
15#include "clang/Analysis/FlowSensitive/NoopAnalysis.h"
16#include "clang/Analysis/FlowSensitive/RecordOps.h"
17#include "clang/Analysis/FlowSensitive/StorageLocation.h"
18#include "clang/Analysis/FlowSensitive/Value.h"
19#include "clang/Basic/LangStandard.h"
20#include "clang/Testing/TestAST.h"
21#include "llvm/ADT/SmallVector.h"
22#include "llvm/ADT/StringRef.h"
23#include "llvm/Testing/Support/Error.h"
24#include "gmock/gmock.h"
25#include "gtest/gtest.h"
26#include <optional>
27#include <string>
28#include <string_view>
29#include <utility>
30
31namespace clang {
32namespace dataflow {
33namespace {
34AST_MATCHER(FunctionDecl, isTemplated) { return Node.isTemplated(); }
35} // namespace
36} // namespace dataflow
37} // namespace clang
38
39namespace {
40
41using namespace clang;
42using namespace dataflow;
43using namespace test;
44using ::testing::Eq;
45using ::testing::IsNull;
46using ::testing::Ne;
47using ::testing::NotNull;
48using ::testing::UnorderedElementsAre;
49
50// Declares a minimal coroutine library.
51constexpr llvm::StringRef CoroutineLibrary = R"cc(
52struct promise;
53struct task;
54
55namespace std {
56template <class, class...>
57struct coroutine_traits {};
58template <>
59struct coroutine_traits<task> {
60 using promise_type = promise;
61};
62
63template <class Promise = void>
64struct coroutine_handle {
65 static constexpr coroutine_handle from_address(void *addr) { return {}; }
66};
67} // namespace std
68
69struct awaitable {
70 bool await_ready() const noexcept;
71 void await_suspend(std::coroutine_handle<promise>) const noexcept;
72 void await_resume() const noexcept;
73};
74struct task {};
75struct promise {
76 task get_return_object();
77 awaitable initial_suspend();
78 awaitable final_suspend() noexcept;
79 void unhandled_exception();
80 void return_void();
81};
82)cc";
83
84void runDataflow(
85 llvm::StringRef Code,
86 std::function<
87 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
88 ASTContext &)>
89 VerifyResults,
90 DataflowAnalysisOptions Options,
91 LangStandard::Kind Std = LangStandard::lang_cxx17,
92 llvm::StringRef TargetFun = "target") {
93 ASSERT_THAT_ERROR(checkDataflowWithNoopAnalysis(Code, VerifyResults, Options,
94 Std, TargetFun),
95 llvm::Succeeded());
96}
97
98void runDataflow(
99 llvm::StringRef Code,
100 std::function<
101 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
102 ASTContext &)>
103 VerifyResults,
104 LangStandard::Kind Std = LangStandard::lang_cxx17,
105 bool ApplyBuiltinTransfer = true, llvm::StringRef TargetFun = "target") {
106 runDataflow(Code, VerifyResults: std::move(VerifyResults),
107 Options: {.BuiltinOpts: ApplyBuiltinTransfer ? BuiltinOptions{}
108 : std::optional<BuiltinOptions>()},
109 Std, TargetFun);
110}
111
112void runDataflowOnLambda(
113 llvm::StringRef Code,
114 std::function<
115 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
116 ASTContext &)>
117 VerifyResults,
118 DataflowAnalysisOptions Options,
119 LangStandard::Kind Std = LangStandard::lang_cxx17) {
120 ASSERT_THAT_ERROR(
121 checkDataflowWithNoopAnalysis(
122 Code,
123 ast_matchers::hasDeclContext(
124 ast_matchers::cxxRecordDecl(ast_matchers::isLambda())),
125 VerifyResults, Options, Std),
126 llvm::Succeeded());
127}
128
129void runDataflowOnLambda(
130 llvm::StringRef Code,
131 std::function<
132 void(const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
133 ASTContext &)>
134 VerifyResults,
135 LangStandard::Kind Std = LangStandard::lang_cxx17,
136 bool ApplyBuiltinTransfer = true) {
137 runDataflowOnLambda(Code, VerifyResults: std::move(VerifyResults),
138 Options: {.BuiltinOpts: ApplyBuiltinTransfer ? BuiltinOptions{}
139 : std::optional<BuiltinOptions>()},
140 Std);
141}
142
143const Formula &getFormula(const ValueDecl &D, const Environment &Env) {
144 return cast<BoolValue>(Val: Env.getValue(D))->formula();
145}
146
147const BindingDecl *findBindingDecl(ASTContext &ASTCtx, std::string_view Name) {
148 using ast_matchers::bindingDecl;
149 using ast_matchers::hasName;
150 auto TargetNodes =
151 ast_matchers::match(Matcher: bindingDecl(hasName(Name)).bind(ID: "v"), Context&: ASTCtx);
152 assert(TargetNodes.size() == 1 && "Name must be unique");
153 return ast_matchers::selectFirst<BindingDecl>(BoundTo: "v", Results: TargetNodes);
154}
155
156TEST(TransferTest, CNotSupported) {
157 TestInputs Inputs("void target() {}");
158 Inputs.Language = TestLanguage::Lang_C89;
159 clang::TestAST AST(Inputs);
160 const auto *Target =
161 cast<FunctionDecl>(Val: test::findValueDecl(ASTCtx&: AST.context(), Name: "target"));
162 ASSERT_THAT_ERROR(AdornedCFG::build(*Target).takeError(),
163 llvm::FailedWithMessage("Can only analyze C++"));
164}
165
166TEST(TransferTest, ObjectiveCNotSupported) {
167 TestInputs Inputs("void target() {}");
168 Inputs.Language = TestLanguage::Lang_OBJC;
169 clang::TestAST AST(Inputs);
170 const auto *Target =
171 cast<FunctionDecl>(Val: test::findValueDecl(ASTCtx&: AST.context(), Name: "target"));
172 ASSERT_THAT_ERROR(AdornedCFG::build(*Target).takeError(),
173 llvm::FailedWithMessage("Can only analyze C++"));
174}
175
176TEST(TransferTest, ObjectiveCXXNotSupported) {
177 TestInputs Inputs("void target() {}");
178 Inputs.Language = TestLanguage::Lang_OBJCXX;
179 clang::TestAST AST(Inputs);
180 const auto *Target =
181 cast<FunctionDecl>(Val: test::findValueDecl(ASTCtx&: AST.context(), Name: "target"));
182 ASSERT_THAT_ERROR(AdornedCFG::build(*Target).takeError(),
183 llvm::FailedWithMessage("Can only analyze C++"));
184}
185
186TEST(TransferTest, IntVarDeclNotTrackedWhenTransferDisabled) {
187 std::string Code = R"(
188 void target() {
189 int Foo;
190 // [[p]]
191 }
192 )";
193 runDataflow(
194 Code,
195 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
196 ASTContext &ASTCtx) {
197 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
198 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
199
200 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
201 ASSERT_THAT(FooDecl, NotNull());
202
203 EXPECT_EQ(Env.getStorageLocation(*FooDecl), nullptr);
204 },
205 Std: LangStandard::lang_cxx17,
206 /*ApplyBuiltinTransfer=*/false);
207}
208
209TEST(TransferTest, BoolVarDecl) {
210 std::string Code = R"(
211 void target() {
212 bool Foo;
213 // [[p]]
214 }
215 )";
216 runDataflow(
217 Code,
218 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
219 ASTContext &ASTCtx) {
220 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
221 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
222
223 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
224 ASSERT_THAT(FooDecl, NotNull());
225
226 const StorageLocation *FooLoc = Env.getStorageLocation(D: *FooDecl);
227 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
228
229 const Value *FooVal = Env.getValue(Loc: *FooLoc);
230 EXPECT_TRUE(isa_and_nonnull<BoolValue>(FooVal));
231 });
232}
233
234TEST(TransferTest, IntVarDecl) {
235 std::string Code = R"(
236 void target() {
237 int Foo;
238 // [[p]]
239 }
240 )";
241 runDataflow(
242 Code,
243 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
244 ASTContext &ASTCtx) {
245 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
246 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
247
248 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
249 ASSERT_THAT(FooDecl, NotNull());
250
251 const StorageLocation *FooLoc = Env.getStorageLocation(D: *FooDecl);
252 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
253
254 const Value *FooVal = Env.getValue(Loc: *FooLoc);
255 EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
256 });
257}
258
259TEST(TransferTest, StructIncomplete) {
260 std::string Code = R"(
261 struct A;
262
263 void target() {
264 A* Foo;
265 // [[p]]
266 }
267 )";
268 runDataflow(
269 Code,
270 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
271 ASTContext &ASTCtx) {
272 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
273 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
274
275 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
276 ASSERT_THAT(FooDecl, NotNull());
277 auto *FooValue = dyn_cast_or_null<PointerValue>(Val: Env.getValue(D: *FooDecl));
278 ASSERT_THAT(FooValue, NotNull());
279
280 EXPECT_TRUE(isa<RecordStorageLocation>(FooValue->getPointeeLoc()));
281 });
282}
283
284// As a memory optimization, we prevent modeling fields nested below a certain
285// level (currently, depth 3). This test verifies this lack of modeling. We also
286// include a regression test for the case that the unmodeled field is a
287// reference to a struct; previously, we crashed when accessing such a field.
288TEST(TransferTest, StructFieldUnmodeled) {
289 std::string Code = R"(
290 struct S { int X; };
291 S GlobalS;
292 struct A { S &Unmodeled = GlobalS; };
293 struct B { A F3; };
294 struct C { B F2; };
295 struct D { C F1; };
296
297 void target() {
298 D Bar;
299 A &Foo = Bar.F1.F2.F3;
300 int Zab = Foo.Unmodeled.X;
301 // [[p]]
302 }
303 )";
304 runDataflow(
305 Code,
306 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
307 ASTContext &ASTCtx) {
308 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
309 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
310
311 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
312 ASSERT_THAT(FooDecl, NotNull());
313 QualType FooReferentType = FooDecl->getType()->getPointeeType();
314 ASSERT_TRUE(FooReferentType->isStructureType());
315 auto FooFields = FooReferentType->getAsRecordDecl()->fields();
316
317 FieldDecl *UnmodeledDecl = nullptr;
318 for (FieldDecl *Field : FooFields) {
319 if (Field->getNameAsString() == "Unmodeled") {
320 UnmodeledDecl = Field;
321 } else {
322 FAIL() << "Unexpected field: " << Field->getNameAsString();
323 }
324 }
325 ASSERT_THAT(UnmodeledDecl, NotNull());
326
327 const auto *FooLoc =
328 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
329 const auto &UnmodeledLoc =
330 *cast<RecordStorageLocation>(Val: FooLoc->getChild(*UnmodeledDecl));
331 StorageLocation &UnmodeledXLoc = getFieldLoc(UnmodeledLoc, "X", ASTCtx);
332 EXPECT_EQ(Env.getValue(UnmodeledXLoc), nullptr);
333
334 const ValueDecl *ZabDecl = findValueDecl(ASTCtx, Name: "Zab");
335 ASSERT_THAT(ZabDecl, NotNull());
336 EXPECT_THAT(Env.getValue(*ZabDecl), NotNull());
337 });
338}
339
340TEST(TransferTest, StructVarDecl) {
341 std::string Code = R"(
342 struct A {
343 int Bar;
344 };
345
346 void target() {
347 A Foo;
348 (void)Foo.Bar;
349 // [[p]]
350 }
351 )";
352 runDataflow(
353 Code,
354 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
355 ASTContext &ASTCtx) {
356 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
357 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
358
359 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
360 ASSERT_THAT(FooDecl, NotNull());
361
362 ASSERT_TRUE(FooDecl->getType()->isStructureType());
363 auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
364
365 FieldDecl *BarDecl = nullptr;
366 for (FieldDecl *Field : FooFields) {
367 if (Field->getNameAsString() == "Bar") {
368 BarDecl = Field;
369 } else {
370 FAIL() << "Unexpected field: " << Field->getNameAsString();
371 }
372 }
373 ASSERT_THAT(BarDecl, NotNull());
374
375 const auto *FooLoc =
376 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
377 EXPECT_TRUE(isa<IntegerValue>(getFieldValue(FooLoc, *BarDecl, Env)));
378 });
379}
380
381TEST(TransferTest, StructVarDeclWithInit) {
382 std::string Code = R"(
383 struct A {
384 int Bar;
385 };
386
387 A Gen();
388
389 void target() {
390 A Foo = Gen();
391 (void)Foo.Bar;
392 // [[p]]
393 }
394 )";
395 runDataflow(
396 Code,
397 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
398 ASTContext &ASTCtx) {
399 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
400 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
401
402 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
403 ASSERT_THAT(FooDecl, NotNull());
404
405 ASSERT_TRUE(FooDecl->getType()->isStructureType());
406 auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
407
408 FieldDecl *BarDecl = nullptr;
409 for (FieldDecl *Field : FooFields) {
410 if (Field->getNameAsString() == "Bar") {
411 BarDecl = Field;
412 } else {
413 FAIL() << "Unexpected field: " << Field->getNameAsString();
414 }
415 }
416 ASSERT_THAT(BarDecl, NotNull());
417
418 const auto *FooLoc =
419 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
420 EXPECT_TRUE(isa<IntegerValue>(getFieldValue(FooLoc, *BarDecl, Env)));
421 });
422}
423
424TEST(TransferTest, StructArrayVarDecl) {
425 std::string Code = R"(
426 struct A {};
427
428 void target() {
429 A Array[2];
430 // [[p]]
431 }
432 )";
433 runDataflow(
434 Code,
435 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
436 ASTContext &ASTCtx) {
437 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
438
439 const ValueDecl *ArrayDecl = findValueDecl(ASTCtx, Name: "Array");
440
441 // We currently don't create values for arrays.
442 ASSERT_THAT(Env.getValue(*ArrayDecl), IsNull());
443 });
444}
445
446TEST(TransferTest, ClassVarDecl) {
447 std::string Code = R"(
448 class A {
449 public:
450 int Bar;
451 };
452
453 void target() {
454 A Foo;
455 (void)Foo.Bar;
456 // [[p]]
457 }
458 )";
459 runDataflow(
460 Code,
461 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
462 ASTContext &ASTCtx) {
463 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
464 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
465
466 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
467 ASSERT_THAT(FooDecl, NotNull());
468
469 ASSERT_TRUE(FooDecl->getType()->isClassType());
470 auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
471
472 FieldDecl *BarDecl = nullptr;
473 for (FieldDecl *Field : FooFields) {
474 if (Field->getNameAsString() == "Bar") {
475 BarDecl = Field;
476 } else {
477 FAIL() << "Unexpected field: " << Field->getNameAsString();
478 }
479 }
480 ASSERT_THAT(BarDecl, NotNull());
481
482 const auto *FooLoc =
483 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
484 EXPECT_TRUE(isa<IntegerValue>(getFieldValue(FooLoc, *BarDecl, Env)));
485 });
486}
487
488TEST(TransferTest, ReferenceVarDecl) {
489 std::string Code = R"(
490 struct A {};
491
492 A &getA();
493
494 void target() {
495 A &Foo = getA();
496 // [[p]]
497 }
498 )";
499 runDataflow(
500 Code,
501 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
502 ASTContext &ASTCtx) {
503 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
504 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
505
506 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
507 ASSERT_THAT(FooDecl, NotNull());
508
509 const StorageLocation *FooLoc = Env.getStorageLocation(D: *FooDecl);
510 ASSERT_TRUE(isa_and_nonnull<RecordStorageLocation>(FooLoc));
511 });
512}
513
514TEST(TransferTest, SelfReferentialReferenceVarDecl) {
515 std::string Code = R"(
516 struct A;
517
518 struct B {};
519
520 struct C {
521 A &FooRef;
522 A *FooPtr;
523 B &BazRef;
524 B *BazPtr;
525 };
526
527 struct A {
528 C &Bar;
529 };
530
531 A &getA();
532
533 void target() {
534 A &Foo = getA();
535 (void)Foo.Bar.FooRef;
536 (void)Foo.Bar.FooPtr;
537 (void)Foo.Bar.BazRef;
538 (void)Foo.Bar.BazPtr;
539 // [[p]]
540 }
541 )";
542 runDataflow(Code, [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>>
543 &Results,
544 ASTContext &ASTCtx) {
545 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
546 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
547
548 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
549 ASSERT_THAT(FooDecl, NotNull());
550
551 ASSERT_TRUE(FooDecl->getType()->isReferenceType());
552 ASSERT_TRUE(FooDecl->getType().getNonReferenceType()->isStructureType());
553 const auto FooFields =
554 FooDecl->getType().getNonReferenceType()->getAsRecordDecl()->fields();
555
556 FieldDecl *BarDecl = nullptr;
557 for (FieldDecl *Field : FooFields) {
558 if (Field->getNameAsString() == "Bar") {
559 BarDecl = Field;
560 } else {
561 FAIL() << "Unexpected field: " << Field->getNameAsString();
562 }
563 }
564 ASSERT_THAT(BarDecl, NotNull());
565
566 ASSERT_TRUE(BarDecl->getType()->isReferenceType());
567 ASSERT_TRUE(BarDecl->getType().getNonReferenceType()->isStructureType());
568 const auto BarFields =
569 BarDecl->getType().getNonReferenceType()->getAsRecordDecl()->fields();
570
571 FieldDecl *FooRefDecl = nullptr;
572 FieldDecl *FooPtrDecl = nullptr;
573 FieldDecl *BazRefDecl = nullptr;
574 FieldDecl *BazPtrDecl = nullptr;
575 for (FieldDecl *Field : BarFields) {
576 if (Field->getNameAsString() == "FooRef") {
577 FooRefDecl = Field;
578 } else if (Field->getNameAsString() == "FooPtr") {
579 FooPtrDecl = Field;
580 } else if (Field->getNameAsString() == "BazRef") {
581 BazRefDecl = Field;
582 } else if (Field->getNameAsString() == "BazPtr") {
583 BazPtrDecl = Field;
584 } else {
585 FAIL() << "Unexpected field: " << Field->getNameAsString();
586 }
587 }
588 ASSERT_THAT(FooRefDecl, NotNull());
589 ASSERT_THAT(FooPtrDecl, NotNull());
590 ASSERT_THAT(BazRefDecl, NotNull());
591 ASSERT_THAT(BazPtrDecl, NotNull());
592
593 const auto &FooLoc =
594 *cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
595
596 const auto &BarLoc =
597 *cast<RecordStorageLocation>(Val: FooLoc.getChild(*BarDecl));
598
599 const auto &FooReferentLoc =
600 *cast<RecordStorageLocation>(BarLoc.getChild(*FooRefDecl));
601 EXPECT_EQ(Env.getValue(*cast<RecordStorageLocation>(
602 FooReferentLoc.getChild(*BarDecl))
603 ->getChild(*FooPtrDecl)),
604 nullptr);
605
606 const auto &FooPtrVal =
607 *cast<PointerValue>(getFieldValue(&BarLoc, *FooPtrDecl, Env));
608 const auto &FooPtrPointeeLoc =
609 cast<RecordStorageLocation>(FooPtrVal.getPointeeLoc());
610 EXPECT_EQ(Env.getValue(*cast<RecordStorageLocation>(
611 FooPtrPointeeLoc.getChild(*BarDecl))
612 ->getChild(*FooPtrDecl)),
613 nullptr);
614
615 EXPECT_TRUE(isa<PointerValue>(getFieldValue(&BarLoc, *BazPtrDecl, Env)));
616 });
617}
618
619TEST(TransferTest, PointerVarDecl) {
620 std::string Code = R"(
621 struct A {};
622
623 A *getA();
624
625 void target() {
626 A *Foo = getA();
627 // [[p]]
628 }
629 )";
630 runDataflow(
631 Code,
632 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
633 ASTContext &ASTCtx) {
634 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
635 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
636
637 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
638 ASSERT_THAT(FooDecl, NotNull());
639
640 const StorageLocation *FooLoc = Env.getStorageLocation(D: *FooDecl);
641 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
642
643 const PointerValue *FooVal = cast<PointerValue>(Val: Env.getValue(Loc: *FooLoc));
644 const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc();
645 EXPECT_TRUE(isa<RecordStorageLocation>(&FooPointeeLoc));
646 });
647}
648
649TEST(TransferTest, SelfReferentialPointerVarDecl) {
650 std::string Code = R"(
651 struct A;
652
653 struct B {};
654
655 struct C {
656 A &FooRef;
657 A *FooPtr;
658 B &BazRef;
659 B *BazPtr;
660 };
661
662 struct A {
663 C *Bar;
664 };
665
666 A *getA();
667
668 void target() {
669 A *Foo = getA();
670 (void)Foo->Bar->FooRef;
671 (void)Foo->Bar->FooPtr;
672 (void)Foo->Bar->BazRef;
673 (void)Foo->Bar->BazPtr;
674 // [[p]]
675 }
676 )";
677 runDataflow(
678 Code,
679 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
680 ASTContext &ASTCtx) {
681 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
682 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
683
684 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
685 ASSERT_THAT(FooDecl, NotNull());
686
687 ASSERT_TRUE(FooDecl->getType()->isPointerType());
688 ASSERT_TRUE(FooDecl->getType()
689 ->getAs<PointerType>()
690 ->getPointeeType()
691 ->isStructureType());
692 const auto FooFields = FooDecl->getType()
693 ->getAs<PointerType>()
694 ->getPointeeType()
695 ->getAsRecordDecl()
696 ->fields();
697
698 FieldDecl *BarDecl = nullptr;
699 for (FieldDecl *Field : FooFields) {
700 if (Field->getNameAsString() == "Bar") {
701 BarDecl = Field;
702 } else {
703 FAIL() << "Unexpected field: " << Field->getNameAsString();
704 }
705 }
706 ASSERT_THAT(BarDecl, NotNull());
707
708 ASSERT_TRUE(BarDecl->getType()->isPointerType());
709 ASSERT_TRUE(BarDecl->getType()
710 ->getAs<PointerType>()
711 ->getPointeeType()
712 ->isStructureType());
713 const auto BarFields = BarDecl->getType()
714 ->getAs<PointerType>()
715 ->getPointeeType()
716 ->getAsRecordDecl()
717 ->fields();
718
719 FieldDecl *FooRefDecl = nullptr;
720 FieldDecl *FooPtrDecl = nullptr;
721 FieldDecl *BazRefDecl = nullptr;
722 FieldDecl *BazPtrDecl = nullptr;
723 for (FieldDecl *Field : BarFields) {
724 if (Field->getNameAsString() == "FooRef") {
725 FooRefDecl = Field;
726 } else if (Field->getNameAsString() == "FooPtr") {
727 FooPtrDecl = Field;
728 } else if (Field->getNameAsString() == "BazRef") {
729 BazRefDecl = Field;
730 } else if (Field->getNameAsString() == "BazPtr") {
731 BazPtrDecl = Field;
732 } else {
733 FAIL() << "Unexpected field: " << Field->getNameAsString();
734 }
735 }
736 ASSERT_THAT(FooRefDecl, NotNull());
737 ASSERT_THAT(FooPtrDecl, NotNull());
738 ASSERT_THAT(BazRefDecl, NotNull());
739 ASSERT_THAT(BazPtrDecl, NotNull());
740
741 const auto &FooLoc =
742 *cast<ScalarStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
743 const auto &FooVal = *cast<PointerValue>(Val: Env.getValue(FooLoc));
744 const auto &FooPointeeLoc =
745 cast<RecordStorageLocation>(FooVal.getPointeeLoc());
746
747 const auto &BarVal =
748 *cast<PointerValue>(getFieldValue(&FooPointeeLoc, *BarDecl, Env));
749 const auto &BarPointeeLoc =
750 cast<RecordStorageLocation>(BarVal.getPointeeLoc());
751
752 const auto &FooPtrVal = *cast<PointerValue>(
753 getFieldValue(&BarPointeeLoc, *FooPtrDecl, Env));
754 const auto &FooPtrPointeeLoc =
755 cast<RecordStorageLocation>(FooPtrVal.getPointeeLoc());
756 EXPECT_EQ(Env.getValue(*FooPtrPointeeLoc.getChild(*BarDecl)), nullptr);
757
758 EXPECT_TRUE(
759 isa<PointerValue>(getFieldValue(&BarPointeeLoc, *BazPtrDecl, Env)));
760 });
761}
762
763TEST(TransferTest, DirectlySelfReferentialReference) {
764 std::string Code = R"(
765 struct target {
766 target() {
767 (void)0;
768 // [[p]]
769 }
770 target &self = *this;
771 };
772 )";
773 runDataflow(
774 Code,
775 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
776 ASTContext &ASTCtx) {
777 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
778 const ValueDecl *SelfDecl = findValueDecl(ASTCtx, Name: "self");
779
780 auto *ThisLoc = Env.getThisPointeeStorageLocation();
781 ASSERT_EQ(ThisLoc->getChild(*SelfDecl), ThisLoc);
782 });
783}
784
785TEST(TransferTest, MultipleVarsDecl) {
786 std::string Code = R"(
787 void target() {
788 int Foo, Bar;
789 (void)0;
790 // [[p]]
791 }
792 )";
793 runDataflow(
794 Code,
795 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
796 ASTContext &ASTCtx) {
797 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
798 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
799
800 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
801 ASSERT_THAT(FooDecl, NotNull());
802
803 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
804 ASSERT_THAT(BarDecl, NotNull());
805
806 const StorageLocation *FooLoc = Env.getStorageLocation(D: *FooDecl);
807 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
808
809 const StorageLocation *BarLoc = Env.getStorageLocation(D: *BarDecl);
810 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
811
812 const Value *FooVal = Env.getValue(Loc: *FooLoc);
813 EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
814
815 const Value *BarVal = Env.getValue(Loc: *BarLoc);
816 EXPECT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
817 });
818}
819
820TEST(TransferTest, JoinVarDecl) {
821 std::string Code = R"(
822 void target(bool B) {
823 int Foo;
824 // [[p1]]
825 if (B) {
826 int Bar;
827 // [[p2]]
828 } else {
829 int Baz;
830 // [[p3]]
831 }
832 (void)0;
833 // [[p4]]
834 }
835 )";
836 runDataflow(Code, VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>>
837 &Results,
838 ASTContext &ASTCtx) {
839 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2", "p3", "p4"));
840
841 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
842 ASSERT_THAT(FooDecl, NotNull());
843
844 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
845 ASSERT_THAT(BarDecl, NotNull());
846
847 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
848 ASSERT_THAT(BazDecl, NotNull());
849
850 const Environment &Env1 = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p1");
851
852 const StorageLocation *FooLoc = Env1.getStorageLocation(D: *FooDecl);
853 EXPECT_THAT(FooLoc, NotNull());
854 EXPECT_THAT(Env1.getStorageLocation(*BarDecl), IsNull());
855 EXPECT_THAT(Env1.getStorageLocation(*BazDecl), IsNull());
856
857 const Environment &Env2 = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p2");
858 EXPECT_EQ(Env2.getStorageLocation(*FooDecl), FooLoc);
859 EXPECT_THAT(Env2.getStorageLocation(*BarDecl), NotNull());
860 EXPECT_THAT(Env2.getStorageLocation(*BazDecl), IsNull());
861
862 const Environment &Env3 = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p3");
863 EXPECT_EQ(Env3.getStorageLocation(*FooDecl), FooLoc);
864 EXPECT_THAT(Env3.getStorageLocation(*BarDecl), IsNull());
865 EXPECT_THAT(Env3.getStorageLocation(*BazDecl), NotNull());
866
867 const Environment &Env4 = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p4");
868 EXPECT_EQ(Env4.getStorageLocation(*FooDecl), FooLoc);
869 EXPECT_THAT(Env4.getStorageLocation(*BarDecl), IsNull());
870 EXPECT_THAT(Env4.getStorageLocation(*BazDecl), IsNull());
871 });
872}
873
874TEST(TransferTest, BinaryOperatorAssign) {
875 std::string Code = R"(
876 void target() {
877 int Foo;
878 int Bar;
879 (Bar) = (Foo);
880 // [[p]]
881 }
882 )";
883 runDataflow(
884 Code,
885 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
886 ASTContext &ASTCtx) {
887 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
888 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
889
890 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
891 ASSERT_THAT(FooDecl, NotNull());
892
893 const Value *FooVal = Env.getValue(D: *FooDecl);
894 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
895
896 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
897 ASSERT_THAT(BarDecl, NotNull());
898
899 EXPECT_EQ(Env.getValue(*BarDecl), FooVal);
900 });
901}
902
903TEST(TransferTest, BinaryOperatorAssignIntegerLiteral) {
904 std::string Code = R"(
905 void target() {
906 int Foo = 1;
907 // [[before]]
908 Foo = 2;
909 // [[after]]
910 }
911 )";
912 runDataflow(
913 Code,
914 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
915 ASTContext &ASTCtx) {
916 const Environment &Before =
917 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "before");
918 const Environment &After = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "after");
919
920 const auto &ValBefore =
921 getValueForDecl<IntegerValue>(ASTCtx, Env: Before, Name: "Foo");
922 const auto &ValAfter =
923 getValueForDecl<IntegerValue>(ASTCtx, Env: After, Name: "Foo");
924 EXPECT_NE(&ValBefore, &ValAfter);
925 });
926}
927
928TEST(TransferTest, VarDeclInitAssign) {
929 std::string Code = R"(
930 void target() {
931 int Foo;
932 int Bar = Foo;
933 // [[p]]
934 }
935 )";
936 runDataflow(
937 Code,
938 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
939 ASTContext &ASTCtx) {
940 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
941 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
942
943 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
944 ASSERT_THAT(FooDecl, NotNull());
945
946 const Value *FooVal = Env.getValue(D: *FooDecl);
947 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
948
949 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
950 ASSERT_THAT(BarDecl, NotNull());
951
952 EXPECT_EQ(Env.getValue(*BarDecl), FooVal);
953 });
954}
955
956TEST(TransferTest, VarDeclInitAssignChained) {
957 std::string Code = R"(
958 void target() {
959 int Foo;
960 int Bar;
961 int Baz = (Bar = Foo);
962 // [[p]]
963 }
964 )";
965 runDataflow(
966 Code,
967 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
968 ASTContext &ASTCtx) {
969 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
970 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
971
972 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
973 ASSERT_THAT(FooDecl, NotNull());
974
975 const Value *FooVal = Env.getValue(D: *FooDecl);
976 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
977
978 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
979 ASSERT_THAT(BarDecl, NotNull());
980
981 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
982 ASSERT_THAT(BazDecl, NotNull());
983
984 EXPECT_EQ(Env.getValue(*BarDecl), FooVal);
985 EXPECT_EQ(Env.getValue(*BazDecl), FooVal);
986 });
987}
988
989TEST(TransferTest, VarDeclInitAssignPtrDeref) {
990 std::string Code = R"(
991 void target() {
992 int Foo;
993 int *Bar;
994 *(Bar) = Foo;
995 int Baz = *(Bar);
996 // [[p]]
997 }
998 )";
999 runDataflow(
1000 Code,
1001 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1002 ASTContext &ASTCtx) {
1003 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1004 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1005
1006 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
1007 ASSERT_THAT(FooDecl, NotNull());
1008
1009 const Value *FooVal = Env.getValue(D: *FooDecl);
1010 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
1011
1012 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
1013 ASSERT_THAT(BarDecl, NotNull());
1014
1015 const auto *BarVal = cast<PointerValue>(Val: Env.getValue(D: *BarDecl));
1016 EXPECT_EQ(Env.getValue(BarVal->getPointeeLoc()), FooVal);
1017
1018 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
1019 ASSERT_THAT(BazDecl, NotNull());
1020
1021 EXPECT_EQ(Env.getValue(*BazDecl), FooVal);
1022 });
1023}
1024
1025TEST(TransferTest, AssignToAndFromReference) {
1026 std::string Code = R"(
1027 void target() {
1028 int Foo;
1029 int Bar;
1030 int &Baz = Foo;
1031 // [[p1]]
1032 Baz = Bar;
1033 int Qux = Baz;
1034 int &Quux = Baz;
1035 // [[p2]]
1036 }
1037 )";
1038 runDataflow(
1039 Code,
1040 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1041 ASTContext &ASTCtx) {
1042 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
1043 const Environment &Env1 = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p1");
1044 const Environment &Env2 = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p2");
1045
1046 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
1047 ASSERT_THAT(FooDecl, NotNull());
1048
1049 const Value *FooVal = Env1.getValue(D: *FooDecl);
1050 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
1051
1052 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
1053 ASSERT_THAT(BarDecl, NotNull());
1054
1055 const Value *BarVal = Env1.getValue(D: *BarDecl);
1056 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
1057
1058 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
1059 ASSERT_THAT(BazDecl, NotNull());
1060
1061 EXPECT_EQ(Env1.getValue(*BazDecl), FooVal);
1062
1063 EXPECT_EQ(Env2.getValue(*BazDecl), BarVal);
1064 EXPECT_EQ(Env2.getValue(*FooDecl), BarVal);
1065
1066 const ValueDecl *QuxDecl = findValueDecl(ASTCtx, Name: "Qux");
1067 ASSERT_THAT(QuxDecl, NotNull());
1068 EXPECT_EQ(Env2.getValue(*QuxDecl), BarVal);
1069
1070 const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, Name: "Quux");
1071 ASSERT_THAT(QuuxDecl, NotNull());
1072 EXPECT_EQ(Env2.getValue(*QuuxDecl), BarVal);
1073 });
1074}
1075
1076TEST(TransferTest, MultipleParamDecls) {
1077 std::string Code = R"(
1078 void target(int Foo, int Bar) {
1079 (void)0;
1080 // [[p]]
1081 }
1082 )";
1083 runDataflow(
1084 Code,
1085 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1086 ASTContext &ASTCtx) {
1087 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1088 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1089
1090 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
1091 ASSERT_THAT(FooDecl, NotNull());
1092
1093 const StorageLocation *FooLoc = Env.getStorageLocation(D: *FooDecl);
1094 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
1095
1096 const Value *FooVal = Env.getValue(Loc: *FooLoc);
1097 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
1098
1099 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
1100 ASSERT_THAT(BarDecl, NotNull());
1101
1102 const StorageLocation *BarLoc = Env.getStorageLocation(D: *BarDecl);
1103 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
1104
1105 const Value *BarVal = Env.getValue(Loc: *BarLoc);
1106 EXPECT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
1107 });
1108}
1109
1110TEST(TransferTest, StructParamDecl) {
1111 std::string Code = R"(
1112 struct A {
1113 int Bar;
1114 };
1115
1116 void target(A Foo) {
1117 (void)Foo.Bar;
1118 // [[p]]
1119 }
1120 )";
1121 runDataflow(
1122 Code,
1123 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1124 ASTContext &ASTCtx) {
1125 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1126 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1127
1128 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
1129 ASSERT_THAT(FooDecl, NotNull());
1130
1131 ASSERT_TRUE(FooDecl->getType()->isStructureType());
1132 auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
1133
1134 FieldDecl *BarDecl = nullptr;
1135 for (FieldDecl *Field : FooFields) {
1136 if (Field->getNameAsString() == "Bar") {
1137 BarDecl = Field;
1138 } else {
1139 FAIL() << "Unexpected field: " << Field->getNameAsString();
1140 }
1141 }
1142 ASSERT_THAT(BarDecl, NotNull());
1143
1144 const auto *FooLoc =
1145 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
1146 EXPECT_TRUE(isa<IntegerValue>(getFieldValue(FooLoc, *BarDecl, Env)));
1147 });
1148}
1149
1150TEST(TransferTest, ReferenceParamDecl) {
1151 std::string Code = R"(
1152 struct A {};
1153
1154 void target(A &Foo) {
1155 (void)0;
1156 // [[p]]
1157 }
1158 )";
1159 runDataflow(
1160 Code,
1161 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1162 ASTContext &ASTCtx) {
1163 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1164 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1165
1166 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
1167 ASSERT_THAT(FooDecl, NotNull());
1168
1169 const StorageLocation *FooLoc = Env.getStorageLocation(D: *FooDecl);
1170 ASSERT_TRUE(isa_and_nonnull<RecordStorageLocation>(FooLoc));
1171 });
1172}
1173
1174TEST(TransferTest, PointerParamDecl) {
1175 std::string Code = R"(
1176 struct A {};
1177
1178 void target(A *Foo) {
1179 (void)0;
1180 // [[p]]
1181 }
1182 )";
1183 runDataflow(
1184 Code,
1185 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1186 ASTContext &ASTCtx) {
1187 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1188 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1189
1190 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
1191 ASSERT_THAT(FooDecl, NotNull());
1192
1193 const StorageLocation *FooLoc = Env.getStorageLocation(D: *FooDecl);
1194 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
1195
1196 const PointerValue *FooVal = cast<PointerValue>(Val: Env.getValue(Loc: *FooLoc));
1197 const StorageLocation &FooPointeeLoc = FooVal->getPointeeLoc();
1198 EXPECT_TRUE(isa<RecordStorageLocation>(&FooPointeeLoc));
1199 });
1200}
1201
1202TEST(TransferTest, StructMember) {
1203 std::string Code = R"(
1204 struct A {
1205 int Bar;
1206 };
1207
1208 void target(A Foo) {
1209 int Baz = Foo.Bar;
1210 // [[p]]
1211 }
1212 )";
1213 runDataflow(
1214 Code,
1215 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1216 ASTContext &ASTCtx) {
1217 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1218 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1219
1220 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
1221 ASSERT_THAT(FooDecl, NotNull());
1222
1223 ASSERT_TRUE(FooDecl->getType()->isStructureType());
1224 auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
1225
1226 FieldDecl *BarDecl = nullptr;
1227 for (FieldDecl *Field : FooFields) {
1228 if (Field->getNameAsString() == "Bar") {
1229 BarDecl = Field;
1230 } else {
1231 FAIL() << "Unexpected field: " << Field->getNameAsString();
1232 }
1233 }
1234 ASSERT_THAT(BarDecl, NotNull());
1235
1236 const auto *FooLoc =
1237 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
1238 const auto *BarVal =
1239 cast<IntegerValue>(Val: getFieldValue(FooLoc, *BarDecl, Env));
1240
1241 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
1242 ASSERT_THAT(BazDecl, NotNull());
1243
1244 EXPECT_EQ(Env.getValue(*BazDecl), BarVal);
1245 });
1246}
1247
1248TEST(TransferTest, StructMemberEnum) {
1249 std::string Code = R"(
1250 struct A {
1251 int Bar;
1252 enum E { ONE, TWO };
1253 };
1254
1255 void target(A Foo) {
1256 A::E Baz = Foo.ONE;
1257 // [[p]]
1258 }
1259 )";
1260 // Minimal expectations -- we're just testing that it doesn't crash, since
1261 // enums aren't interpreted.
1262 runDataflow(
1263 Code,
1264 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1265 ASTContext &ASTCtx) {
1266 EXPECT_THAT(Results.keys(), UnorderedElementsAre("p"));
1267 });
1268}
1269
1270TEST(TransferTest, DerivedBaseMemberClass) {
1271 std::string Code = R"(
1272 class A {
1273 int ADefault;
1274 protected:
1275 int AProtected;
1276 private:
1277 int APrivate;
1278 public:
1279 int APublic;
1280
1281 private:
1282 friend void target();
1283 };
1284
1285 class B : public A {
1286 int BDefault;
1287 protected:
1288 int BProtected;
1289 private:
1290 int BPrivate;
1291
1292 private:
1293 friend void target();
1294 };
1295
1296 void target() {
1297 B Foo;
1298 (void)Foo.ADefault;
1299 (void)Foo.AProtected;
1300 (void)Foo.APrivate;
1301 (void)Foo.APublic;
1302 (void)Foo.BDefault;
1303 (void)Foo.BProtected;
1304 (void)Foo.BPrivate;
1305 // [[p]]
1306 }
1307 )";
1308 runDataflow(
1309 Code,
1310 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1311 ASTContext &ASTCtx) {
1312 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1313 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1314
1315 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
1316 ASSERT_THAT(FooDecl, NotNull());
1317 ASSERT_TRUE(FooDecl->getType()->isRecordType());
1318
1319 // Derived-class fields.
1320 const FieldDecl *BDefaultDecl = nullptr;
1321 const FieldDecl *BProtectedDecl = nullptr;
1322 const FieldDecl *BPrivateDecl = nullptr;
1323 for (const FieldDecl *Field :
1324 FooDecl->getType()->getAsRecordDecl()->fields()) {
1325 if (Field->getNameAsString() == "BDefault") {
1326 BDefaultDecl = Field;
1327 } else if (Field->getNameAsString() == "BProtected") {
1328 BProtectedDecl = Field;
1329 } else if (Field->getNameAsString() == "BPrivate") {
1330 BPrivateDecl = Field;
1331 } else {
1332 FAIL() << "Unexpected field: " << Field->getNameAsString();
1333 }
1334 }
1335 ASSERT_THAT(BDefaultDecl, NotNull());
1336 ASSERT_THAT(BProtectedDecl, NotNull());
1337 ASSERT_THAT(BPrivateDecl, NotNull());
1338
1339 // Base-class fields.
1340 const FieldDecl *ADefaultDecl = nullptr;
1341 const FieldDecl *APrivateDecl = nullptr;
1342 const FieldDecl *AProtectedDecl = nullptr;
1343 const FieldDecl *APublicDecl = nullptr;
1344 for (const clang::CXXBaseSpecifier &Base :
1345 FooDecl->getType()->getAsCXXRecordDecl()->bases()) {
1346 QualType BaseType = Base.getType();
1347 ASSERT_TRUE(BaseType->isRecordType());
1348 for (const FieldDecl *Field : BaseType->getAsRecordDecl()->fields()) {
1349 if (Field->getNameAsString() == "ADefault") {
1350 ADefaultDecl = Field;
1351 } else if (Field->getNameAsString() == "AProtected") {
1352 AProtectedDecl = Field;
1353 } else if (Field->getNameAsString() == "APrivate") {
1354 APrivateDecl = Field;
1355 } else if (Field->getNameAsString() == "APublic") {
1356 APublicDecl = Field;
1357 } else {
1358 FAIL() << "Unexpected field: " << Field->getNameAsString();
1359 }
1360 }
1361 }
1362 ASSERT_THAT(ADefaultDecl, NotNull());
1363 ASSERT_THAT(AProtectedDecl, NotNull());
1364 ASSERT_THAT(APrivateDecl, NotNull());
1365 ASSERT_THAT(APublicDecl, NotNull());
1366
1367 ASSERT_TRUE(
1368 isa<RecordStorageLocation>(Env.getStorageLocation(*FooDecl)));
1369 });
1370}
1371
1372static void derivedBaseMemberExpectations(
1373 const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1374 ASTContext &ASTCtx) {
1375 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1376 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1377
1378 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
1379 ASSERT_THAT(FooDecl, NotNull());
1380
1381 ASSERT_TRUE(FooDecl->getType()->isRecordType());
1382 const FieldDecl *BarDecl = nullptr;
1383 for (const clang::CXXBaseSpecifier &Base :
1384 FooDecl->getType()->getAsCXXRecordDecl()->bases()) {
1385 QualType BaseType = Base.getType();
1386 ASSERT_TRUE(BaseType->isStructureType());
1387
1388 for (const FieldDecl *Field : BaseType->getAsRecordDecl()->fields()) {
1389 if (Field->getNameAsString() == "Bar") {
1390 BarDecl = Field;
1391 } else {
1392 FAIL() << "Unexpected field: " << Field->getNameAsString();
1393 }
1394 }
1395 }
1396 ASSERT_THAT(BarDecl, NotNull());
1397
1398 const auto &FooLoc =
1399 *cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
1400 EXPECT_NE(Env.getValue(*FooLoc.getChild(*BarDecl)), nullptr);
1401}
1402
1403TEST(TransferTest, DerivedBaseMemberStructDefault) {
1404 std::string Code = R"(
1405 struct A {
1406 int Bar;
1407 };
1408 struct B : public A {
1409 };
1410
1411 void target() {
1412 B Foo;
1413 (void)Foo.Bar;
1414 // [[p]]
1415 }
1416 )";
1417 runDataflow(Code, VerifyResults: derivedBaseMemberExpectations);
1418}
1419
1420TEST(TransferTest, DerivedBaseMemberPrivateFriend) {
1421 // Include an access to `Foo.Bar` to verify the analysis doesn't crash on that
1422 // access.
1423 std::string Code = R"(
1424 struct A {
1425 private:
1426 friend void target();
1427 int Bar;
1428 };
1429 struct B : public A {
1430 };
1431
1432 void target() {
1433 B Foo;
1434 (void)Foo.Bar;
1435 // [[p]]
1436 }
1437 )";
1438 runDataflow(Code, VerifyResults: derivedBaseMemberExpectations);
1439}
1440
1441TEST(TransferTest, ClassMember) {
1442 std::string Code = R"(
1443 class A {
1444 public:
1445 int Bar;
1446 };
1447
1448 void target(A Foo) {
1449 int Baz = Foo.Bar;
1450 // [[p]]
1451 }
1452 )";
1453 runDataflow(
1454 Code,
1455 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1456 ASTContext &ASTCtx) {
1457 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1458 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1459
1460 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
1461 ASSERT_THAT(FooDecl, NotNull());
1462
1463 ASSERT_TRUE(FooDecl->getType()->isClassType());
1464 auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
1465
1466 FieldDecl *BarDecl = nullptr;
1467 for (FieldDecl *Field : FooFields) {
1468 if (Field->getNameAsString() == "Bar") {
1469 BarDecl = Field;
1470 } else {
1471 FAIL() << "Unexpected field: " << Field->getNameAsString();
1472 }
1473 }
1474 ASSERT_THAT(BarDecl, NotNull());
1475
1476 const auto *FooLoc =
1477 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
1478 const auto *BarVal =
1479 cast<IntegerValue>(Val: getFieldValue(FooLoc, *BarDecl, Env));
1480
1481 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
1482 ASSERT_THAT(BazDecl, NotNull());
1483
1484 EXPECT_EQ(Env.getValue(*BazDecl), BarVal);
1485 });
1486}
1487
1488TEST(TransferTest, BaseClassInitializer) {
1489 using ast_matchers::cxxConstructorDecl;
1490 using ast_matchers::hasName;
1491 using ast_matchers::ofClass;
1492
1493 std::string Code = R"(
1494 class A {
1495 public:
1496 A(int I) : Bar(I) {}
1497 int Bar;
1498 };
1499
1500 class B : public A {
1501 public:
1502 B(int I) : A(I) {
1503 (void)0;
1504 // [[p]]
1505 }
1506 };
1507 )";
1508 ASSERT_THAT_ERROR(
1509 checkDataflow<NoopAnalysis>(
1510 AnalysisInputs<NoopAnalysis>(
1511 Code, cxxConstructorDecl(ofClass(hasName("B"))),
1512 [](ASTContext &C, Environment &) { return NoopAnalysis(C); })
1513 .withASTBuildArgs(
1514 {"-fsyntax-only", "-fno-delayed-template-parsing",
1515 "-std=" + std::string(LangStandard::getLangStandardForKind(
1516 LangStandard::lang_cxx17)
1517 .getName())}),
1518 /*VerifyResults=*/
1519 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1520 const AnalysisOutputs &) {
1521 // Regression test to verify that base-class initializers do not
1522 // trigger an assertion. If we add support for such initializers in
1523 // the future, we can expand this test to check more specific
1524 // properties.
1525 EXPECT_THAT(Results.keys(), UnorderedElementsAre("p"));
1526 }),
1527 llvm::Succeeded());
1528}
1529
1530TEST(TransferTest, FieldsDontHaveValuesInConstructor) {
1531 // In a constructor, unlike in regular member functions, we don't want fields
1532 // to be pre-initialized with values, because doing so is the job of the
1533 // constructor.
1534 std::string Code = R"(
1535 struct target {
1536 target() {
1537 0;
1538 // [[p]]
1539 // Mention the field so it is modeled;
1540 Val;
1541 }
1542
1543 int Val;
1544 };
1545 )";
1546 runDataflow(
1547 Code,
1548 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1549 ASTContext &ASTCtx) {
1550 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1551 EXPECT_EQ(getFieldValue(Env.getThisPointeeStorageLocation(), "Val",
1552 ASTCtx, Env),
1553 nullptr);
1554 });
1555}
1556
1557TEST(TransferTest, FieldsDontHaveValuesInConstructorWithBaseClass) {
1558 // See above, but for a class with a base class.
1559 std::string Code = R"(
1560 struct Base {
1561 int BaseVal;
1562 };
1563
1564 struct target : public Base {
1565 target() {
1566 0;
1567 // [[p]]
1568 // Mention the fields so they are modeled.
1569 BaseVal;
1570 Val;
1571 }
1572
1573 int Val;
1574 };
1575 )";
1576 runDataflow(
1577 Code,
1578 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1579 ASTContext &ASTCtx) {
1580 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1581 // The field of the base class should already have been initialized with
1582 // a value by the base constructor.
1583 EXPECT_NE(getFieldValue(Env.getThisPointeeStorageLocation(), "BaseVal",
1584 ASTCtx, Env),
1585 nullptr);
1586 EXPECT_EQ(getFieldValue(Env.getThisPointeeStorageLocation(), "Val",
1587 ASTCtx, Env),
1588 nullptr);
1589 });
1590}
1591
1592TEST(TransferTest, StructModeledFieldsWithAccessor) {
1593 std::string Code = R"(
1594 class S {
1595 int *Ptr;
1596 int *PtrNonConst;
1597 int Int;
1598 int IntWithInc;
1599 int IntNotAccessed;
1600 int IntRef;
1601 public:
1602 int *getPtr() const { return Ptr; }
1603 int *getPtrNonConst() { return PtrNonConst; }
1604 int getInt(int i) const { return Int; }
1605 int getWithInc(int i) { IntWithInc += i; return IntWithInc; }
1606 int getIntNotAccessed() const { return IntNotAccessed; }
1607 int getIntNoDefinition() const;
1608 int &getIntRef() { return IntRef; }
1609 void returnVoid() const { return; }
1610 };
1611
1612 void target() {
1613 S s;
1614 int *p1 = s.getPtr();
1615 int *p2 = s.getPtrNonConst();
1616 int i1 = s.getInt(1);
1617 int i2 = s.getWithInc(1);
1618 int i3 = s.getIntNoDefinition();
1619 int &iref = s.getIntRef();
1620
1621 // Regression test: Don't crash on an indirect call (which doesn't have
1622 // an associated `CXXMethodDecl`).
1623 auto ptr_to_member_fn = &S::getPtr;
1624 p1 = (s.*ptr_to_member_fn)();
1625
1626 // Regression test: Don't crash on a return statement without a value.
1627 s.returnVoid();
1628 // [[p]]
1629 }
1630 )";
1631 runDataflow(
1632 Code,
1633 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1634 ASTContext &ASTCtx) {
1635 const Environment &Env =
1636 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1637 auto &SLoc = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "s");
1638 std::vector<const ValueDecl*> Fields;
1639 for (auto [Field, _] : SLoc.children())
1640 Fields.push_back(x: Field);
1641 // Only the fields that have simple accessor methods (that have a
1642 // single statement body that returns the member variable) should be
1643 // modeled.
1644 ASSERT_THAT(Fields, UnorderedElementsAre(
1645 findValueDecl(ASTCtx, "Ptr"), findValueDecl(ASTCtx, "PtrNonConst"),
1646 findValueDecl(ASTCtx, "Int"), findValueDecl(ASTCtx, "IntRef")));
1647 });
1648}
1649
1650TEST(TransferTest, StructModeledFieldsInTypeid) {
1651 // Test that we model fields mentioned inside a `typeid()` expression only if
1652 // that expression is potentially evaluated -- i.e. if the expression inside
1653 // `typeid()` is a glvalue of polymorphic type (see
1654 // `CXXTypeidExpr::isPotentiallyEvaluated()` and [expr.typeid]p3).
1655 std::string Code = R"(
1656 // Definitions needed for `typeid`.
1657 namespace std {
1658 class type_info {};
1659 class bad_typeid {};
1660 } // namespace std
1661
1662 struct NonPolymorphic {};
1663
1664 struct Polymorphic {
1665 virtual ~Polymorphic() = default;
1666 };
1667
1668 struct S {
1669 NonPolymorphic *NonPoly;
1670 Polymorphic *Poly;
1671 };
1672
1673 void target(S &s) {
1674 typeid(*s.NonPoly);
1675 typeid(*s.Poly);
1676 // [[p]]
1677 }
1678 )";
1679 runDataflow(
1680 Code,
1681 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1682 ASTContext &ASTCtx) {
1683 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1684 auto &SLoc = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "s");
1685 std::vector<const ValueDecl *> Fields;
1686 for (auto [Field, _] : SLoc.children())
1687 Fields.push_back(x: Field);
1688 EXPECT_THAT(Fields,
1689 UnorderedElementsAre(findValueDecl(ASTCtx, "Poly")));
1690 });
1691}
1692
1693TEST(TransferTest, StructModeledFieldsWithComplicatedInheritance) {
1694 std::string Code = R"(
1695 struct Base1 {
1696 int base1_1;
1697 int base1_2;
1698 };
1699 struct Intermediate : Base1 {
1700 int intermediate_1;
1701 int intermediate_2;
1702 };
1703 struct Base2 {
1704 int base2_1;
1705 int base2_2;
1706 };
1707 struct MostDerived : public Intermediate, Base2 {
1708 int most_derived_1;
1709 int most_derived_2;
1710 };
1711
1712 void target() {
1713 MostDerived MD;
1714 MD.base1_2 = 1;
1715 MD.intermediate_2 = 1;
1716 MD.base2_2 = 1;
1717 MD.most_derived_2 = 1;
1718 // [[p]]
1719 }
1720 )";
1721 runDataflow(
1722 Code,
1723 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1724 ASTContext &ASTCtx) {
1725 const Environment &Env =
1726 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1727
1728 // Only the accessed fields should exist in the model.
1729 auto &MDLoc = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "MD");
1730 std::vector<const ValueDecl*> Fields;
1731 for (auto [Field, _] : MDLoc.children())
1732 Fields.push_back(x: Field);
1733 ASSERT_THAT(Fields, UnorderedElementsAre(
1734 findValueDecl(ASTCtx, "base1_2"),
1735 findValueDecl(ASTCtx, "intermediate_2"),
1736 findValueDecl(ASTCtx, "base2_2"),
1737 findValueDecl(ASTCtx, "most_derived_2")));
1738 });
1739}
1740
1741TEST(TransferTest, StructInitializerListWithComplicatedInheritance) {
1742 std::string Code = R"(
1743 struct Base1 {
1744 int base1;
1745 };
1746 struct Intermediate : Base1 {
1747 int intermediate;
1748 };
1749 struct Base2 {
1750 int base2;
1751 };
1752 struct MostDerived : public Intermediate, Base2 {
1753 int most_derived;
1754 };
1755
1756 void target() {
1757 MostDerived MD = {};
1758 // [[p]]
1759 }
1760 )";
1761 runDataflow(
1762 Code,
1763 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1764 ASTContext &ASTCtx) {
1765 const Environment &Env =
1766 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1767
1768 // When a struct is initialized with a initializer list, all the
1769 // fields are considered "accessed", and therefore do exist.
1770 auto &MD = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "MD");
1771 ASSERT_THAT(cast<IntegerValue>(
1772 getFieldValue(&MD, *findValueDecl(ASTCtx, "base1"), Env)),
1773 NotNull());
1774 ASSERT_THAT(cast<IntegerValue>(
1775 getFieldValue(&MD, *findValueDecl(ASTCtx, "intermediate"), Env)),
1776 NotNull());
1777 ASSERT_THAT(cast<IntegerValue>(
1778 getFieldValue(&MD, *findValueDecl(ASTCtx, "base2"), Env)),
1779 NotNull());
1780 ASSERT_THAT(cast<IntegerValue>(
1781 getFieldValue(&MD, *findValueDecl(ASTCtx, "most_derived"), Env)),
1782 NotNull());
1783 });
1784}
1785
1786TEST(TransferTest, ReferenceMember) {
1787 std::string Code = R"(
1788 struct A {
1789 int &Bar;
1790 };
1791
1792 void target(A Foo) {
1793 int Baz = Foo.Bar;
1794 // [[p]]
1795 }
1796 )";
1797 runDataflow(
1798 Code,
1799 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1800 ASTContext &ASTCtx) {
1801 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1802 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1803
1804 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
1805 ASSERT_THAT(FooDecl, NotNull());
1806
1807 ASSERT_TRUE(FooDecl->getType()->isStructureType());
1808 auto FooFields = FooDecl->getType()->getAsRecordDecl()->fields();
1809
1810 FieldDecl *BarDecl = nullptr;
1811 for (FieldDecl *Field : FooFields) {
1812 if (Field->getNameAsString() == "Bar") {
1813 BarDecl = Field;
1814 } else {
1815 FAIL() << "Unexpected field: " << Field->getNameAsString();
1816 }
1817 }
1818 ASSERT_THAT(BarDecl, NotNull());
1819
1820 const auto *FooLoc =
1821 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
1822 const auto *BarReferentVal =
1823 cast<IntegerValue>(Val: getFieldValue(FooLoc, *BarDecl, Env));
1824
1825 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
1826 ASSERT_THAT(BazDecl, NotNull());
1827
1828 EXPECT_EQ(Env.getValue(*BazDecl), BarReferentVal);
1829 });
1830}
1831
1832TEST(TransferTest, StructThisMember) {
1833 std::string Code = R"(
1834 struct A {
1835 int Bar;
1836
1837 struct B {
1838 int Baz;
1839 };
1840
1841 B Qux;
1842
1843 void target() {
1844 int Foo = Bar;
1845 int Quux = Qux.Baz;
1846 // [[p]]
1847 }
1848 };
1849 )";
1850 runDataflow(
1851 Code,
1852 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1853 ASTContext &ASTCtx) {
1854 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1855 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1856
1857 const auto *ThisLoc = Env.getThisPointeeStorageLocation();
1858 ASSERT_THAT(ThisLoc, NotNull());
1859
1860 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
1861 ASSERT_THAT(BarDecl, NotNull());
1862
1863 const auto *BarLoc =
1864 cast<ScalarStorageLocation>(Val: ThisLoc->getChild(D: *BarDecl));
1865 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
1866
1867 const Value *BarVal = Env.getValue(*BarLoc);
1868 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
1869
1870 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
1871 ASSERT_THAT(FooDecl, NotNull());
1872 EXPECT_EQ(Env.getValue(*FooDecl), BarVal);
1873
1874 const ValueDecl *QuxDecl = findValueDecl(ASTCtx, Name: "Qux");
1875 ASSERT_THAT(QuxDecl, NotNull());
1876
1877 ASSERT_TRUE(QuxDecl->getType()->isStructureType());
1878 auto QuxFields = QuxDecl->getType()->getAsRecordDecl()->fields();
1879
1880 FieldDecl *BazDecl = nullptr;
1881 for (FieldDecl *Field : QuxFields) {
1882 if (Field->getNameAsString() == "Baz") {
1883 BazDecl = Field;
1884 } else {
1885 FAIL() << "Unexpected field: " << Field->getNameAsString();
1886 }
1887 }
1888 ASSERT_THAT(BazDecl, NotNull());
1889
1890 const auto *QuxLoc =
1891 cast<RecordStorageLocation>(Val: ThisLoc->getChild(D: *QuxDecl));
1892
1893 const auto *BazVal =
1894 cast<IntegerValue>(Val: getFieldValue(QuxLoc, *BazDecl, Env));
1895
1896 const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, Name: "Quux");
1897 ASSERT_THAT(QuuxDecl, NotNull());
1898 EXPECT_EQ(Env.getValue(*QuuxDecl), BazVal);
1899 });
1900}
1901
1902TEST(TransferTest, ClassThisMember) {
1903 std::string Code = R"(
1904 class A {
1905 int Bar;
1906
1907 class B {
1908 public:
1909 int Baz;
1910 };
1911
1912 B Qux;
1913
1914 void target() {
1915 int Foo = Bar;
1916 int Quux = Qux.Baz;
1917 // [[p]]
1918 }
1919 };
1920 )";
1921 runDataflow(
1922 Code,
1923 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1924 ASTContext &ASTCtx) {
1925 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1926 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1927
1928 const auto *ThisLoc = Env.getThisPointeeStorageLocation();
1929
1930 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
1931 ASSERT_THAT(BarDecl, NotNull());
1932
1933 const auto *BarLoc =
1934 cast<ScalarStorageLocation>(Val: ThisLoc->getChild(D: *BarDecl));
1935 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
1936
1937 const Value *BarVal = Env.getValue(*BarLoc);
1938 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
1939
1940 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
1941 ASSERT_THAT(FooDecl, NotNull());
1942 EXPECT_EQ(Env.getValue(*FooDecl), BarVal);
1943
1944 const ValueDecl *QuxDecl = findValueDecl(ASTCtx, Name: "Qux");
1945 ASSERT_THAT(QuxDecl, NotNull());
1946
1947 ASSERT_TRUE(QuxDecl->getType()->isClassType());
1948 auto QuxFields = QuxDecl->getType()->getAsRecordDecl()->fields();
1949
1950 FieldDecl *BazDecl = nullptr;
1951 for (FieldDecl *Field : QuxFields) {
1952 if (Field->getNameAsString() == "Baz") {
1953 BazDecl = Field;
1954 } else {
1955 FAIL() << "Unexpected field: " << Field->getNameAsString();
1956 }
1957 }
1958 ASSERT_THAT(BazDecl, NotNull());
1959
1960 const auto *QuxLoc =
1961 cast<RecordStorageLocation>(Val: ThisLoc->getChild(D: *QuxDecl));
1962
1963 const auto *BazVal =
1964 cast<IntegerValue>(Val: getFieldValue(QuxLoc, *BazDecl, Env));
1965
1966 const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, Name: "Quux");
1967 ASSERT_THAT(QuuxDecl, NotNull());
1968 EXPECT_EQ(Env.getValue(*QuuxDecl), BazVal);
1969 });
1970}
1971
1972TEST(TransferTest, UnionThisMember) {
1973 std::string Code = R"(
1974 union A {
1975 int Foo;
1976 int Bar;
1977
1978 void target() {
1979 A a;
1980 // Mention the fields to ensure they're included in the analysis.
1981 (void)a.Foo;
1982 (void)a.Bar;
1983 // [[p]]
1984 }
1985 };
1986 )";
1987 runDataflow(
1988 Code,
1989 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
1990 ASTContext &ASTCtx) {
1991 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
1992 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
1993
1994 const auto *ThisLoc = Env.getThisPointeeStorageLocation();
1995 ASSERT_THAT(ThisLoc, NotNull());
1996
1997 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
1998 ASSERT_THAT(FooDecl, NotNull());
1999
2000 const auto *FooLoc =
2001 cast<ScalarStorageLocation>(Val: ThisLoc->getChild(D: *FooDecl));
2002 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
2003
2004 const Value *FooVal = Env.getValue(*FooLoc);
2005 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
2006
2007 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
2008 ASSERT_THAT(BarDecl, NotNull());
2009
2010 const auto *BarLoc =
2011 cast<ScalarStorageLocation>(Val: ThisLoc->getChild(D: *BarDecl));
2012 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
2013
2014 const Value *BarVal = Env.getValue(*BarLoc);
2015 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
2016 });
2017}
2018
2019TEST(TransferTest, StructThisInLambda) {
2020 std::string ThisCaptureCode = R"(
2021 struct A {
2022 void frob() {
2023 [this]() {
2024 int Foo = Bar;
2025 // [[p1]]
2026 }();
2027 }
2028
2029 int Bar;
2030 };
2031 )";
2032 runDataflow(
2033 ThisCaptureCode,
2034 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2035 ASTContext &ASTCtx) {
2036 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1"));
2037 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p1");
2038
2039 const auto *ThisLoc = Env.getThisPointeeStorageLocation();
2040 ASSERT_THAT(ThisLoc, NotNull());
2041
2042 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
2043 ASSERT_THAT(BarDecl, NotNull());
2044
2045 const auto *BarLoc =
2046 cast<ScalarStorageLocation>(Val: ThisLoc->getChild(D: *BarDecl));
2047 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
2048
2049 const Value *BarVal = Env.getValue(*BarLoc);
2050 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
2051
2052 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
2053 ASSERT_THAT(FooDecl, NotNull());
2054 EXPECT_EQ(Env.getValue(*FooDecl), BarVal);
2055 },
2056 LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator()");
2057
2058 std::string RefCaptureDefaultCode = R"(
2059 struct A {
2060 void frob() {
2061 [&]() {
2062 int Foo = Bar;
2063 // [[p2]]
2064 }();
2065 }
2066
2067 int Bar;
2068 };
2069 )";
2070 runDataflow(
2071 RefCaptureDefaultCode,
2072 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2073 ASTContext &ASTCtx) {
2074 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p2"));
2075 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p2");
2076
2077 const auto *ThisLoc = Env.getThisPointeeStorageLocation();
2078 ASSERT_THAT(ThisLoc, NotNull());
2079
2080 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
2081 ASSERT_THAT(BarDecl, NotNull());
2082
2083 const auto *BarLoc =
2084 cast<ScalarStorageLocation>(Val: ThisLoc->getChild(D: *BarDecl));
2085 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
2086
2087 const Value *BarVal = Env.getValue(*BarLoc);
2088 ASSERT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
2089
2090 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
2091 ASSERT_THAT(FooDecl, NotNull());
2092 EXPECT_EQ(Env.getValue(*FooDecl), BarVal);
2093 },
2094 LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, "operator()");
2095
2096 std::string FreeFunctionLambdaCode = R"(
2097 void foo() {
2098 int Bar;
2099 [&]() {
2100 int Foo = Bar;
2101 // [[p3]]
2102 }();
2103 }
2104 )";
2105 runDataflow(
2106 Code: FreeFunctionLambdaCode,
2107 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2108 ASTContext &ASTCtx) {
2109 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p3"));
2110 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p3");
2111
2112 EXPECT_THAT(Env.getThisPointeeStorageLocation(), IsNull());
2113 },
2114 Std: LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, TargetFun: "operator()");
2115}
2116
2117TEST(TransferTest, ConstructorInitializer) {
2118 std::string Code = R"(
2119 struct target {
2120 int Bar;
2121
2122 target(int Foo) : Bar(Foo) {
2123 int Qux = Bar;
2124 // [[p]]
2125 }
2126 };
2127 )";
2128 runDataflow(
2129 Code,
2130 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2131 ASTContext &ASTCtx) {
2132 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
2133 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
2134
2135 const auto *ThisLoc = Env.getThisPointeeStorageLocation();
2136 ASSERT_THAT(ThisLoc, NotNull());
2137
2138 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
2139 ASSERT_THAT(FooDecl, NotNull());
2140
2141 const auto *FooVal = cast<IntegerValue>(Val: Env.getValue(D: *FooDecl));
2142
2143 const ValueDecl *QuxDecl = findValueDecl(ASTCtx, Name: "Qux");
2144 ASSERT_THAT(QuxDecl, NotNull());
2145 EXPECT_EQ(Env.getValue(*QuxDecl), FooVal);
2146 });
2147}
2148
2149TEST(TransferTest, DefaultInitializer) {
2150 std::string Code = R"(
2151 struct target {
2152 int Bar;
2153 int Baz = Bar;
2154
2155 target(int Foo) : Bar(Foo) {
2156 int Qux = Baz;
2157 // [[p]]
2158 }
2159 };
2160 )";
2161 runDataflow(
2162 Code,
2163 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2164 ASTContext &ASTCtx) {
2165 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
2166 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
2167
2168 const auto *ThisLoc = Env.getThisPointeeStorageLocation();
2169 ASSERT_THAT(ThisLoc, NotNull());
2170
2171 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
2172 ASSERT_THAT(FooDecl, NotNull());
2173
2174 const auto *FooVal = cast<IntegerValue>(Val: Env.getValue(D: *FooDecl));
2175
2176 const ValueDecl *QuxDecl = findValueDecl(ASTCtx, Name: "Qux");
2177 ASSERT_THAT(QuxDecl, NotNull());
2178 EXPECT_EQ(Env.getValue(*QuxDecl), FooVal);
2179 });
2180}
2181
2182TEST(TransferTest, DefaultInitializerReference) {
2183 std::string Code = R"(
2184 struct target {
2185 int &Bar;
2186 int &Baz = Bar;
2187
2188 target(int &Foo) : Bar(Foo) {
2189 int &Qux = Baz;
2190 // [[p]]
2191 }
2192 };
2193 )";
2194 runDataflow(
2195 Code,
2196 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2197 ASTContext &ASTCtx) {
2198 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
2199 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
2200
2201 const auto *ThisLoc = Env.getThisPointeeStorageLocation();
2202 ASSERT_THAT(ThisLoc, NotNull());
2203
2204 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
2205 ASSERT_THAT(FooDecl, NotNull());
2206
2207 const auto *FooLoc = Env.getStorageLocation(D: *FooDecl);
2208
2209 const ValueDecl *QuxDecl = findValueDecl(ASTCtx, Name: "Qux");
2210 ASSERT_THAT(QuxDecl, NotNull());
2211
2212 const auto *QuxLoc = Env.getStorageLocation(D: *QuxDecl);
2213 EXPECT_EQ(QuxLoc, FooLoc);
2214 });
2215}
2216
2217TEST(TransferTest, TemporaryObject) {
2218 std::string Code = R"(
2219 struct A {
2220 int Bar;
2221 };
2222
2223 void target() {
2224 A Foo = A();
2225 (void)Foo.Bar;
2226 // [[p]]
2227 }
2228 )";
2229 runDataflow(
2230 Code,
2231 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2232 ASTContext &ASTCtx) {
2233 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
2234 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
2235
2236 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
2237 ASSERT_THAT(FooDecl, NotNull());
2238
2239 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
2240 ASSERT_THAT(BarDecl, NotNull());
2241
2242 const auto *FooLoc =
2243 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
2244 EXPECT_TRUE(isa<IntegerValue>(getFieldValue(FooLoc, *BarDecl, Env)));
2245 });
2246}
2247
2248TEST(TransferTest, ElidableConstructor) {
2249 // This test is effectively the same as TransferTest.TemporaryObject, but
2250 // the code is compiled as C++14.
2251 std::string Code = R"(
2252 struct A {
2253 int Bar;
2254 };
2255
2256 void target() {
2257 A Foo = A();
2258 (void)Foo.Bar;
2259 // [[p]]
2260 }
2261 )";
2262 runDataflow(
2263 Code,
2264 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2265 ASTContext &ASTCtx) {
2266 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
2267 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
2268
2269 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
2270 ASSERT_THAT(FooDecl, NotNull());
2271
2272 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
2273 ASSERT_THAT(BarDecl, NotNull());
2274
2275 const auto *FooLoc =
2276 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
2277 EXPECT_TRUE(isa<IntegerValue>(getFieldValue(FooLoc, *BarDecl, Env)));
2278 },
2279 Std: LangStandard::lang_cxx14);
2280}
2281
2282TEST(TransferTest, AssignmentOperator) {
2283 std::string Code = R"(
2284 struct A {
2285 int Baz;
2286 };
2287
2288 void target() {
2289 A Foo = { 1 };
2290 A Bar = { 2 };
2291 // [[p1]]
2292 A &Rval = (Foo = Bar);
2293 // [[p2]]
2294 Foo.Baz = 3;
2295 // [[p3]]
2296 }
2297 )";
2298 runDataflow(
2299 Code,
2300 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2301 ASTContext &ASTCtx) {
2302 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
2303 ASSERT_THAT(FooDecl, NotNull());
2304
2305 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
2306 ASSERT_THAT(BarDecl, NotNull());
2307
2308 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
2309 ASSERT_THAT(BazDecl, NotNull());
2310
2311 // Before copy assignment.
2312 {
2313 const Environment &Env1 = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p1");
2314
2315 const auto *FooLoc1 =
2316 cast<RecordStorageLocation>(Val: Env1.getStorageLocation(D: *FooDecl));
2317 const auto *BarLoc1 =
2318 cast<RecordStorageLocation>(Val: Env1.getStorageLocation(D: *BarDecl));
2319 EXPECT_FALSE(recordsEqual(*FooLoc1, *BarLoc1, Env1));
2320
2321 const auto *FooBazVal1 =
2322 cast<IntegerValue>(Val: getFieldValue(Loc: FooLoc1, Field: *BazDecl, Env: Env1));
2323 const auto *BarBazVal1 =
2324 cast<IntegerValue>(Val: getFieldValue(Loc: BarLoc1, Field: *BazDecl, Env: Env1));
2325 EXPECT_NE(FooBazVal1, BarBazVal1);
2326 }
2327
2328 // After copy assignment.
2329 {
2330 const Environment &Env2 = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p2");
2331
2332 const auto *FooLoc2 =
2333 cast<RecordStorageLocation>(Val: Env2.getStorageLocation(D: *FooDecl));
2334 const auto *BarLoc2 =
2335 cast<RecordStorageLocation>(Val: Env2.getStorageLocation(D: *BarDecl));
2336
2337 EXPECT_TRUE(recordsEqual(*FooLoc2, *BarLoc2, Env2));
2338 EXPECT_EQ(&getLocForDecl(ASTCtx, Env2, "Rval"), FooLoc2);
2339
2340 const auto *FooBazVal2 =
2341 cast<IntegerValue>(Val: getFieldValue(Loc: FooLoc2, Field: *BazDecl, Env: Env2));
2342 const auto *BarBazVal2 =
2343 cast<IntegerValue>(Val: getFieldValue(Loc: BarLoc2, Field: *BazDecl, Env: Env2));
2344 EXPECT_EQ(FooBazVal2, BarBazVal2);
2345 }
2346
2347 // After value update.
2348 {
2349 const Environment &Env3 = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p3");
2350
2351 const auto *FooLoc3 =
2352 cast<RecordStorageLocation>(Val: Env3.getStorageLocation(D: *FooDecl));
2353 const auto *BarLoc3 =
2354 cast<RecordStorageLocation>(Val: Env3.getStorageLocation(D: *BarDecl));
2355 EXPECT_FALSE(recordsEqual(*FooLoc3, *BarLoc3, Env3));
2356
2357 const auto *FooBazVal3 =
2358 cast<IntegerValue>(Val: getFieldValue(Loc: FooLoc3, Field: *BazDecl, Env: Env3));
2359 const auto *BarBazVal3 =
2360 cast<IntegerValue>(Val: getFieldValue(Loc: BarLoc3, Field: *BazDecl, Env: Env3));
2361 EXPECT_NE(FooBazVal3, BarBazVal3);
2362 }
2363 });
2364}
2365
2366// It's legal for the assignment operator to take its source parameter by value.
2367// Check that we handle this correctly. (This is a repro -- we used to
2368// assert-fail on this.)
2369TEST(TransferTest, AssignmentOperator_ArgByValue) {
2370 std::string Code = R"(
2371 struct A {
2372 int Baz;
2373 A &operator=(A);
2374 };
2375
2376 void target() {
2377 A Foo = { 1 };
2378 A Bar = { 2 };
2379 Foo = Bar;
2380 // [[p]]
2381 }
2382 )";
2383 runDataflow(
2384 Code,
2385 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2386 ASTContext &ASTCtx) {
2387 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
2388 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
2389
2390 const auto &FooLoc =
2391 getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "Foo");
2392 const auto &BarLoc =
2393 getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "Bar");
2394
2395 const auto *FooBazVal =
2396 cast<IntegerValue>(Val: getFieldValue(Loc: &FooLoc, Field: *BazDecl, Env));
2397 const auto *BarBazVal =
2398 cast<IntegerValue>(Val: getFieldValue(Loc: &BarLoc, Field: *BazDecl, Env));
2399 EXPECT_EQ(FooBazVal, BarBazVal);
2400 });
2401}
2402
2403TEST(TransferTest, AssignmentOperatorFromBase) {
2404 std::string Code = R"(
2405 struct Base {
2406 int base;
2407 };
2408 struct Derived : public Base {
2409 using Base::operator=;
2410 int derived;
2411 };
2412 void target(Base B, Derived D) {
2413 D.base = 1;
2414 D.derived = 1;
2415 // [[before]]
2416 D = B;
2417 // [[after]]
2418 }
2419 )";
2420 runDataflow(
2421 Code,
2422 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2423 ASTContext &ASTCtx) {
2424 const Environment &EnvBefore =
2425 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "before");
2426 const Environment &EnvAfter =
2427 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "after");
2428
2429 auto &BLoc =
2430 getLocForDecl<RecordStorageLocation>(ASTCtx, Env: EnvBefore, Name: "B");
2431 auto &DLoc =
2432 getLocForDecl<RecordStorageLocation>(ASTCtx, Env: EnvBefore, Name: "D");
2433
2434 EXPECT_NE(getFieldValue(&BLoc, "base", ASTCtx, EnvBefore),
2435 getFieldValue(&DLoc, "base", ASTCtx, EnvBefore));
2436 EXPECT_EQ(getFieldValue(&BLoc, "base", ASTCtx, EnvAfter),
2437 getFieldValue(&DLoc, "base", ASTCtx, EnvAfter));
2438
2439 EXPECT_EQ(getFieldValue(&DLoc, "derived", ASTCtx, EnvBefore),
2440 getFieldValue(&DLoc, "derived", ASTCtx, EnvAfter));
2441 });
2442}
2443
2444TEST(TransferTest, AssignmentOperatorFromCallResult) {
2445 std::string Code = R"(
2446 struct A {};
2447 A ReturnA();
2448
2449 void target() {
2450 A MyA;
2451 MyA = ReturnA();
2452 }
2453 )";
2454 runDataflow(
2455 Code,
2456 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2457 ASTContext &ASTCtx) {
2458 // As of this writing, we don't produce a `Value` for the call
2459 // `ReturnA()`. The only condition we're testing for is that the
2460 // analysis should not crash in this case.
2461 });
2462}
2463
2464TEST(TransferTest, AssignmentOperatorWithInitAndInheritance) {
2465 // This is a crash repro.
2466 std::string Code = R"(
2467 struct B { int Foo; };
2468 struct S : public B {};
2469 void target() {
2470 S S1 = { 1 };
2471 S S2;
2472 S S3;
2473 S1 = S2; // Only Dst has InitListExpr.
2474 S3 = S1; // Only Src has InitListExpr.
2475 // [[p]]
2476 }
2477 )";
2478 runDataflow(
2479 Code,
2480 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2481 ASTContext &ASTCtx) {});
2482}
2483
2484TEST(TransferTest, AssignmentOperatorReturnsVoid) {
2485 // This is a crash repro.
2486 std::string Code = R"(
2487 struct S {
2488 void operator=(S&& other);
2489 };
2490 void target() {
2491 S s;
2492 s = S();
2493 // [[p]]
2494 }
2495 )";
2496 runDataflow(
2497 Code,
2498 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2499 ASTContext &ASTCtx) {});
2500}
2501
2502TEST(TransferTest, AssignmentOperatorReturnsByValue) {
2503 // This is a crash repro.
2504 std::string Code = R"(
2505 struct S {
2506 S operator=(const S&);
2507 int i;
2508 };
2509 void target() {
2510 S S1 = { 1 };
2511 S S2 = { 2 };
2512 S S3 = { 3 };
2513 // [[before]]
2514 // Test that the returned value is modeled by assigning to another value.
2515 S1 = (S2 = S3);
2516 (void)0;
2517 // [[after]]
2518 }
2519 )";
2520 runDataflow(
2521 Code,
2522 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2523 ASTContext &ASTCtx) {
2524 const ValueDecl *S1Decl = findValueDecl(ASTCtx, Name: "S1");
2525 const ValueDecl *S2Decl = findValueDecl(ASTCtx, Name: "S2");
2526 const ValueDecl *S3Decl = findValueDecl(ASTCtx, Name: "S3");
2527
2528 const Environment &EnvBefore =
2529 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "before");
2530
2531 EXPECT_FALSE(recordsEqual(
2532 *EnvBefore.get<RecordStorageLocation>(*S1Decl),
2533 *EnvBefore.get<RecordStorageLocation>(*S2Decl), EnvBefore));
2534 EXPECT_FALSE(recordsEqual(
2535 *EnvBefore.get<RecordStorageLocation>(*S2Decl),
2536 *EnvBefore.get<RecordStorageLocation>(*S3Decl), EnvBefore));
2537
2538 const Environment &EnvAfter =
2539 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "after");
2540
2541 EXPECT_TRUE(recordsEqual(*EnvAfter.get<RecordStorageLocation>(*S1Decl),
2542 *EnvAfter.get<RecordStorageLocation>(*S2Decl),
2543 EnvAfter));
2544 EXPECT_TRUE(recordsEqual(*EnvAfter.get<RecordStorageLocation>(*S2Decl),
2545 *EnvAfter.get<RecordStorageLocation>(*S3Decl),
2546 EnvAfter));
2547 });
2548}
2549
2550TEST(TransferTest, AssignmentOperatorReturnsDifferentTypeByRef) {
2551 // This is a crash repro.
2552 std::string Code = R"(
2553 struct DifferentType {};
2554 struct S {
2555 DifferentType& operator=(const S&);
2556 };
2557 void target() {
2558 S s;
2559 s = S();
2560 // [[p]]
2561 }
2562 )";
2563 runDataflow(
2564 Code,
2565 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2566 ASTContext &ASTCtx) {});
2567}
2568
2569TEST(TransferTest, AssignmentOperatorReturnsDifferentTypeByValue) {
2570 // This is a crash repro.
2571 std::string Code = R"(
2572 struct DifferentType {};
2573 struct S {
2574 DifferentType operator=(const S&);
2575 };
2576 void target() {
2577 S s;
2578 s = S();
2579 // [[p]]
2580 }
2581 )";
2582 runDataflow(
2583 Code,
2584 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2585 ASTContext &ASTCtx) {});
2586}
2587
2588TEST(TransferTest, InitListExprAsXValue) {
2589 // This is a crash repro.
2590 std::string Code = R"(
2591 void target() {
2592 bool&& Foo{false};
2593 // [[p]]
2594 }
2595 )";
2596 runDataflow(
2597 Code,
2598 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2599 ASTContext &ASTCtx) {
2600 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
2601 const auto &FooVal = getValueForDecl<BoolValue>(ASTCtx, Env, Name: "Foo");
2602 ASSERT_TRUE(FooVal.formula().isLiteral(false));
2603 });
2604}
2605
2606TEST(TransferTest, ArrayInitListExprOneRecordElement) {
2607 // This is a crash repro.
2608 std::string Code = R"cc(
2609 struct S {};
2610
2611 void target() { S foo[] = {S()}; }
2612 )cc";
2613 runDataflow(
2614 Code,
2615 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2616 ASTContext &ASTCtx) {
2617 // Just verify that it doesn't crash.
2618 });
2619}
2620
2621TEST(TransferTest, InitListExprAsUnion) {
2622 // This is a crash repro.
2623 std::string Code = R"cc(
2624 class target {
2625 union {
2626 int *a;
2627 bool *b;
2628 } F;
2629
2630 public:
2631 constexpr target() : F{nullptr} {
2632 int *null = nullptr;
2633 F.b; // Make sure we reference 'b' so it is modeled.
2634 // [[p]]
2635 }
2636 };
2637 )cc";
2638 runDataflow(
2639 Code,
2640 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2641 ASTContext &ASTCtx) {
2642 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
2643
2644 auto &FLoc = getFieldLoc<RecordStorageLocation>(
2645 Loc: *Env.getThisPointeeStorageLocation(), Name: "F", ASTCtx);
2646 auto *AVal = cast<PointerValue>(Val: getFieldValue(Loc: &FLoc, Name: "a", ASTCtx, Env));
2647 EXPECT_EQ(AVal, &getValueForDecl<PointerValue>(ASTCtx, Env, "null"));
2648 EXPECT_EQ(getFieldValue(&FLoc, "b", ASTCtx, Env), nullptr);
2649 });
2650}
2651
2652TEST(TransferTest, EmptyInitListExprForUnion) {
2653 // This is a crash repro.
2654 std::string Code = R"cc(
2655 class target {
2656 union {
2657 int *a;
2658 bool *b;
2659 } F;
2660
2661 public:
2662 // Empty initializer list means that `F` is aggregate-initialized.
2663 // For a union, this has the effect that the first member of the union
2664 // is copy-initialized from an empty initializer list; in this specific
2665 // case, this has the effect of initializing `a` with null.
2666 constexpr target() : F{} {
2667 int *null = nullptr;
2668 F.b; // Make sure we reference 'b' so it is modeled.
2669 // [[p]]
2670 }
2671 };
2672 )cc";
2673 runDataflow(
2674 Code,
2675 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2676 ASTContext &ASTCtx) {
2677 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
2678
2679 auto &FLoc = getFieldLoc<RecordStorageLocation>(
2680 Loc: *Env.getThisPointeeStorageLocation(), Name: "F", ASTCtx);
2681 auto *AVal = cast<PointerValue>(Val: getFieldValue(Loc: &FLoc, Name: "a", ASTCtx, Env));
2682 EXPECT_EQ(AVal, &getValueForDecl<PointerValue>(ASTCtx, Env, "null"));
2683 EXPECT_EQ(getFieldValue(&FLoc, "b", ASTCtx, Env), nullptr);
2684 });
2685}
2686
2687TEST(TransferTest, EmptyInitListExprForStruct) {
2688 std::string Code = R"cc(
2689 class target {
2690 struct {
2691 int *a;
2692 bool *b;
2693 } F;
2694
2695 public:
2696 constexpr target() : F{} {
2697 int *NullIntPtr = nullptr;
2698 bool *NullBoolPtr = nullptr;
2699 // [[p]]
2700 }
2701 };
2702 )cc";
2703 runDataflow(
2704 Code,
2705 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2706 ASTContext &ASTCtx) {
2707 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
2708
2709 auto &FLoc = getFieldLoc<RecordStorageLocation>(
2710 Loc: *Env.getThisPointeeStorageLocation(), Name: "F", ASTCtx);
2711 auto *AVal = cast<PointerValue>(Val: getFieldValue(Loc: &FLoc, Name: "a", ASTCtx, Env));
2712 EXPECT_EQ(AVal,
2713 &getValueForDecl<PointerValue>(ASTCtx, Env, "NullIntPtr"));
2714 auto *BVal = cast<PointerValue>(Val: getFieldValue(Loc: &FLoc, Name: "b", ASTCtx, Env));
2715 EXPECT_EQ(BVal,
2716 &getValueForDecl<PointerValue>(ASTCtx, Env, "NullBoolPtr"));
2717 });
2718}
2719
2720TEST(TransferTest, CopyConstructor) {
2721 std::string Code = R"(
2722 struct A {
2723 int Baz;
2724 };
2725
2726 void target() {
2727 A Foo = { 1 };
2728 A Bar = Foo;
2729 // [[after_copy]]
2730 Foo.Baz = 2;
2731 // [[after_update]]
2732 }
2733 )";
2734 runDataflow(
2735 Code,
2736 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2737 ASTContext &ASTCtx) {
2738 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
2739 ASSERT_THAT(FooDecl, NotNull());
2740
2741 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
2742 ASSERT_THAT(BarDecl, NotNull());
2743
2744 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
2745 ASSERT_THAT(BazDecl, NotNull());
2746
2747 // after_copy
2748 {
2749 const Environment &Env =
2750 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "after_copy");
2751
2752 const auto *FooLoc =
2753 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
2754 const auto *BarLoc =
2755 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *BarDecl));
2756
2757 // The records compare equal.
2758 EXPECT_TRUE(recordsEqual(*FooLoc, *BarLoc, Env));
2759
2760 // In particular, the value of `Baz` in both records is the same.
2761 const auto *FooBazVal =
2762 cast<IntegerValue>(Val: getFieldValue(Loc: FooLoc, Field: *BazDecl, Env));
2763 const auto *BarBazVal =
2764 cast<IntegerValue>(Val: getFieldValue(Loc: BarLoc, Field: *BazDecl, Env));
2765 EXPECT_EQ(FooBazVal, BarBazVal);
2766 }
2767
2768 // after_update
2769 {
2770 const Environment &Env =
2771 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "after_update");
2772
2773 const auto *FooLoc =
2774 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
2775 const auto *BarLoc =
2776 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *BarDecl));
2777
2778 EXPECT_FALSE(recordsEqual(*FooLoc, *BarLoc, Env));
2779
2780 const auto *FooBazVal =
2781 cast<IntegerValue>(Val: getFieldValue(Loc: FooLoc, Field: *BazDecl, Env));
2782 const auto *BarBazVal =
2783 cast<IntegerValue>(Val: getFieldValue(Loc: BarLoc, Field: *BazDecl, Env));
2784 EXPECT_NE(FooBazVal, BarBazVal);
2785 }
2786 });
2787}
2788
2789TEST(TransferTest, CopyConstructorWithDefaultArgument) {
2790 std::string Code = R"(
2791 struct A {
2792 int Baz;
2793 A() = default;
2794 A(const A& a, bool def = true) { Baz = a.Baz; }
2795 };
2796
2797 void target() {
2798 A Foo;
2799 (void)Foo.Baz;
2800 A Bar = Foo;
2801 // [[p]]
2802 }
2803 )";
2804 runDataflow(
2805 Code,
2806 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2807 ASTContext &ASTCtx) {
2808 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
2809 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
2810
2811 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
2812 ASSERT_THAT(FooDecl, NotNull());
2813
2814 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
2815 ASSERT_THAT(BarDecl, NotNull());
2816
2817 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
2818 ASSERT_THAT(BazDecl, NotNull());
2819
2820 const auto *FooLoc =
2821 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
2822 const auto *BarLoc =
2823 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *BarDecl));
2824 EXPECT_TRUE(recordsEqual(*FooLoc, *BarLoc, Env));
2825
2826 const auto *FooBazVal =
2827 cast<IntegerValue>(Val: getFieldValue(Loc: FooLoc, Field: *BazDecl, Env));
2828 const auto *BarBazVal =
2829 cast<IntegerValue>(Val: getFieldValue(Loc: BarLoc, Field: *BazDecl, Env));
2830 EXPECT_EQ(FooBazVal, BarBazVal);
2831 });
2832}
2833
2834TEST(TransferTest, CopyConstructorWithParens) {
2835 std::string Code = R"(
2836 struct A {
2837 int Baz;
2838 };
2839
2840 void target() {
2841 A Foo;
2842 (void)Foo.Baz;
2843 A Bar((A(Foo)));
2844 // [[p]]
2845 }
2846 )";
2847 runDataflow(
2848 Code,
2849 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2850 ASTContext &ASTCtx) {
2851 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
2852 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
2853
2854 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
2855 ASSERT_THAT(FooDecl, NotNull());
2856
2857 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
2858 ASSERT_THAT(BarDecl, NotNull());
2859
2860 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
2861 ASSERT_THAT(BazDecl, NotNull());
2862
2863 const auto *FooLoc =
2864 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
2865 const auto *BarLoc =
2866 cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *BarDecl));
2867 EXPECT_TRUE(recordsEqual(*FooLoc, *BarLoc, Env));
2868
2869 const auto *FooBazVal =
2870 cast<IntegerValue>(Val: getFieldValue(Loc: FooLoc, Field: *BazDecl, Env));
2871 const auto *BarBazVal =
2872 cast<IntegerValue>(Val: getFieldValue(Loc: BarLoc, Field: *BazDecl, Env));
2873 EXPECT_EQ(FooBazVal, BarBazVal);
2874 });
2875}
2876
2877TEST(TransferTest, CopyConstructorWithInitializerListAsSyntacticSugar) {
2878 std::string Code = R"(
2879 struct A {
2880 int Baz;
2881 };
2882 void target() {
2883 A Foo = {3};
2884 (void)Foo.Baz;
2885 A Bar = {A(Foo)};
2886 // [[p]]
2887 }
2888 )";
2889 runDataflow(
2890 Code,
2891 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2892 ASTContext &ASTCtx) {
2893 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
2894
2895 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
2896
2897 const auto &FooLoc =
2898 getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "Foo");
2899 const auto &BarLoc =
2900 getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "Bar");
2901
2902 const auto *FooBazVal =
2903 cast<IntegerValue>(Val: getFieldValue(Loc: &FooLoc, Field: *BazDecl, Env));
2904 const auto *BarBazVal =
2905 cast<IntegerValue>(Val: getFieldValue(Loc: &BarLoc, Field: *BazDecl, Env));
2906 EXPECT_EQ(FooBazVal, BarBazVal);
2907 });
2908}
2909
2910TEST(TransferTest, CopyConstructorArgIsRefReturnedByFunction) {
2911 // This is a crash repro.
2912 std::string Code = R"(
2913 struct S {};
2914 const S &returnsSRef();
2915 void target() {
2916 S s(returnsSRef());
2917 }
2918 )";
2919 runDataflow(
2920 Code,
2921 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2922 ASTContext &ASTCtx) {});
2923}
2924
2925TEST(TransferTest, MoveConstructor) {
2926 std::string Code = R"(
2927 namespace std {
2928
2929 template <typename T> struct remove_reference { using type = T; };
2930 template <typename T> struct remove_reference<T&> { using type = T; };
2931 template <typename T> struct remove_reference<T&&> { using type = T; };
2932
2933 template <typename T>
2934 using remove_reference_t = typename remove_reference<T>::type;
2935
2936 template <typename T>
2937 std::remove_reference_t<T>&& move(T&& x);
2938
2939 } // namespace std
2940
2941 struct A {
2942 int Baz;
2943 };
2944
2945 void target() {
2946 A Foo;
2947 A Bar;
2948 (void)Foo.Baz;
2949 // [[p1]]
2950 Foo = std::move(Bar);
2951 // [[p2]]
2952 }
2953 )";
2954 runDataflow(
2955 Code,
2956 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
2957 ASTContext &ASTCtx) {
2958 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
2959 const Environment &Env1 = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p1");
2960 const Environment &Env2 = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p2");
2961
2962 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
2963 ASSERT_THAT(FooDecl, NotNull());
2964
2965 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
2966 ASSERT_THAT(BarDecl, NotNull());
2967
2968 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
2969 ASSERT_THAT(BazDecl, NotNull());
2970
2971 const auto *FooLoc1 =
2972 cast<RecordStorageLocation>(Val: Env1.getStorageLocation(D: *FooDecl));
2973 const auto *BarLoc1 =
2974 cast<RecordStorageLocation>(Val: Env1.getStorageLocation(D: *BarDecl));
2975
2976 EXPECT_FALSE(recordsEqual(*FooLoc1, *BarLoc1, Env1));
2977
2978 const auto *FooBazVal1 =
2979 cast<IntegerValue>(Val: getFieldValue(Loc: FooLoc1, Field: *BazDecl, Env: Env1));
2980 const auto *BarBazVal1 =
2981 cast<IntegerValue>(Val: getFieldValue(Loc: BarLoc1, Field: *BazDecl, Env: Env1));
2982 EXPECT_NE(FooBazVal1, BarBazVal1);
2983
2984 const auto *FooLoc2 =
2985 cast<RecordStorageLocation>(Val: Env2.getStorageLocation(D: *FooDecl));
2986 EXPECT_TRUE(recordsEqual(*FooLoc2, Env2, *BarLoc1, Env1));
2987
2988 const auto *FooBazVal2 =
2989 cast<IntegerValue>(Val: getFieldValue(Loc: FooLoc1, Field: *BazDecl, Env: Env2));
2990 EXPECT_EQ(FooBazVal2, BarBazVal1);
2991 });
2992}
2993
2994TEST(TransferTest, BindTemporary) {
2995 std::string Code = R"(
2996 struct A {
2997 virtual ~A() = default;
2998
2999 int Baz;
3000 };
3001
3002 void target(A Foo) {
3003 int Bar = A(Foo).Baz;
3004 // [[p]]
3005 }
3006 )";
3007 runDataflow(
3008 Code,
3009 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3010 ASTContext &ASTCtx) {
3011 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
3012 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3013
3014 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
3015 ASSERT_THAT(FooDecl, NotNull());
3016
3017 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
3018 ASSERT_THAT(BarDecl, NotNull());
3019
3020 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
3021 ASSERT_THAT(BazDecl, NotNull());
3022
3023 const auto &FooLoc =
3024 *cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
3025 const auto *BarVal = cast<IntegerValue>(Val: Env.getValue(D: *BarDecl));
3026 EXPECT_EQ(BarVal, getFieldValue(&FooLoc, *BazDecl, Env));
3027 });
3028}
3029
3030TEST(TransferTest, ResultObjectLocation) {
3031 std::string Code = R"(
3032 struct A {
3033 virtual ~A() = default;
3034 };
3035
3036 void target() {
3037 0, A();
3038 (void)0; // [[p]]
3039 }
3040 )";
3041 using ast_matchers::binaryOperator;
3042 using ast_matchers::cxxBindTemporaryExpr;
3043 using ast_matchers::cxxTemporaryObjectExpr;
3044 using ast_matchers::exprWithCleanups;
3045 using ast_matchers::has;
3046 using ast_matchers::hasOperatorName;
3047 using ast_matchers::hasRHS;
3048 using ast_matchers::match;
3049 using ast_matchers::selectFirst;
3050 using ast_matchers::traverse;
3051 runDataflow(
3052 Code,
3053 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3054 ASTContext &ASTCtx) {
3055 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3056
3057 // The expression `0, A()` in the code above produces the following
3058 // structure, consisting of four prvalues of record type.
3059 // `Env.getResultObjectLocation()` should return the same location for
3060 // all of these.
3061 auto MatchResult = match(
3062 Matcher: traverse(TK: TK_AsIs,
3063 InnerMatcher: exprWithCleanups(
3064 has(binaryOperator(
3065 hasOperatorName(Name: ","),
3066 hasRHS(InnerMatcher: cxxBindTemporaryExpr(
3067 has(cxxTemporaryObjectExpr().bind(
3068 ID: "toe")))
3069 .bind(ID: "bte")))
3070 .bind(ID: "comma")))
3071 .bind(ID: "ewc")),
3072 Context&: ASTCtx);
3073 auto *TOE = selectFirst<CXXTemporaryObjectExpr>(BoundTo: "toe", Results: MatchResult);
3074 ASSERT_NE(TOE, nullptr);
3075 auto *Comma = selectFirst<BinaryOperator>(BoundTo: "comma", Results: MatchResult);
3076 ASSERT_NE(Comma, nullptr);
3077 auto *EWC = selectFirst<ExprWithCleanups>(BoundTo: "ewc", Results: MatchResult);
3078 ASSERT_NE(EWC, nullptr);
3079 auto *BTE = selectFirst<CXXBindTemporaryExpr>(BoundTo: "bte", Results: MatchResult);
3080 ASSERT_NE(BTE, nullptr);
3081
3082 RecordStorageLocation &Loc = Env.getResultObjectLocation(*TOE);
3083 EXPECT_EQ(&Loc, &Env.getResultObjectLocation(*Comma));
3084 EXPECT_EQ(&Loc, &Env.getResultObjectLocation(*EWC));
3085 EXPECT_EQ(&Loc, &Env.getResultObjectLocation(*BTE));
3086 });
3087}
3088
3089TEST(TransferTest, ResultObjectLocationForDefaultArgExpr) {
3090 std::string Code = R"(
3091 struct Inner {};
3092 struct Outer {
3093 Inner I = {};
3094 };
3095
3096 void funcWithDefaultArg(Outer O = {});
3097 void target() {
3098 funcWithDefaultArg();
3099 // [[p]]
3100 }
3101 )";
3102
3103 using ast_matchers::cxxDefaultArgExpr;
3104 using ast_matchers::match;
3105 using ast_matchers::selectFirst;
3106 runDataflow(
3107 Code,
3108 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3109 ASTContext &ASTCtx) {
3110 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3111
3112 auto *DefaultArg = selectFirst<CXXDefaultArgExpr>(
3113 BoundTo: "default_arg",
3114 Results: match(Matcher: cxxDefaultArgExpr().bind(ID: "default_arg"), Context&: ASTCtx));
3115 ASSERT_NE(DefaultArg, nullptr);
3116
3117 // The values for default arguments aren't modeled; we merely verify
3118 // that we can get a result object location for a default arg.
3119 Env.getResultObjectLocation(*DefaultArg);
3120 });
3121}
3122
3123TEST(TransferTest, ResultObjectLocationForDefaultInitExpr) {
3124 std::string Code = R"(
3125 struct S {};
3126 struct target {
3127 target () {
3128 (void)0;
3129 // [[p]]
3130 }
3131 S s = {};
3132 };
3133 )";
3134
3135 using ast_matchers::cxxCtorInitializer;
3136 using ast_matchers::match;
3137 using ast_matchers::selectFirst;
3138 runDataflow(
3139 Code,
3140 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3141 ASTContext &ASTCtx) {
3142 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3143
3144 const ValueDecl *SField = findValueDecl(ASTCtx, Name: "s");
3145
3146 auto *CtorInit = selectFirst<CXXCtorInitializer>(
3147 BoundTo: "ctor_initializer",
3148 Results: match(Matcher: cxxCtorInitializer().bind(ID: "ctor_initializer"), Context&: ASTCtx));
3149 ASSERT_NE(CtorInit, nullptr);
3150
3151 auto *DefaultInit = cast<CXXDefaultInitExpr>(Val: CtorInit->getInit());
3152
3153 RecordStorageLocation &Loc = Env.getResultObjectLocation(*DefaultInit);
3154
3155 EXPECT_EQ(&Loc, Env.getThisPointeeStorageLocation()->getChild(*SField));
3156 });
3157}
3158
3159// This test ensures that CXXOperatorCallExpr returning prvalues are correctly
3160// handled by the transfer functions, especially that `getResultObjectLocation`
3161// correctly returns a storage location for those.
3162TEST(TransferTest, ResultObjectLocationForCXXOperatorCallExpr) {
3163 std::string Code = R"(
3164 struct A {
3165 A operator+(int);
3166 };
3167
3168 void target() {
3169 A a;
3170 a + 3;
3171 (void)0; // [[p]]
3172 }
3173 )";
3174 using ast_matchers::cxxOperatorCallExpr;
3175 using ast_matchers::match;
3176 using ast_matchers::selectFirst;
3177 using ast_matchers::traverse;
3178 runDataflow(
3179 Code,
3180 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3181 ASTContext &ASTCtx) {
3182 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3183
3184 auto *CallExpr = selectFirst<CXXOperatorCallExpr>(
3185 BoundTo: "call_expr",
3186 Results: match(Matcher: cxxOperatorCallExpr().bind(ID: "call_expr"), Context&: ASTCtx));
3187
3188 EXPECT_NE(&Env.getResultObjectLocation(*CallExpr), nullptr);
3189 });
3190}
3191
3192TEST(TransferTest, ResultObjectLocationForInitListExpr) {
3193 std::string Code = R"cc(
3194 struct Inner {};
3195
3196 struct Outer { Inner I; };
3197
3198 void target() {
3199 Outer O = { Inner() };
3200 // [[p]]
3201 }
3202 )cc";
3203 using ast_matchers::asString;
3204 using ast_matchers::cxxConstructExpr;
3205 using ast_matchers::hasType;
3206 using ast_matchers::match;
3207 using ast_matchers::selectFirst;
3208 using ast_matchers::traverse;
3209 runDataflow(
3210 Code,
3211 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3212 ASTContext &ASTCtx) {
3213 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3214
3215 auto *Construct = selectFirst<CXXConstructExpr>(
3216 BoundTo: "construct",
3217 Results: match(
3218 Matcher: cxxConstructExpr(hasType(InnerMatcher: asString(Name: "Inner"))).bind(ID: "construct"),
3219 Context&: ASTCtx));
3220
3221 EXPECT_EQ(
3222 &Env.getResultObjectLocation(*Construct),
3223 &getFieldLoc(getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "O"),
3224 "I", ASTCtx));
3225 });
3226}
3227
3228TEST(TransferTest, ResultObjectLocationForParenInitListExpr) {
3229 std::string Code = R"cc(
3230 struct Inner {};
3231
3232 struct Outer { Inner I; };
3233
3234 void target() {
3235 Outer O((Inner()));
3236 // [[p]]
3237 }
3238 )cc";
3239 using ast_matchers::asString;
3240 using ast_matchers::cxxConstructExpr;
3241 using ast_matchers::hasType;
3242 using ast_matchers::match;
3243 using ast_matchers::selectFirst;
3244 using ast_matchers::traverse;
3245 runDataflow(
3246 Code,
3247 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3248 ASTContext &ASTCtx) {
3249 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3250
3251 auto *Construct = selectFirst<CXXConstructExpr>(
3252 BoundTo: "construct",
3253 Results: match(
3254 Matcher: cxxConstructExpr(hasType(InnerMatcher: asString(Name: "Inner"))).bind(ID: "construct"),
3255 Context&: ASTCtx));
3256
3257 EXPECT_EQ(
3258 &Env.getResultObjectLocation(*Construct),
3259 &getFieldLoc(getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "O"),
3260 "I", ASTCtx));
3261 },
3262 Std: LangStandard::lang_cxx20);
3263}
3264
3265// Check that the `std::strong_ordering` object returned by builtin `<=>` has a
3266// correctly modeled result object location.
3267TEST(TransferTest, ResultObjectLocationForBuiltinSpaceshipOperator) {
3268 std::string Code = R"(
3269 namespace std {
3270 // This is the minimal definition required to get
3271 // `Sema::CheckComparisonCategoryType()` to accept this fake.
3272 struct strong_ordering {
3273 enum class ordering { less, equal, greater };
3274 ordering o;
3275 static const strong_ordering less;
3276 static const strong_ordering equivalent;
3277 static const strong_ordering equal;
3278 static const strong_ordering greater;
3279 };
3280
3281 inline constexpr strong_ordering strong_ordering::less =
3282 { strong_ordering::ordering::less };
3283 inline constexpr strong_ordering strong_ordering::equal =
3284 { strong_ordering::ordering::equal };
3285 inline constexpr strong_ordering strong_ordering::equivalent =
3286 { strong_ordering::ordering::equal };
3287 inline constexpr strong_ordering strong_ordering::greater =
3288 { strong_ordering::ordering::greater };
3289 }
3290 void target(int i, int j) {
3291 auto ordering = i <=> j;
3292 // [[p]]
3293 }
3294 )";
3295 using ast_matchers::binaryOperator;
3296 using ast_matchers::hasOperatorName;
3297 using ast_matchers::match;
3298 using ast_matchers::selectFirst;
3299 using ast_matchers::traverse;
3300 runDataflow(
3301 Code,
3302 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3303 ASTContext &ASTCtx) {
3304 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3305
3306 auto *Spaceship = selectFirst<BinaryOperator>(
3307 BoundTo: "op",
3308 Results: match(Matcher: binaryOperator(hasOperatorName(Name: "<=>")).bind(ID: "op"), Context&: ASTCtx));
3309
3310 EXPECT_EQ(
3311 &Env.getResultObjectLocation(*Spaceship),
3312 &getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "ordering"));
3313 },
3314 Std: LangStandard::lang_cxx20);
3315}
3316
3317TEST(TransferTest, ResultObjectLocationForStdInitializerListExpr) {
3318 std::string Code = R"(
3319 namespace std {
3320 template <typename T>
3321 struct initializer_list { const T *a, *b; };
3322 } // namespace std
3323
3324 void target() {
3325 std::initializer_list<int> list = {1};
3326 // [[p]]
3327 }
3328 )";
3329
3330 using ast_matchers::cxxStdInitializerListExpr;
3331 using ast_matchers::match;
3332 using ast_matchers::selectFirst;
3333 runDataflow(
3334 Code,
3335 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3336 ASTContext &ASTCtx) {
3337 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3338
3339 auto *StdInitList = selectFirst<CXXStdInitializerListExpr>(
3340 BoundTo: "std_init_list",
3341 Results: match(Matcher: cxxStdInitializerListExpr().bind(ID: "std_init_list"), Context&: ASTCtx));
3342 ASSERT_NE(StdInitList, nullptr);
3343
3344 EXPECT_EQ(&Env.getResultObjectLocation(*StdInitList),
3345 &getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "list"));
3346 });
3347}
3348
3349TEST(TransferTest, ResultObjectLocationForStmtExpr) {
3350 std::string Code = R"(
3351 struct S {};
3352 void target() {
3353 S s = ({ S(); });
3354 // [[p]]
3355 }
3356 )";
3357 using ast_matchers::cxxConstructExpr;
3358 using ast_matchers::match;
3359 using ast_matchers::selectFirst;
3360 using ast_matchers::traverse;
3361 runDataflow(
3362 Code,
3363 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3364 ASTContext &ASTCtx) {
3365 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3366
3367 auto *Construct = selectFirst<CXXConstructExpr>(
3368 BoundTo: "construct", Results: match(Matcher: cxxConstructExpr().bind(ID: "construct"), Context&: ASTCtx));
3369
3370 EXPECT_EQ(&Env.getResultObjectLocation(*Construct),
3371 &getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s"));
3372 });
3373}
3374
3375TEST(TransferTest, ResultObjectLocationForBuiltinBitCastExpr) {
3376 std::string Code = R"(
3377 struct S { int i; };
3378 void target(int i) {
3379 S s = __builtin_bit_cast(S, i);
3380 // [[p]]
3381 }
3382 )";
3383 using ast_matchers::explicitCastExpr;
3384 using ast_matchers::match;
3385 using ast_matchers::selectFirst;
3386 using ast_matchers::traverse;
3387 runDataflow(
3388 Code,
3389 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3390 ASTContext &ASTCtx) {
3391 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3392
3393 auto *BuiltinBitCast = selectFirst<BuiltinBitCastExpr>(
3394 BoundTo: "cast", Results: match(Matcher: explicitCastExpr().bind(ID: "cast"), Context&: ASTCtx));
3395
3396 EXPECT_EQ(&Env.getResultObjectLocation(*BuiltinBitCast),
3397 &getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s"));
3398 });
3399}
3400
3401TEST(TransferTest, ResultObjectLocationForAtomicExpr) {
3402 std::string Code = R"(
3403 struct S {};
3404 void target(_Atomic(S) *ptr) {
3405 S s = __c11_atomic_load(ptr, __ATOMIC_SEQ_CST);
3406 // [[p]]
3407 }
3408 )";
3409 using ast_matchers::atomicExpr;
3410 using ast_matchers::match;
3411 using ast_matchers::selectFirst;
3412 using ast_matchers::traverse;
3413 runDataflow(
3414 Code,
3415 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3416 ASTContext &ASTCtx) {
3417 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3418
3419 auto *Atomic = selectFirst<AtomicExpr>(
3420 BoundTo: "atomic", Results: match(Matcher: atomicExpr().bind(ID: "atomic"), Context&: ASTCtx));
3421
3422 EXPECT_EQ(&Env.getResultObjectLocation(*Atomic),
3423 &getLocForDecl<RecordStorageLocation>(ASTCtx, Env, "s"));
3424 });
3425}
3426
3427TEST(TransferTest, ResultObjectLocationPropagatesThroughConditionalOperator) {
3428 std::string Code = R"(
3429 struct A {
3430 A(int);
3431 };
3432
3433 void target(bool b) {
3434 A a = b ? A(0) : A(1);
3435 (void)0; // [[p]]
3436 }
3437 )";
3438 using ast_matchers::cxxConstructExpr;
3439 using ast_matchers::equals;
3440 using ast_matchers::hasArgument;
3441 using ast_matchers::integerLiteral;
3442 using ast_matchers::match;
3443 using ast_matchers::selectFirst;
3444 using ast_matchers::traverse;
3445 runDataflow(
3446 Code,
3447 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3448 ASTContext &ASTCtx) {
3449 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3450
3451 auto *ConstructExpr0 = selectFirst<CXXConstructExpr>(
3452 BoundTo: "construct",
3453 Results: match(Matcher: cxxConstructExpr(hasArgument(N: 0, InnerMatcher: integerLiteral(equals(Value: 0))))
3454 .bind(ID: "construct"),
3455 Context&: ASTCtx));
3456 auto *ConstructExpr1 = selectFirst<CXXConstructExpr>(
3457 BoundTo: "construct",
3458 Results: match(Matcher: cxxConstructExpr(hasArgument(N: 0, InnerMatcher: integerLiteral(equals(Value: 1))))
3459 .bind(ID: "construct"),
3460 Context&: ASTCtx));
3461
3462 auto &ALoc = getLocForDecl<StorageLocation>(ASTCtx, Env, Name: "a");
3463 EXPECT_EQ(&Env.getResultObjectLocation(*ConstructExpr0), &ALoc);
3464 EXPECT_EQ(&Env.getResultObjectLocation(*ConstructExpr1), &ALoc);
3465 });
3466}
3467
3468TEST(TransferTest, ResultObjectLocationDontVisitNestedRecordDecl) {
3469 // This is a crash repro.
3470 // We used to crash because when propagating result objects, we would visit
3471 // nested record and function declarations, but we don't model fields used
3472 // only in these.
3473 std::string Code = R"(
3474 struct S1 {};
3475 struct S2 { S1 s1; };
3476 void target() {
3477 struct Nested {
3478 void f() {
3479 S2 s2 = { S1() };
3480 }
3481 };
3482 }
3483 )";
3484 runDataflow(
3485 Code,
3486 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3487 ASTContext &ASTCtx) {});
3488}
3489
3490TEST(TransferTest, ResultObjectLocationDontVisitUnevaluatedContexts) {
3491 // This is a crash repro.
3492 // We used to crash because when propagating result objects, we would visit
3493 // unevaluated contexts, but we don't model fields used only in these.
3494
3495 auto testFunction = [](llvm::StringRef Code, llvm::StringRef TargetFun) {
3496 runDataflow(
3497 Code,
3498 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3499 ASTContext &ASTCtx) {},
3500 Std: LangStandard::lang_gnucxx17,
3501 /* ApplyBuiltinTransfer= */ true, TargetFun);
3502 };
3503
3504 std::string Code = R"cc(
3505 // Definitions needed for `typeid`.
3506 namespace std {
3507 class type_info {};
3508 class bad_typeid {};
3509 } // namespace std
3510
3511 struct S1 {};
3512 struct S2 { S1 s1; };
3513
3514 // We test each type of unevaluated context from a different target
3515 // function. Some types of unevaluated contexts may actually cause the
3516 // field `s1` to be modeled, and we don't want this to "pollute" the tests
3517 // for the other unevaluated contexts.
3518 void decltypeTarget() {
3519 decltype(S2{}) Dummy;
3520 }
3521 void typeofTarget() {
3522 typeof(S2{}) Dummy;
3523 }
3524 void typeidTarget() {
3525#if __has_feature(cxx_rtti)
3526 typeid(S2{});
3527#endif
3528 }
3529 void sizeofTarget() {
3530 sizeof(S2{});
3531 }
3532 void noexceptTarget() {
3533 noexcept(S2{});
3534 }
3535 )cc";
3536
3537 testFunction(Code, "decltypeTarget");
3538 testFunction(Code, "typeofTarget");
3539 testFunction(Code, "typeidTarget");
3540 testFunction(Code, "sizeofTarget");
3541 testFunction(Code, "noexceptTarget");
3542}
3543
3544TEST(TransferTest, StaticCast) {
3545 std::string Code = R"(
3546 void target(int Foo) {
3547 int Bar = static_cast<int>(Foo);
3548 // [[p]]
3549 }
3550 )";
3551 runDataflow(
3552 Code,
3553 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3554 ASTContext &ASTCtx) {
3555 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
3556 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3557
3558 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
3559 ASSERT_THAT(FooDecl, NotNull());
3560
3561 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
3562 ASSERT_THAT(BarDecl, NotNull());
3563
3564 const auto *FooVal = Env.getValue(D: *FooDecl);
3565 const auto *BarVal = Env.getValue(D: *BarDecl);
3566 EXPECT_TRUE(isa<IntegerValue>(FooVal));
3567 EXPECT_TRUE(isa<IntegerValue>(BarVal));
3568 EXPECT_EQ(FooVal, BarVal);
3569 });
3570}
3571
3572TEST(TransferTest, IntegralCast) {
3573 std::string Code = R"(
3574 void target(int Foo) {
3575 long Bar = Foo;
3576 // [[p]]
3577 }
3578 )";
3579 runDataflow(
3580 Code,
3581 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3582 ASTContext &ASTCtx) {
3583 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3584
3585 const auto &FooVal = getValueForDecl<IntegerValue>(ASTCtx, Env, Name: "Foo");
3586 const auto &BarVal = getValueForDecl<IntegerValue>(ASTCtx, Env, Name: "Bar");
3587 EXPECT_EQ(&FooVal, &BarVal);
3588 });
3589}
3590
3591TEST(TransferTest, IntegraltoBooleanCast) {
3592 std::string Code = R"(
3593 void target(int Foo) {
3594 bool Bar = Foo;
3595 // [[p]]
3596 }
3597 )";
3598 runDataflow(
3599 Code,
3600 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3601 ASTContext &ASTCtx) {
3602 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3603
3604 const auto &FooVal = getValueForDecl(ASTCtx, Env, Name: "Foo");
3605 const auto &BarVal = getValueForDecl(ASTCtx, Env, Name: "Bar");
3606 EXPECT_TRUE(isa<IntegerValue>(FooVal));
3607 EXPECT_TRUE(isa<BoolValue>(BarVal));
3608 });
3609}
3610
3611TEST(TransferTest, IntegralToBooleanCastFromBool) {
3612 std::string Code = R"(
3613 void target(bool Foo) {
3614 int Zab = Foo;
3615 bool Bar = Zab;
3616 // [[p]]
3617 }
3618 )";
3619 runDataflow(
3620 Code,
3621 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3622 ASTContext &ASTCtx) {
3623 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3624
3625 const auto &FooVal = getValueForDecl<BoolValue>(ASTCtx, Env, Name: "Foo");
3626 const auto &BarVal = getValueForDecl<BoolValue>(ASTCtx, Env, Name: "Bar");
3627 EXPECT_EQ(&FooVal, &BarVal);
3628 });
3629}
3630
3631TEST(TransferTest, WidenBoolValueInIntegerVariable) {
3632 // This is a crash repro.
3633 // This test sets up a case where we perform widening on an integer variable
3634 // that contains a `BoolValue` for the previous iteration and an
3635 // `IntegerValue` for the current iteration. We used to crash on this because
3636 // `widenDistinctValues()` assumed that if the previous iteration had a
3637 // `BoolValue`, the current iteration would too.
3638 // FIXME: The real fix here is to make sure we never store `BoolValue`s in
3639 // integer variables; see also the comment in `widenDistinctValues()`.
3640 std::string Code = R"cc(
3641 struct S {
3642 int i;
3643 S *next;
3644 };
3645 void target(S *s) {
3646 for (; s; s = s->next)
3647 s->i = false;
3648 }
3649 )cc";
3650 runDataflow(Code,
3651 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
3652 ASTContext &) {});
3653}
3654
3655TEST(TransferTest, NullToPointerCast) {
3656 std::string Code = R"(
3657 using my_nullptr_t = decltype(nullptr);
3658 struct Baz {};
3659 void target() {
3660 int *FooX = nullptr;
3661 int *FooY = nullptr;
3662 bool **Bar = nullptr;
3663 Baz *Baz = nullptr;
3664 my_nullptr_t Null = 0;
3665 // [[p]]
3666 }
3667 )";
3668 runDataflow(
3669 Code,
3670 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3671 ASTContext &ASTCtx) {
3672 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
3673 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3674
3675 const ValueDecl *FooXDecl = findValueDecl(ASTCtx, Name: "FooX");
3676 ASSERT_THAT(FooXDecl, NotNull());
3677
3678 const ValueDecl *FooYDecl = findValueDecl(ASTCtx, Name: "FooY");
3679 ASSERT_THAT(FooYDecl, NotNull());
3680
3681 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
3682 ASSERT_THAT(BarDecl, NotNull());
3683
3684 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
3685 ASSERT_THAT(BazDecl, NotNull());
3686
3687 const ValueDecl *NullDecl = findValueDecl(ASTCtx, Name: "Null");
3688 ASSERT_THAT(NullDecl, NotNull());
3689
3690 const auto *FooXVal = cast<PointerValue>(Val: Env.getValue(D: *FooXDecl));
3691 const auto *FooYVal = cast<PointerValue>(Val: Env.getValue(D: *FooYDecl));
3692 const auto *BarVal = cast<PointerValue>(Val: Env.getValue(D: *BarDecl));
3693 const auto *BazVal = cast<PointerValue>(Val: Env.getValue(D: *BazDecl));
3694 const auto *NullVal = cast<PointerValue>(Val: Env.getValue(D: *NullDecl));
3695
3696 EXPECT_EQ(FooXVal, FooYVal);
3697 EXPECT_NE(FooXVal, BarVal);
3698 EXPECT_NE(FooXVal, BazVal);
3699 EXPECT_NE(BarVal, BazVal);
3700
3701 const StorageLocation &FooPointeeLoc = FooXVal->getPointeeLoc();
3702 EXPECT_TRUE(isa<ScalarStorageLocation>(FooPointeeLoc));
3703 EXPECT_THAT(Env.getValue(FooPointeeLoc), IsNull());
3704
3705 const StorageLocation &BarPointeeLoc = BarVal->getPointeeLoc();
3706 EXPECT_TRUE(isa<ScalarStorageLocation>(BarPointeeLoc));
3707 EXPECT_THAT(Env.getValue(BarPointeeLoc), IsNull());
3708
3709 const StorageLocation &BazPointeeLoc = BazVal->getPointeeLoc();
3710 EXPECT_TRUE(isa<RecordStorageLocation>(BazPointeeLoc));
3711 EXPECT_EQ(BazVal, &Env.fork().getOrCreateNullPointerValue(
3712 BazPointeeLoc.getType()));
3713
3714 const StorageLocation &NullPointeeLoc = NullVal->getPointeeLoc();
3715 EXPECT_TRUE(isa<ScalarStorageLocation>(NullPointeeLoc));
3716 EXPECT_THAT(Env.getValue(NullPointeeLoc), IsNull());
3717 });
3718}
3719
3720TEST(TransferTest, PointerToMemberVariable) {
3721 std::string Code = R"(
3722 struct S {
3723 int i;
3724 };
3725 void target() {
3726 int S::*MemberPointer = &S::i;
3727 // [[p]]
3728 }
3729 )";
3730 runDataflow(
3731 Code,
3732 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3733 ASTContext &ASTCtx) {
3734 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3735
3736 const ValueDecl *MemberPointerDecl =
3737 findValueDecl(ASTCtx, Name: "MemberPointer");
3738 ASSERT_THAT(MemberPointerDecl, NotNull());
3739 ASSERT_THAT(Env.getValue(*MemberPointerDecl), IsNull());
3740 });
3741}
3742
3743TEST(TransferTest, PointerToMemberFunction) {
3744 std::string Code = R"(
3745 struct S {
3746 void Method();
3747 };
3748 void target() {
3749 void (S::*MemberPointer)() = &S::Method;
3750 // [[p]]
3751 }
3752 )";
3753 runDataflow(
3754 Code,
3755 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3756 ASTContext &ASTCtx) {
3757 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3758
3759 const ValueDecl *MemberPointerDecl =
3760 findValueDecl(ASTCtx, Name: "MemberPointer");
3761 ASSERT_THAT(MemberPointerDecl, NotNull());
3762 ASSERT_THAT(Env.getValue(*MemberPointerDecl), IsNull());
3763 });
3764}
3765
3766TEST(TransferTest, NullToMemberPointerCast) {
3767 std::string Code = R"(
3768 struct Foo {};
3769 void target() {
3770 int Foo::*MemberPointer = nullptr;
3771 // [[p]]
3772 }
3773 )";
3774 runDataflow(
3775 Code,
3776 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3777 ASTContext &ASTCtx) {
3778 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
3779 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3780
3781 const ValueDecl *MemberPointerDecl =
3782 findValueDecl(ASTCtx, Name: "MemberPointer");
3783 ASSERT_THAT(MemberPointerDecl, NotNull());
3784 ASSERT_THAT(Env.getValue(*MemberPointerDecl), IsNull());
3785 });
3786}
3787
3788TEST(TransferTest, AddrOfValue) {
3789 std::string Code = R"(
3790 void target() {
3791 int Foo;
3792 int *Bar = &Foo;
3793 // [[p]]
3794 }
3795 )";
3796 runDataflow(
3797 Code,
3798 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3799 ASTContext &ASTCtx) {
3800 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
3801 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3802
3803 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
3804 ASSERT_THAT(FooDecl, NotNull());
3805
3806 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
3807 ASSERT_THAT(BarDecl, NotNull());
3808
3809 const auto *FooLoc =
3810 cast<ScalarStorageLocation>(Val: Env.getStorageLocation(D: *FooDecl));
3811 const auto *BarVal = cast<PointerValue>(Val: Env.getValue(D: *BarDecl));
3812 EXPECT_EQ(&BarVal->getPointeeLoc(), FooLoc);
3813 });
3814}
3815
3816TEST(TransferTest, AddrOfReference) {
3817 std::string Code = R"(
3818 void target(int *Foo) {
3819 int *Bar = &(*Foo);
3820 // [[p]]
3821 }
3822 )";
3823 runDataflow(
3824 Code,
3825 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3826 ASTContext &ASTCtx) {
3827 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
3828 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3829
3830 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
3831 ASSERT_THAT(FooDecl, NotNull());
3832
3833 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
3834 ASSERT_THAT(BarDecl, NotNull());
3835
3836 const auto *FooVal = cast<PointerValue>(Val: Env.getValue(D: *FooDecl));
3837 const auto *BarVal = cast<PointerValue>(Val: Env.getValue(D: *BarDecl));
3838 EXPECT_EQ(&BarVal->getPointeeLoc(), &FooVal->getPointeeLoc());
3839 });
3840}
3841
3842TEST(TransferTest, Preincrement) {
3843 std::string Code = R"(
3844 void target(int I) {
3845 (void)0; // [[before]]
3846 int &IRef = ++I;
3847 // [[after]]
3848 }
3849 )";
3850 runDataflow(
3851 Code,
3852 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3853 ASTContext &ASTCtx) {
3854 const Environment &EnvBefore =
3855 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "before");
3856 const Environment &EnvAfter =
3857 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "after");
3858
3859 EXPECT_EQ(&getLocForDecl(ASTCtx, EnvAfter, "IRef"),
3860 &getLocForDecl(ASTCtx, EnvBefore, "I"));
3861
3862 const ValueDecl *IDecl = findValueDecl(ASTCtx, Name: "I");
3863 EXPECT_NE(EnvBefore.getValue(*IDecl), nullptr);
3864 EXPECT_EQ(EnvAfter.getValue(*IDecl), nullptr);
3865 });
3866}
3867
3868TEST(TransferTest, Postincrement) {
3869 std::string Code = R"(
3870 void target(int I) {
3871 (void)0; // [[before]]
3872 int OldVal = I++;
3873 // [[after]]
3874 }
3875 )";
3876 runDataflow(
3877 Code,
3878 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3879 ASTContext &ASTCtx) {
3880 const Environment &EnvBefore =
3881 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "before");
3882 const Environment &EnvAfter =
3883 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "after");
3884
3885 EXPECT_EQ(&getValueForDecl(ASTCtx, EnvBefore, "I"),
3886 &getValueForDecl(ASTCtx, EnvAfter, "OldVal"));
3887
3888 const ValueDecl *IDecl = findValueDecl(ASTCtx, Name: "I");
3889 EXPECT_EQ(EnvAfter.getValue(*IDecl), nullptr);
3890 });
3891}
3892
3893// We test just one of the compound assignment operators because we know the
3894// code for propagating the storage location is shared among all of them.
3895TEST(TransferTest, AddAssign) {
3896 std::string Code = R"(
3897 void target(int I) {
3898 int &IRef = (I += 1);
3899 // [[p]]
3900 }
3901 )";
3902 runDataflow(
3903 Code,
3904 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3905 ASTContext &ASTCtx) {
3906 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3907
3908 EXPECT_EQ(&getLocForDecl(ASTCtx, Env, "IRef"),
3909 &getLocForDecl(ASTCtx, Env, "I"));
3910 });
3911}
3912
3913TEST(TransferTest, CannotAnalyzeFunctionTemplate) {
3914 std::string Code = R"(
3915 template <typename T>
3916 void target() {}
3917 )";
3918 ASSERT_THAT_ERROR(
3919 checkDataflowWithNoopAnalysis(Code),
3920 llvm::FailedWithMessage("Cannot analyze templated declarations"));
3921}
3922
3923TEST(TransferTest, CannotAnalyzeMethodOfClassTemplate) {
3924 std::string Code = R"(
3925 template <typename T>
3926 struct A {
3927 void target() {}
3928 };
3929 )";
3930 ASSERT_THAT_ERROR(
3931 checkDataflowWithNoopAnalysis(Code),
3932 llvm::FailedWithMessage("Cannot analyze templated declarations"));
3933}
3934
3935TEST(TransferTest, VarDeclInitAssignConditionalOperator) {
3936 std::string Code = R"(
3937 struct A {
3938 int i;
3939 };
3940
3941 void target(A Foo, A Bar, bool Cond) {
3942 A Baz = Cond ? A(Foo) : A(Bar);
3943 // Make sure A::i is modeled.
3944 Baz.i;
3945 /*[[p]]*/
3946 }
3947 )";
3948 runDataflow(
3949 Code,
3950 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3951 ASTContext &ASTCtx) {
3952 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
3953
3954 auto *FooIVal = cast<IntegerValue>(Val: getFieldValue(
3955 Loc: &getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "Foo"), Name: "i",
3956 ASTCtx, Env));
3957 auto *BarIVal = cast<IntegerValue>(Val: getFieldValue(
3958 Loc: &getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "Bar"), Name: "i",
3959 ASTCtx, Env));
3960 auto *BazIVal = cast<IntegerValue>(Val: getFieldValue(
3961 Loc: &getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "Baz"), Name: "i",
3962 ASTCtx, Env));
3963
3964 EXPECT_NE(BazIVal, FooIVal);
3965 EXPECT_NE(BazIVal, BarIVal);
3966 });
3967}
3968
3969TEST(TransferTest, VarDeclInDoWhile) {
3970 std::string Code = R"(
3971 void target(int *Foo) {
3972 do {
3973 int Bar = *Foo;
3974 // [[in_loop]]
3975 } while (false);
3976 (void)0;
3977 // [[after_loop]]
3978 }
3979 )";
3980 runDataflow(
3981 Code,
3982 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
3983 ASTContext &ASTCtx) {
3984 const Environment &EnvInLoop =
3985 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "in_loop");
3986 const Environment &EnvAfterLoop =
3987 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "after_loop");
3988
3989 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
3990 ASSERT_THAT(FooDecl, NotNull());
3991
3992 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
3993 ASSERT_THAT(BarDecl, NotNull());
3994
3995 const auto *FooVal =
3996 cast<PointerValue>(Val: EnvAfterLoop.getValue(D: *FooDecl));
3997 const auto *FooPointeeVal =
3998 cast<IntegerValue>(Val: EnvAfterLoop.getValue(Loc: FooVal->getPointeeLoc()));
3999
4000 const auto *BarVal = cast<IntegerValue>(Val: EnvInLoop.getValue(D: *BarDecl));
4001 EXPECT_EQ(BarVal, FooPointeeVal);
4002
4003 ASSERT_THAT(EnvAfterLoop.getValue(*BarDecl), IsNull());
4004 });
4005}
4006
4007TEST(TransferTest, UnreachableAfterWhileTrue) {
4008 std::string Code = R"(
4009 void target() {
4010 while (true) {}
4011 (void)0;
4012 /*[[p]]*/
4013 }
4014 )";
4015 runDataflow(
4016 Code,
4017 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4018 ASTContext &ASTCtx) {
4019 // The node after the while-true is pruned because it is trivially
4020 // known to be unreachable.
4021 ASSERT_TRUE(Results.empty());
4022 });
4023}
4024
4025TEST(TransferTest, AggregateInitialization) {
4026 std::string BracesCode = R"(
4027 struct A {
4028 int Foo;
4029 };
4030
4031 struct B {
4032 int Bar;
4033 A Baz;
4034 int Qux;
4035 };
4036
4037 void target(int BarArg, int FooArg, int QuxArg) {
4038 B Quux{BarArg, {FooArg}, QuxArg};
4039 B OtherB;
4040 /*[[p]]*/
4041 }
4042 )";
4043 std::string BraceElisionCode = R"(
4044 struct A {
4045 int Foo;
4046 };
4047
4048 struct B {
4049 int Bar;
4050 A Baz;
4051 int Qux;
4052 };
4053
4054 void target(int BarArg, int FooArg, int QuxArg) {
4055 B Quux = {BarArg, FooArg, QuxArg};
4056 B OtherB;
4057 /*[[p]]*/
4058 }
4059 )";
4060 for (const std::string &Code : {BracesCode, BraceElisionCode}) {
4061 runDataflow(
4062 Code,
4063 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4064 ASTContext &ASTCtx) {
4065 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4066 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4067
4068 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
4069 ASSERT_THAT(FooDecl, NotNull());
4070
4071 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4072 ASSERT_THAT(BarDecl, NotNull());
4073
4074 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
4075 ASSERT_THAT(BazDecl, NotNull());
4076
4077 const ValueDecl *QuxDecl = findValueDecl(ASTCtx, Name: "Qux");
4078 ASSERT_THAT(QuxDecl, NotNull());
4079
4080 const ValueDecl *FooArgDecl = findValueDecl(ASTCtx, Name: "FooArg");
4081 ASSERT_THAT(FooArgDecl, NotNull());
4082
4083 const ValueDecl *BarArgDecl = findValueDecl(ASTCtx, Name: "BarArg");
4084 ASSERT_THAT(BarArgDecl, NotNull());
4085
4086 const ValueDecl *QuxArgDecl = findValueDecl(ASTCtx, Name: "QuxArg");
4087 ASSERT_THAT(QuxArgDecl, NotNull());
4088
4089 const ValueDecl *QuuxDecl = findValueDecl(ASTCtx, Name: "Quux");
4090 ASSERT_THAT(QuuxDecl, NotNull());
4091
4092 const auto *FooArgVal = cast<IntegerValue>(Val: Env.getValue(D: *FooArgDecl));
4093 const auto *BarArgVal = cast<IntegerValue>(Val: Env.getValue(D: *BarArgDecl));
4094 const auto *QuxArgVal = cast<IntegerValue>(Val: Env.getValue(D: *QuxArgDecl));
4095
4096 const auto &QuuxLoc =
4097 *cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *QuuxDecl));
4098 const auto &BazLoc =
4099 *cast<RecordStorageLocation>(Val: QuuxLoc.getChild(D: *BazDecl));
4100
4101 EXPECT_EQ(getFieldValue(&QuuxLoc, *BarDecl, Env), BarArgVal);
4102 EXPECT_EQ(getFieldValue(&BazLoc, *FooDecl, Env), FooArgVal);
4103 EXPECT_EQ(getFieldValue(&QuuxLoc, *QuxDecl, Env), QuxArgVal);
4104
4105 // Check that fields initialized in an initializer list are always
4106 // modeled in other instances of the same type.
4107 const auto &OtherBLoc =
4108 getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "OtherB");
4109 EXPECT_THAT(OtherBLoc.getChild(*BarDecl), NotNull());
4110 EXPECT_THAT(OtherBLoc.getChild(*BazDecl), NotNull());
4111 EXPECT_THAT(OtherBLoc.getChild(*QuxDecl), NotNull());
4112 });
4113 }
4114}
4115
4116TEST(TransferTest, AggregateInitializationReferenceField) {
4117 std::string Code = R"(
4118 struct S {
4119 int &RefField;
4120 };
4121
4122 void target(int i) {
4123 S s = { i };
4124 /*[[p]]*/
4125 }
4126 )";
4127 runDataflow(
4128 Code,
4129 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4130 ASTContext &ASTCtx) {
4131 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4132
4133 const ValueDecl *RefFieldDecl = findValueDecl(ASTCtx, Name: "RefField");
4134
4135 auto &ILoc = getLocForDecl<StorageLocation>(ASTCtx, Env, Name: "i");
4136 auto &SLoc = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "s");
4137
4138 EXPECT_EQ(SLoc.getChild(*RefFieldDecl), &ILoc);
4139 });
4140}
4141
4142TEST(TransferTest, AggregateInitialization_NotExplicitlyInitializedField) {
4143 std::string Code = R"(
4144 struct S {
4145 int i1;
4146 int i2;
4147 };
4148
4149 void target(int i) {
4150 S s = { i };
4151 /*[[p]]*/
4152 }
4153 )";
4154 runDataflow(
4155 Code,
4156 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4157 ASTContext &ASTCtx) {
4158 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4159
4160 const ValueDecl *I1FieldDecl = findValueDecl(ASTCtx, Name: "i1");
4161 const ValueDecl *I2FieldDecl = findValueDecl(ASTCtx, Name: "i2");
4162
4163 auto &SLoc = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "s");
4164
4165 auto &IValue = getValueForDecl<IntegerValue>(ASTCtx, Env, Name: "i");
4166 auto &I1Value =
4167 *cast<IntegerValue>(Val: getFieldValue(Loc: &SLoc, Field: *I1FieldDecl, Env));
4168 EXPECT_EQ(&I1Value, &IValue);
4169 auto &I2Value =
4170 *cast<IntegerValue>(Val: getFieldValue(Loc: &SLoc, Field: *I2FieldDecl, Env));
4171 EXPECT_NE(&I2Value, &IValue);
4172 });
4173}
4174
4175TEST(TransferTest, AggregateInitializationFunctionPointer) {
4176 // This is a repro for an assertion failure.
4177 // nullptr takes on the type of a const function pointer, but its type was
4178 // asserted to be equal to the *unqualified* type of Field, which no longer
4179 // included the const.
4180 std::string Code = R"(
4181 struct S {
4182 void (*const Field)();
4183 };
4184
4185 void target() {
4186 S s{nullptr};
4187 }
4188 )";
4189 runDataflow(
4190 Code,
4191 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4192 ASTContext &ASTCtx) {});
4193}
4194
4195TEST(TransferTest, AssignToUnionMember) {
4196 std::string Code = R"(
4197 union A {
4198 int Foo;
4199 };
4200
4201 void target(int Bar) {
4202 A Baz;
4203 Baz.Foo = Bar;
4204 // [[p]]
4205 }
4206 )";
4207 runDataflow(
4208 Code,
4209 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4210 ASTContext &ASTCtx) {
4211 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4212 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4213
4214 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
4215 ASSERT_THAT(BazDecl, NotNull());
4216 ASSERT_TRUE(BazDecl->getType()->isUnionType());
4217
4218 auto BazFields = BazDecl->getType()->getAsRecordDecl()->fields();
4219 FieldDecl *FooDecl = nullptr;
4220 for (FieldDecl *Field : BazFields) {
4221 if (Field->getNameAsString() == "Foo") {
4222 FooDecl = Field;
4223 } else {
4224 FAIL() << "Unexpected field: " << Field->getNameAsString();
4225 }
4226 }
4227 ASSERT_THAT(FooDecl, NotNull());
4228
4229 const auto *BazLoc = dyn_cast_or_null<RecordStorageLocation>(
4230 Val: Env.getStorageLocation(D: *BazDecl));
4231 ASSERT_THAT(BazLoc, NotNull());
4232
4233 const auto *FooVal =
4234 cast<IntegerValue>(Val: getFieldValue(BazLoc, *FooDecl, Env));
4235
4236 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4237 ASSERT_THAT(BarDecl, NotNull());
4238 const auto *BarLoc = Env.getStorageLocation(D: *BarDecl);
4239 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
4240
4241 EXPECT_EQ(Env.getValue(*BarLoc), FooVal);
4242 });
4243}
4244
4245TEST(TransferTest, AssignFromBoolLiteral) {
4246 std::string Code = R"(
4247 void target() {
4248 bool Foo = true;
4249 bool Bar = false;
4250 // [[p]]
4251 }
4252 )";
4253 runDataflow(
4254 Code,
4255 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4256 ASTContext &ASTCtx) {
4257 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4258 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4259
4260 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
4261 ASSERT_THAT(FooDecl, NotNull());
4262
4263 const auto *FooVal =
4264 dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *FooDecl));
4265 ASSERT_THAT(FooVal, NotNull());
4266
4267 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4268 ASSERT_THAT(BarDecl, NotNull());
4269
4270 const auto *BarVal =
4271 dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *BarDecl));
4272 ASSERT_THAT(BarVal, NotNull());
4273
4274 EXPECT_EQ(FooVal, &Env.getBoolLiteralValue(true));
4275 EXPECT_EQ(BarVal, &Env.getBoolLiteralValue(false));
4276 });
4277}
4278
4279TEST(TransferTest, AssignFromCompositeBoolExpression) {
4280 {
4281 std::string Code = R"(
4282 void target(bool Foo, bool Bar, bool Qux) {
4283 bool Baz = (Foo) && (Bar || Qux);
4284 // [[p]]
4285 }
4286 )";
4287 runDataflow(
4288 Code,
4289 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4290 ASTContext &ASTCtx) {
4291 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4292 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4293
4294 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
4295 ASSERT_THAT(FooDecl, NotNull());
4296
4297 const auto *FooVal =
4298 dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *FooDecl));
4299 ASSERT_THAT(FooVal, NotNull());
4300
4301 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4302 ASSERT_THAT(BarDecl, NotNull());
4303
4304 const auto *BarVal =
4305 dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *BarDecl));
4306 ASSERT_THAT(BarVal, NotNull());
4307
4308 const ValueDecl *QuxDecl = findValueDecl(ASTCtx, Name: "Qux");
4309 ASSERT_THAT(QuxDecl, NotNull());
4310
4311 const auto *QuxVal =
4312 dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *QuxDecl));
4313 ASSERT_THAT(QuxVal, NotNull());
4314
4315 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
4316 ASSERT_THAT(BazDecl, NotNull());
4317
4318 const auto *BazVal =
4319 dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *BazDecl));
4320 ASSERT_THAT(BazVal, NotNull());
4321 auto &A = Env.arena();
4322 EXPECT_EQ(&BazVal->formula(),
4323 &A.makeAnd(FooVal->formula(),
4324 A.makeOr(BarVal->formula(), QuxVal->formula())));
4325 });
4326 }
4327
4328 {
4329 std::string Code = R"(
4330 void target(bool Foo, bool Bar, bool Qux) {
4331 bool Baz = (Foo && Qux) || (Bar);
4332 // [[p]]
4333 }
4334 )";
4335 runDataflow(
4336 Code,
4337 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4338 ASTContext &ASTCtx) {
4339 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4340 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4341
4342 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
4343 ASSERT_THAT(FooDecl, NotNull());
4344
4345 const auto *FooVal =
4346 dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *FooDecl));
4347 ASSERT_THAT(FooVal, NotNull());
4348
4349 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4350 ASSERT_THAT(BarDecl, NotNull());
4351
4352 const auto *BarVal =
4353 dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *BarDecl));
4354 ASSERT_THAT(BarVal, NotNull());
4355
4356 const ValueDecl *QuxDecl = findValueDecl(ASTCtx, Name: "Qux");
4357 ASSERT_THAT(QuxDecl, NotNull());
4358
4359 const auto *QuxVal =
4360 dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *QuxDecl));
4361 ASSERT_THAT(QuxVal, NotNull());
4362
4363 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
4364 ASSERT_THAT(BazDecl, NotNull());
4365
4366 const auto *BazVal =
4367 dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *BazDecl));
4368 ASSERT_THAT(BazVal, NotNull());
4369 auto &A = Env.arena();
4370 EXPECT_EQ(&BazVal->formula(),
4371 &A.makeOr(A.makeAnd(FooVal->formula(), QuxVal->formula()),
4372 BarVal->formula()));
4373 });
4374 }
4375
4376 {
4377 std::string Code = R"(
4378 void target(bool A, bool B, bool C, bool D) {
4379 bool Foo = ((A && B) && C) && D;
4380 // [[p]]
4381 }
4382 )";
4383 runDataflow(
4384 Code,
4385 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4386 ASTContext &ASTCtx) {
4387 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4388 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4389
4390 const ValueDecl *ADecl = findValueDecl(ASTCtx, Name: "A");
4391 ASSERT_THAT(ADecl, NotNull());
4392
4393 const auto *AVal = dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *ADecl));
4394 ASSERT_THAT(AVal, NotNull());
4395
4396 const ValueDecl *BDecl = findValueDecl(ASTCtx, Name: "B");
4397 ASSERT_THAT(BDecl, NotNull());
4398
4399 const auto *BVal = dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *BDecl));
4400 ASSERT_THAT(BVal, NotNull());
4401
4402 const ValueDecl *CDecl = findValueDecl(ASTCtx, Name: "C");
4403 ASSERT_THAT(CDecl, NotNull());
4404
4405 const auto *CVal = dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *CDecl));
4406 ASSERT_THAT(CVal, NotNull());
4407
4408 const ValueDecl *DDecl = findValueDecl(ASTCtx, Name: "D");
4409 ASSERT_THAT(DDecl, NotNull());
4410
4411 const auto *DVal = dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *DDecl));
4412 ASSERT_THAT(DVal, NotNull());
4413
4414 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
4415 ASSERT_THAT(FooDecl, NotNull());
4416
4417 const auto *FooVal =
4418 dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *FooDecl));
4419 ASSERT_THAT(FooVal, NotNull());
4420 auto &A = Env.arena();
4421 EXPECT_EQ(
4422 &FooVal->formula(),
4423 &A.makeAnd(A.makeAnd(A.makeAnd(AVal->formula(), BVal->formula()),
4424 CVal->formula()),
4425 DVal->formula()));
4426 });
4427 }
4428}
4429
4430TEST(TransferTest, AssignFromBoolNegation) {
4431 std::string Code = R"(
4432 void target() {
4433 bool Foo = true;
4434 bool Bar = !(Foo);
4435 // [[p]]
4436 }
4437 )";
4438 runDataflow(
4439 Code,
4440 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4441 ASTContext &ASTCtx) {
4442 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4443 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4444
4445 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
4446 ASSERT_THAT(FooDecl, NotNull());
4447
4448 const auto *FooVal =
4449 dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *FooDecl));
4450 ASSERT_THAT(FooVal, NotNull());
4451
4452 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4453 ASSERT_THAT(BarDecl, NotNull());
4454
4455 const auto *BarVal =
4456 dyn_cast_or_null<BoolValue>(Val: Env.getValue(D: *BarDecl));
4457 ASSERT_THAT(BarVal, NotNull());
4458 auto &A = Env.arena();
4459 EXPECT_EQ(&BarVal->formula(), &A.makeNot(FooVal->formula()));
4460 });
4461}
4462
4463TEST(TransferTest, BuiltinExpect) {
4464 std::string Code = R"(
4465 void target(long Foo) {
4466 long Bar = __builtin_expect(Foo, true);
4467 /*[[p]]*/
4468 }
4469 )";
4470 runDataflow(
4471 Code,
4472 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4473 ASTContext &ASTCtx) {
4474 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4475 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4476
4477 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
4478 ASSERT_THAT(FooDecl, NotNull());
4479
4480 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4481 ASSERT_THAT(BarDecl, NotNull());
4482
4483 EXPECT_EQ(Env.getValue(*FooDecl), Env.getValue(*BarDecl));
4484 });
4485}
4486
4487// `__builtin_expect` takes and returns a `long` argument, so other types
4488// involve casts. This verifies that we identify the input and output in that
4489// case.
4490TEST(TransferTest, BuiltinExpectBoolArg) {
4491 std::string Code = R"(
4492 void target(bool Foo) {
4493 bool Bar = __builtin_expect(Foo, true);
4494 /*[[p]]*/
4495 }
4496 )";
4497 runDataflow(
4498 Code,
4499 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4500 ASTContext &ASTCtx) {
4501 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4502 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4503
4504 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
4505 ASSERT_THAT(FooDecl, NotNull());
4506
4507 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4508 ASSERT_THAT(BarDecl, NotNull());
4509
4510 EXPECT_EQ(Env.getValue(*FooDecl), Env.getValue(*BarDecl));
4511 });
4512}
4513
4514TEST(TransferTest, BuiltinUnreachable) {
4515 std::string Code = R"(
4516 void target(bool Foo) {
4517 bool Bar = false;
4518 if (Foo)
4519 Bar = Foo;
4520 else
4521 __builtin_unreachable();
4522 (void)0;
4523 /*[[p]]*/
4524 }
4525 )";
4526 runDataflow(
4527 Code,
4528 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4529 ASTContext &ASTCtx) {
4530 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4531 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4532
4533 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
4534 ASSERT_THAT(FooDecl, NotNull());
4535
4536 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4537 ASSERT_THAT(BarDecl, NotNull());
4538
4539 // `__builtin_unreachable` promises that the code is
4540 // unreachable, so the compiler treats the "then" branch as the
4541 // only possible predecessor of this statement.
4542 EXPECT_EQ(Env.getValue(*FooDecl), Env.getValue(*BarDecl));
4543 });
4544}
4545
4546TEST(TransferTest, BuiltinTrap) {
4547 std::string Code = R"(
4548 void target(bool Foo) {
4549 bool Bar = false;
4550 if (Foo)
4551 Bar = Foo;
4552 else
4553 __builtin_trap();
4554 (void)0;
4555 /*[[p]]*/
4556 }
4557 )";
4558 runDataflow(
4559 Code,
4560 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4561 ASTContext &ASTCtx) {
4562 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4563 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4564
4565 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
4566 ASSERT_THAT(FooDecl, NotNull());
4567
4568 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4569 ASSERT_THAT(BarDecl, NotNull());
4570
4571 // `__builtin_trap` ensures program termination, so only the
4572 // "then" branch is a predecessor of this statement.
4573 EXPECT_EQ(Env.getValue(*FooDecl), Env.getValue(*BarDecl));
4574 });
4575}
4576
4577TEST(TransferTest, BuiltinDebugTrap) {
4578 std::string Code = R"(
4579 void target(bool Foo) {
4580 bool Bar = false;
4581 if (Foo)
4582 Bar = Foo;
4583 else
4584 __builtin_debugtrap();
4585 (void)0;
4586 /*[[p]]*/
4587 }
4588 )";
4589 runDataflow(
4590 Code,
4591 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4592 ASTContext &ASTCtx) {
4593 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4594 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4595
4596 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
4597 ASSERT_THAT(FooDecl, NotNull());
4598
4599 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4600 ASSERT_THAT(BarDecl, NotNull());
4601
4602 // `__builtin_debugtrap` doesn't ensure program termination.
4603 EXPECT_NE(Env.getValue(*FooDecl), Env.getValue(*BarDecl));
4604 });
4605}
4606
4607TEST(TransferTest, StaticIntSingleVarDecl) {
4608 std::string Code = R"(
4609 void target() {
4610 static int Foo;
4611 // [[p]]
4612 }
4613 )";
4614 runDataflow(
4615 Code,
4616 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4617 ASTContext &ASTCtx) {
4618 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4619 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4620
4621 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
4622 ASSERT_THAT(FooDecl, NotNull());
4623
4624 const StorageLocation *FooLoc = Env.getStorageLocation(D: *FooDecl);
4625 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
4626
4627 const Value *FooVal = Env.getValue(Loc: *FooLoc);
4628 EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
4629 });
4630}
4631
4632TEST(TransferTest, StaticIntGroupVarDecl) {
4633 std::string Code = R"(
4634 void target() {
4635 static int Foo, Bar;
4636 (void)0;
4637 // [[p]]
4638 }
4639 )";
4640 runDataflow(
4641 Code,
4642 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4643 ASTContext &ASTCtx) {
4644 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4645 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4646
4647 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
4648 ASSERT_THAT(FooDecl, NotNull());
4649
4650 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4651 ASSERT_THAT(BarDecl, NotNull());
4652
4653 const StorageLocation *FooLoc = Env.getStorageLocation(D: *FooDecl);
4654 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
4655
4656 const StorageLocation *BarLoc = Env.getStorageLocation(D: *BarDecl);
4657 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(BarLoc));
4658
4659 const Value *FooVal = Env.getValue(Loc: *FooLoc);
4660 EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
4661
4662 const Value *BarVal = Env.getValue(Loc: *BarLoc);
4663 EXPECT_TRUE(isa_and_nonnull<IntegerValue>(BarVal));
4664
4665 EXPECT_NE(FooVal, BarVal);
4666 });
4667}
4668
4669TEST(TransferTest, GlobalIntVarDecl) {
4670 std::string Code = R"(
4671 static int Foo;
4672
4673 void target() {
4674 int Bar = Foo;
4675 int Baz = Foo;
4676 // [[p]]
4677 }
4678 )";
4679 runDataflow(
4680 Code,
4681 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4682 ASTContext &ASTCtx) {
4683 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4684 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4685
4686 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4687 ASSERT_THAT(BarDecl, NotNull());
4688
4689 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
4690 ASSERT_THAT(BazDecl, NotNull());
4691
4692 const Value *BarVal = cast<IntegerValue>(Val: Env.getValue(D: *BarDecl));
4693 const Value *BazVal = cast<IntegerValue>(Val: Env.getValue(D: *BazDecl));
4694 EXPECT_EQ(BarVal, BazVal);
4695 });
4696}
4697
4698TEST(TransferTest, StaticMemberIntVarDecl) {
4699 std::string Code = R"(
4700 struct A {
4701 static int Foo;
4702 };
4703
4704 void target(A a) {
4705 int Bar = a.Foo;
4706 int Baz = a.Foo;
4707 // [[p]]
4708 }
4709 )";
4710 runDataflow(
4711 Code,
4712 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4713 ASTContext &ASTCtx) {
4714 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4715 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4716
4717 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4718 ASSERT_THAT(BarDecl, NotNull());
4719
4720 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
4721 ASSERT_THAT(BazDecl, NotNull());
4722
4723 const Value *BarVal = cast<IntegerValue>(Val: Env.getValue(D: *BarDecl));
4724 const Value *BazVal = cast<IntegerValue>(Val: Env.getValue(D: *BazDecl));
4725 EXPECT_EQ(BarVal, BazVal);
4726 });
4727}
4728
4729TEST(TransferTest, StaticMemberRefVarDecl) {
4730 std::string Code = R"(
4731 struct A {
4732 static int &Foo;
4733 };
4734
4735 void target(A a) {
4736 int Bar = a.Foo;
4737 int Baz = a.Foo;
4738 // [[p]]
4739 }
4740 )";
4741 runDataflow(
4742 Code,
4743 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4744 ASTContext &ASTCtx) {
4745 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4746 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4747
4748 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4749 ASSERT_THAT(BarDecl, NotNull());
4750
4751 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
4752 ASSERT_THAT(BazDecl, NotNull());
4753
4754 const Value *BarVal = cast<IntegerValue>(Val: Env.getValue(D: *BarDecl));
4755 const Value *BazVal = cast<IntegerValue>(Val: Env.getValue(D: *BazDecl));
4756 EXPECT_EQ(BarVal, BazVal);
4757 });
4758}
4759
4760TEST(TransferTest, AssignMemberBeforeCopy) {
4761 std::string Code = R"(
4762 struct A {
4763 int Foo;
4764 };
4765
4766 void target() {
4767 A A1;
4768 A A2;
4769 int Bar;
4770 A1.Foo = Bar;
4771 A2 = A1;
4772 // [[p]]
4773 }
4774 )";
4775 runDataflow(
4776 Code,
4777 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4778 ASTContext &ASTCtx) {
4779 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
4780 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4781
4782 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
4783 ASSERT_THAT(FooDecl, NotNull());
4784
4785 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4786 ASSERT_THAT(BarDecl, NotNull());
4787
4788 const ValueDecl *A1Decl = findValueDecl(ASTCtx, Name: "A1");
4789 ASSERT_THAT(A1Decl, NotNull());
4790
4791 const ValueDecl *A2Decl = findValueDecl(ASTCtx, Name: "A2");
4792 ASSERT_THAT(A2Decl, NotNull());
4793
4794 const auto *BarVal = cast<IntegerValue>(Val: Env.getValue(D: *BarDecl));
4795
4796 const auto &A2Loc =
4797 *cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *A2Decl));
4798 EXPECT_EQ(getFieldValue(&A2Loc, *FooDecl, Env), BarVal);
4799 });
4800}
4801
4802TEST(TransferTest, BooleanEquality) {
4803 std::string Code = R"(
4804 void target(bool Bar) {
4805 bool Foo = true;
4806 if (Bar == Foo) {
4807 (void)0;
4808 /*[[p-then]]*/
4809 } else {
4810 (void)0;
4811 /*[[p-else]]*/
4812 }
4813 }
4814 )";
4815 runDataflow(
4816 Code,
4817 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4818 ASTContext &ASTCtx) {
4819 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p-then", "p-else"));
4820 const Environment &EnvThen =
4821 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p-then");
4822 const Environment &EnvElse =
4823 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p-else");
4824
4825 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4826 ASSERT_THAT(BarDecl, NotNull());
4827
4828 auto &BarValThen = getFormula(D: *BarDecl, Env: EnvThen);
4829 EXPECT_TRUE(EnvThen.proves(BarValThen));
4830
4831 auto &BarValElse = getFormula(D: *BarDecl, Env: EnvElse);
4832 EXPECT_TRUE(EnvElse.proves(EnvElse.arena().makeNot(BarValElse)));
4833 });
4834}
4835
4836TEST(TransferTest, BooleanInequality) {
4837 std::string Code = R"(
4838 void target(bool Bar) {
4839 bool Foo = true;
4840 if (Bar != Foo) {
4841 (void)0;
4842 /*[[p-then]]*/
4843 } else {
4844 (void)0;
4845 /*[[p-else]]*/
4846 }
4847 }
4848 )";
4849 runDataflow(
4850 Code,
4851 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4852 ASTContext &ASTCtx) {
4853 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p-then", "p-else"));
4854 const Environment &EnvThen =
4855 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p-then");
4856 const Environment &EnvElse =
4857 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p-else");
4858
4859 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
4860 ASSERT_THAT(BarDecl, NotNull());
4861
4862 auto &BarValThen = getFormula(D: *BarDecl, Env: EnvThen);
4863 EXPECT_TRUE(EnvThen.proves(EnvThen.arena().makeNot(BarValThen)));
4864
4865 auto &BarValElse = getFormula(D: *BarDecl, Env: EnvElse);
4866 EXPECT_TRUE(EnvElse.proves(BarValElse));
4867 });
4868}
4869
4870TEST(TransferTest, PointerEquality) {
4871 std::string Code = R"cc(
4872 void target() {
4873 int i = 0;
4874 int i_other = 0;
4875 int *p1 = &i;
4876 int *p2 = &i;
4877 int *p_other = &i_other;
4878 int *null = nullptr;
4879
4880 bool p1_eq_p1 = (p1 == p1);
4881 bool p1_eq_p2 = (p1 == p2);
4882 bool p1_eq_p_other = (p1 == p_other);
4883
4884 bool p1_eq_null = (p1 == null);
4885 bool p1_eq_nullptr = (p1 == nullptr);
4886 bool null_eq_nullptr = (null == nullptr);
4887 bool nullptr_eq_nullptr = (nullptr == nullptr);
4888
4889 // We won't duplicate all of the tests above with `!=`, as we know that
4890 // the implementation simply negates the result of the `==` comparison.
4891 // Instead, just spot-check one case.
4892 bool p1_ne_p1 = (p1 != p1);
4893
4894 (void)0; // [[p]]
4895 }
4896 )cc";
4897 runDataflow(
4898 Code,
4899 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4900 ASTContext &ASTCtx) {
4901 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4902
4903 // Check the we have indeed set things up so that `p1` and `p2` have
4904 // different pointer values.
4905 EXPECT_NE(&getValueForDecl<PointerValue>(ASTCtx, Env, "p1"),
4906 &getValueForDecl<PointerValue>(ASTCtx, Env, "p2"));
4907
4908 EXPECT_EQ(&getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_p1"),
4909 &Env.getBoolLiteralValue(true));
4910 EXPECT_EQ(&getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_p2"),
4911 &Env.getBoolLiteralValue(true));
4912 EXPECT_TRUE(isa<AtomicBoolValue>(
4913 getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_p_other")));
4914
4915 EXPECT_TRUE(isa<AtomicBoolValue>(
4916 getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_null")));
4917 EXPECT_TRUE(isa<AtomicBoolValue>(
4918 getValueForDecl<BoolValue>(ASTCtx, Env, "p1_eq_nullptr")));
4919 EXPECT_EQ(&getValueForDecl<BoolValue>(ASTCtx, Env, "null_eq_nullptr"),
4920 &Env.getBoolLiteralValue(true));
4921 EXPECT_EQ(
4922 &getValueForDecl<BoolValue>(ASTCtx, Env, "nullptr_eq_nullptr"),
4923 &Env.getBoolLiteralValue(true));
4924
4925 EXPECT_EQ(&getValueForDecl<BoolValue>(ASTCtx, Env, "p1_ne_p1"),
4926 &Env.getBoolLiteralValue(false));
4927 });
4928}
4929
4930TEST(TransferTest, PointerEqualityUnionMembers) {
4931 std::string Code = R"cc(
4932 union U {
4933 int i1;
4934 int i2;
4935 };
4936 void target() {
4937 U u;
4938 bool i1_eq_i2 = (&u.i1 == &u.i2);
4939
4940 (void)0; // [[p]]
4941 }
4942 )cc";
4943 runDataflow(
4944 Code,
4945 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4946 ASTContext &ASTCtx) {
4947 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4948
4949 // FIXME: By the standard, `u.i1` and `u.i2` should have the same
4950 // address, but we don't yet model this property of union members
4951 // correctly. The result is therefore weaker than it could be (just an
4952 // atom rather than a true literal), though not wrong.
4953 EXPECT_TRUE(isa<AtomicBoolValue>(
4954 getValueForDecl<BoolValue>(ASTCtx, Env, "i1_eq_i2")));
4955 });
4956}
4957
4958TEST(TransferTest, IntegerLiteralEquality) {
4959 std::string Code = R"(
4960 void target() {
4961 bool equal = (42 == 42);
4962 // [[p]]
4963 }
4964 )";
4965 runDataflow(
4966 Code,
4967 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4968 ASTContext &ASTCtx) {
4969 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
4970
4971 auto &Equal =
4972 getValueForDecl<BoolValue>(ASTCtx, Env, Name: "equal").formula();
4973 EXPECT_TRUE(Env.proves(Equal));
4974 });
4975}
4976
4977TEST(TransferTest, UnsupportedValueEquality) {
4978 std::string Code = R"(
4979 // An explicitly unsupported type by the framework.
4980 enum class EC {
4981 A,
4982 B
4983 };
4984
4985 void target() {
4986 EC ec = EC::A;
4987
4988 bool unsupported_eq_same = (EC::A == EC::A);
4989 bool unsupported_eq_other = (EC::A == EC::B);
4990 bool unsupported_eq_var = (ec == EC::B);
4991
4992 (void)0; // [[p]]
4993 }
4994 )";
4995 runDataflow(
4996 Code,
4997 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
4998 ASTContext &ASTCtx) {
4999 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
5000
5001 // We do not model the values of unsupported types, so this
5002 // seemingly-trivial case will not be true either.
5003 EXPECT_TRUE(isa<AtomicBoolValue>(
5004 getValueForDecl<BoolValue>(ASTCtx, Env, "unsupported_eq_same")));
5005 EXPECT_TRUE(isa<AtomicBoolValue>(
5006 getValueForDecl<BoolValue>(ASTCtx, Env, "unsupported_eq_other")));
5007 EXPECT_TRUE(isa<AtomicBoolValue>(
5008 getValueForDecl<BoolValue>(ASTCtx, Env, "unsupported_eq_var")));
5009 });
5010}
5011
5012TEST(TransferTest, CorrelatedBranches) {
5013 std::string Code = R"(
5014 void target(bool B, bool C) {
5015 if (B) {
5016 return;
5017 }
5018 (void)0;
5019 /*[[p0]]*/
5020 if (C) {
5021 B = true;
5022 /*[[p1]]*/
5023 }
5024 if (B) {
5025 (void)0;
5026 /*[[p2]]*/
5027 }
5028 }
5029 )";
5030 runDataflow(
5031 Code,
5032 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5033 ASTContext &ASTCtx) {
5034 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p0", "p1", "p2"));
5035
5036 const ValueDecl *CDecl = findValueDecl(ASTCtx, Name: "C");
5037 ASSERT_THAT(CDecl, NotNull());
5038
5039 {
5040 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p0");
5041 const ValueDecl *BDecl = findValueDecl(ASTCtx, Name: "B");
5042 ASSERT_THAT(BDecl, NotNull());
5043 auto &BVal = getFormula(D: *BDecl, Env);
5044
5045 EXPECT_TRUE(Env.proves(Env.arena().makeNot(BVal)));
5046 }
5047
5048 {
5049 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p1");
5050 auto &CVal = getFormula(D: *CDecl, Env);
5051 EXPECT_TRUE(Env.proves(CVal));
5052 }
5053
5054 {
5055 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p2");
5056 auto &CVal = getFormula(D: *CDecl, Env);
5057 EXPECT_TRUE(Env.proves(CVal));
5058 }
5059 });
5060}
5061
5062TEST(TransferTest, LoopWithAssignmentConverges) {
5063 std::string Code = R"(
5064 bool foo();
5065
5066 void target() {
5067 do {
5068 bool Bar = foo();
5069 if (Bar) break;
5070 (void)Bar;
5071 /*[[p]]*/
5072 } while (true);
5073 }
5074 )";
5075 // The key property that we are verifying is implicit in `runDataflow` --
5076 // namely, that the analysis succeeds, rather than hitting the maximum number
5077 // of iterations.
5078 runDataflow(
5079 Code,
5080 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5081 ASTContext &ASTCtx) {
5082 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
5083 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
5084
5085 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
5086 ASSERT_THAT(BarDecl, NotNull());
5087
5088 auto &BarVal = getFormula(D: *BarDecl, Env);
5089 EXPECT_TRUE(Env.proves(Env.arena().makeNot(BarVal)));
5090 });
5091}
5092
5093TEST(TransferTest, LoopWithStagedAssignments) {
5094 std::string Code = R"(
5095 bool foo();
5096
5097 void target() {
5098 bool Bar = false;
5099 bool Err = false;
5100 while (foo()) {
5101 if (Bar)
5102 Err = true;
5103 Bar = true;
5104 /*[[p]]*/
5105 }
5106 }
5107 )";
5108 runDataflow(
5109 Code,
5110 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5111 ASTContext &ASTCtx) {
5112 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
5113 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
5114
5115 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
5116 ASSERT_THAT(BarDecl, NotNull());
5117 const ValueDecl *ErrDecl = findValueDecl(ASTCtx, Name: "Err");
5118 ASSERT_THAT(ErrDecl, NotNull());
5119
5120 auto &BarVal = getFormula(D: *BarDecl, Env);
5121 auto &ErrVal = getFormula(D: *ErrDecl, Env);
5122 EXPECT_TRUE(Env.proves(BarVal));
5123 // An unsound analysis, for example only evaluating the loop once, can
5124 // conclude that `Err` is false. So, we test that this conclusion is not
5125 // reached.
5126 EXPECT_FALSE(Env.proves(Env.arena().makeNot(ErrVal)));
5127 });
5128}
5129
5130TEST(TransferTest, LoopWithReferenceAssignmentConverges) {
5131 std::string Code = R"(
5132 bool &foo();
5133
5134 void target() {
5135 do {
5136 bool& Bar = foo();
5137 if (Bar) break;
5138 (void)Bar;
5139 /*[[p]]*/
5140 } while (true);
5141 }
5142 )";
5143 // The key property that we are verifying is that the analysis succeeds,
5144 // rather than hitting the maximum number of iterations.
5145 runDataflow(
5146 Code,
5147 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5148 ASTContext &ASTCtx) {
5149 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
5150 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
5151
5152 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
5153 ASSERT_THAT(BarDecl, NotNull());
5154
5155 auto &BarVal = getFormula(D: *BarDecl, Env);
5156 EXPECT_TRUE(Env.proves(Env.arena().makeNot(BarVal)));
5157 });
5158}
5159
5160TEST(TransferTest, LoopWithStructReferenceAssignmentConverges) {
5161 std::string Code = R"(
5162 struct Lookup {
5163 int x;
5164 };
5165
5166 void target(Lookup val, bool b) {
5167 const Lookup* l = nullptr;
5168 while (b) {
5169 l = &val;
5170 /*[[p-inner]]*/
5171 }
5172 (void)0;
5173 /*[[p-outer]]*/
5174 }
5175 )";
5176 // The key property that we are verifying is implicit in `runDataflow` --
5177 // namely, that the analysis succeeds, rather than hitting the maximum number
5178 // of iterations.
5179 runDataflow(
5180 Code,
5181 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5182 ASTContext &ASTCtx) {
5183 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p-inner", "p-outer"));
5184 const Environment &InnerEnv =
5185 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p-inner");
5186 const Environment &OuterEnv =
5187 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p-outer");
5188
5189 const ValueDecl *ValDecl = findValueDecl(ASTCtx, Name: "val");
5190 ASSERT_THAT(ValDecl, NotNull());
5191
5192 const ValueDecl *LDecl = findValueDecl(ASTCtx, Name: "l");
5193 ASSERT_THAT(LDecl, NotNull());
5194
5195 // Inner.
5196 auto *LVal = dyn_cast<PointerValue>(Val: InnerEnv.getValue(D: *LDecl));
5197 ASSERT_THAT(LVal, NotNull());
5198
5199 EXPECT_EQ(&LVal->getPointeeLoc(),
5200 InnerEnv.getStorageLocation(*ValDecl));
5201
5202 // Outer.
5203 LVal = dyn_cast<PointerValue>(Val: OuterEnv.getValue(D: *LDecl));
5204 ASSERT_THAT(LVal, NotNull());
5205
5206 // The loop body may not have been executed, so we should not conclude
5207 // that `l` points to `val`.
5208 EXPECT_NE(&LVal->getPointeeLoc(),
5209 OuterEnv.getStorageLocation(*ValDecl));
5210 });
5211}
5212
5213TEST(TransferTest, LoopDereferencingChangingPointerConverges) {
5214 std::string Code = R"cc(
5215 bool some_condition();
5216
5217 void target(int i1, int i2) {
5218 int *p = &i1;
5219 while (true) {
5220 (void)*p;
5221 if (some_condition())
5222 p = &i1;
5223 else
5224 p = &i2;
5225 }
5226 }
5227 )cc";
5228 ASSERT_THAT_ERROR(checkDataflowWithNoopAnalysis(Code), llvm::Succeeded());
5229}
5230
5231TEST(TransferTest, LoopDereferencingChangingRecordPointerConverges) {
5232 std::string Code = R"cc(
5233 struct Lookup {
5234 int x;
5235 };
5236
5237 bool some_condition();
5238
5239 void target(Lookup l1, Lookup l2) {
5240 Lookup *l = &l1;
5241 while (true) {
5242 (void)l->x;
5243 if (some_condition())
5244 l = &l1;
5245 else
5246 l = &l2;
5247 }
5248 }
5249 )cc";
5250 ASSERT_THAT_ERROR(checkDataflowWithNoopAnalysis(Code), llvm::Succeeded());
5251}
5252
5253TEST(TransferTest, LoopWithShortCircuitedConditionConverges) {
5254 std::string Code = R"cc(
5255 bool foo();
5256
5257 void target() {
5258 bool c = false;
5259 while (foo() || foo()) {
5260 c = true;
5261 }
5262 }
5263 )cc";
5264 ASSERT_THAT_ERROR(checkDataflowWithNoopAnalysis(Code), llvm::Succeeded());
5265}
5266
5267TEST(TransferTest, LoopCanProveInvariantForBoolean) {
5268 // Check that we can prove `b` is always false in the loop.
5269 // This test exercises the logic in `widenDistinctValues()` that preserves
5270 // information if the boolean can be proved to be either true or false in both
5271 // the previous and current iteration.
5272 std::string Code = R"cc(
5273 int return_int();
5274 void target() {
5275 bool b = return_int() == 0;
5276 if (b) return;
5277 while (true) {
5278 b;
5279 // [[p]]
5280 b = return_int() == 0;
5281 if (b) return;
5282 }
5283 }
5284 )cc";
5285 runDataflow(
5286 Code,
5287 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5288 ASTContext &ASTCtx) {
5289 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
5290 auto &BVal = getValueForDecl<BoolValue>(ASTCtx, Env, Name: "b");
5291 EXPECT_TRUE(Env.proves(Env.arena().makeNot(BVal.formula())));
5292 });
5293}
5294
5295TEST(TransferTest, DoesNotCrashOnUnionThisExpr) {
5296 std::string Code = R"cc(
5297 union Union {
5298 int A;
5299 float B;
5300 };
5301
5302 void foo() {
5303 Union A;
5304 Union B;
5305 A = B;
5306 }
5307 )cc";
5308 // This is a crash regression test when calling the transfer function on a
5309 // `CXXThisExpr` that refers to a union.
5310 runDataflow(
5311 Code,
5312 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
5313 ASTContext &) {},
5314 Std: LangStandard::lang_cxx17, /*ApplyBuiltinTransfer=*/true, TargetFun: "operator=");
5315}
5316
5317TEST(TransferTest, DoesNotCrashOnNullChildren) {
5318 std::string Code = (CoroutineLibrary + R"cc(
5319 task target() noexcept {
5320 co_return;
5321 }
5322 )cc")
5323 .str();
5324 // This is a crash regression test when calling `AdornedCFG::build` on a
5325 // statement (in this case, the `CoroutineBodyStmt`) with null children.
5326 runDataflow(
5327 Code,
5328 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &,
5329 ASTContext &) {},
5330 Std: LangStandard::lang_cxx20, /*ApplyBuiltinTransfer=*/true);
5331}
5332
5333TEST(TransferTest, StructuredBindingAssignFromStructIntMembersToRefs) {
5334 std::string Code = R"(
5335 struct A {
5336 int Foo;
5337 int Bar;
5338 };
5339
5340 void target() {
5341 int Qux;
5342 A Baz;
5343 Baz.Foo = Qux;
5344 auto &FooRef = Baz.Foo;
5345 auto &BarRef = Baz.Bar;
5346 auto &[BoundFooRef, BoundBarRef] = Baz;
5347 // [[p]]
5348 }
5349 )";
5350 runDataflow(
5351 Code,
5352 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5353 ASTContext &ASTCtx) {
5354 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
5355 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
5356
5357 const ValueDecl *FooRefDecl = findValueDecl(ASTCtx, Name: "FooRef");
5358 ASSERT_THAT(FooRefDecl, NotNull());
5359
5360 const ValueDecl *BarRefDecl = findValueDecl(ASTCtx, Name: "BarRef");
5361 ASSERT_THAT(BarRefDecl, NotNull());
5362
5363 const ValueDecl *QuxDecl = findValueDecl(ASTCtx, Name: "Qux");
5364 ASSERT_THAT(QuxDecl, NotNull());
5365
5366 const ValueDecl *BoundFooRefDecl = findValueDecl(ASTCtx, Name: "BoundFooRef");
5367 ASSERT_THAT(BoundFooRefDecl, NotNull());
5368
5369 const ValueDecl *BoundBarRefDecl = findValueDecl(ASTCtx, Name: "BoundBarRef");
5370 ASSERT_THAT(BoundBarRefDecl, NotNull());
5371
5372 const StorageLocation *FooRefLoc = Env.getStorageLocation(D: *FooRefDecl);
5373 ASSERT_THAT(FooRefLoc, NotNull());
5374
5375 const StorageLocation *BarRefLoc = Env.getStorageLocation(D: *BarRefDecl);
5376 ASSERT_THAT(BarRefLoc, NotNull());
5377
5378 const Value *QuxVal = Env.getValue(D: *QuxDecl);
5379 ASSERT_THAT(QuxVal, NotNull());
5380
5381 const StorageLocation *BoundFooRefLoc =
5382 Env.getStorageLocation(D: *BoundFooRefDecl);
5383 EXPECT_EQ(BoundFooRefLoc, FooRefLoc);
5384
5385 const StorageLocation *BoundBarRefLoc =
5386 Env.getStorageLocation(D: *BoundBarRefDecl);
5387 EXPECT_EQ(BoundBarRefLoc, BarRefLoc);
5388
5389 EXPECT_EQ(Env.getValue(*BoundFooRefDecl), QuxVal);
5390 });
5391}
5392
5393TEST(TransferTest, StructuredBindingAssignFromStructRefMembersToRefs) {
5394 std::string Code = R"(
5395 struct A {
5396 int &Foo;
5397 int &Bar;
5398 };
5399
5400 void target(A Baz) {
5401 int Qux;
5402 Baz.Foo = Qux;
5403 auto &FooRef = Baz.Foo;
5404 auto &BarRef = Baz.Bar;
5405 auto &[BoundFooRef, BoundBarRef] = Baz;
5406 // [[p]]
5407 }
5408 )";
5409 runDataflow(
5410 Code,
5411 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5412 ASTContext &ASTCtx) {
5413 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
5414 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
5415
5416 const ValueDecl *FooRefDecl = findValueDecl(ASTCtx, Name: "FooRef");
5417 ASSERT_THAT(FooRefDecl, NotNull());
5418
5419 const ValueDecl *BarRefDecl = findValueDecl(ASTCtx, Name: "BarRef");
5420 ASSERT_THAT(BarRefDecl, NotNull());
5421
5422 const ValueDecl *QuxDecl = findValueDecl(ASTCtx, Name: "Qux");
5423 ASSERT_THAT(QuxDecl, NotNull());
5424
5425 const ValueDecl *BoundFooRefDecl = findValueDecl(ASTCtx, Name: "BoundFooRef");
5426 ASSERT_THAT(BoundFooRefDecl, NotNull());
5427
5428 const ValueDecl *BoundBarRefDecl = findValueDecl(ASTCtx, Name: "BoundBarRef");
5429 ASSERT_THAT(BoundBarRefDecl, NotNull());
5430
5431 const StorageLocation *FooRefLoc = Env.getStorageLocation(D: *FooRefDecl);
5432 ASSERT_THAT(FooRefLoc, NotNull());
5433
5434 const StorageLocation *BarRefLoc = Env.getStorageLocation(D: *BarRefDecl);
5435 ASSERT_THAT(BarRefLoc, NotNull());
5436
5437 const Value *QuxVal = Env.getValue(D: *QuxDecl);
5438 ASSERT_THAT(QuxVal, NotNull());
5439
5440 const StorageLocation *BoundFooRefLoc =
5441 Env.getStorageLocation(D: *BoundFooRefDecl);
5442 EXPECT_EQ(BoundFooRefLoc, FooRefLoc);
5443
5444 const StorageLocation *BoundBarRefLoc =
5445 Env.getStorageLocation(D: *BoundBarRefDecl);
5446 EXPECT_EQ(BoundBarRefLoc, BarRefLoc);
5447
5448 EXPECT_EQ(Env.getValue(*BoundFooRefDecl), QuxVal);
5449 });
5450}
5451
5452TEST(TransferTest, StructuredBindingAssignFromStructIntMembersToInts) {
5453 std::string Code = R"(
5454 struct A {
5455 int Foo;
5456 int Bar;
5457 };
5458
5459 void target() {
5460 int Qux;
5461 A Baz;
5462 Baz.Foo = Qux;
5463 auto &FooRef = Baz.Foo;
5464 auto &BarRef = Baz.Bar;
5465 auto [BoundFoo, BoundBar] = Baz;
5466 // [[p]]
5467 }
5468 )";
5469 runDataflow(
5470 Code,
5471 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5472 ASTContext &ASTCtx) {
5473 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
5474 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
5475
5476 const ValueDecl *FooRefDecl = findValueDecl(ASTCtx, Name: "FooRef");
5477 ASSERT_THAT(FooRefDecl, NotNull());
5478
5479 const ValueDecl *BarRefDecl = findValueDecl(ASTCtx, Name: "BarRef");
5480 ASSERT_THAT(BarRefDecl, NotNull());
5481
5482 const ValueDecl *BoundFooDecl = findValueDecl(ASTCtx, Name: "BoundFoo");
5483 ASSERT_THAT(BoundFooDecl, NotNull());
5484
5485 const ValueDecl *BoundBarDecl = findValueDecl(ASTCtx, Name: "BoundBar");
5486 ASSERT_THAT(BoundBarDecl, NotNull());
5487
5488 const ValueDecl *QuxDecl = findValueDecl(ASTCtx, Name: "Qux");
5489 ASSERT_THAT(QuxDecl, NotNull());
5490
5491 const StorageLocation *FooRefLoc = Env.getStorageLocation(D: *FooRefDecl);
5492 ASSERT_THAT(FooRefLoc, NotNull());
5493
5494 const StorageLocation *BarRefLoc = Env.getStorageLocation(D: *BarRefDecl);
5495 ASSERT_THAT(BarRefLoc, NotNull());
5496
5497 const Value *QuxVal = Env.getValue(D: *QuxDecl);
5498 ASSERT_THAT(QuxVal, NotNull());
5499
5500 const StorageLocation *BoundFooLoc =
5501 Env.getStorageLocation(D: *BoundFooDecl);
5502 EXPECT_NE(BoundFooLoc, FooRefLoc);
5503
5504 const StorageLocation *BoundBarLoc =
5505 Env.getStorageLocation(D: *BoundBarDecl);
5506 EXPECT_NE(BoundBarLoc, BarRefLoc);
5507
5508 EXPECT_EQ(Env.getValue(*BoundFooDecl), QuxVal);
5509 });
5510}
5511
5512TEST(TransferTest, StructuredBindingAssignFromTupleLikeType) {
5513 std::string Code = R"(
5514 namespace std {
5515 using size_t = int;
5516 template <class> struct tuple_size;
5517 template <std::size_t, class> struct tuple_element;
5518 template <class...> class tuple;
5519
5520 namespace {
5521 template <class T, T v>
5522 struct size_helper { static const T value = v; };
5523 } // namespace
5524
5525 template <class... T>
5526 struct tuple_size<tuple<T...>> : size_helper<std::size_t, sizeof...(T)> {};
5527
5528 template <std::size_t I, class... T>
5529 struct tuple_element<I, tuple<T...>> {
5530 using type = __type_pack_element<I, T...>;
5531 };
5532
5533 template <class...> class tuple {};
5534
5535 template <std::size_t I, class... T>
5536 typename tuple_element<I, tuple<T...>>::type get(tuple<T...>);
5537 } // namespace std
5538
5539 std::tuple<bool, int> makeTuple();
5540
5541 void target(bool B) {
5542 auto [BoundFoo, BoundBar] = makeTuple();
5543 bool Baz;
5544 // Include if-then-else to test interaction of `BindingDecl` with join.
5545 if (B) {
5546 Baz = BoundFoo;
5547 (void)BoundBar;
5548 // [[p1]]
5549 } else {
5550 Baz = BoundFoo;
5551 }
5552 (void)0;
5553 // [[p2]]
5554 }
5555 )";
5556 runDataflow(
5557 Code,
5558 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5559 ASTContext &ASTCtx) {
5560 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
5561 const Environment &Env1 = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p1");
5562
5563 const ValueDecl *BoundFooDecl = findBindingDecl(ASTCtx, Name: "BoundFoo");
5564 ASSERT_THAT(BoundFooDecl, NotNull());
5565
5566 const ValueDecl *BoundBarDecl = findBindingDecl(ASTCtx, Name: "BoundBar");
5567 ASSERT_THAT(BoundBarDecl, NotNull());
5568
5569 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
5570 ASSERT_THAT(BazDecl, NotNull());
5571
5572 // BindingDecls always map to references -- either lvalue or rvalue, so
5573 // we still need to skip here.
5574 const Value *BoundFooValue = Env1.getValue(D: *BoundFooDecl);
5575 ASSERT_THAT(BoundFooValue, NotNull());
5576 EXPECT_TRUE(isa<BoolValue>(BoundFooValue));
5577
5578 const Value *BoundBarValue = Env1.getValue(D: *BoundBarDecl);
5579 ASSERT_THAT(BoundBarValue, NotNull());
5580 EXPECT_TRUE(isa<IntegerValue>(BoundBarValue));
5581
5582 // Test that a `DeclRefExpr` to a `BindingDecl` works as expected.
5583 EXPECT_EQ(Env1.getValue(*BazDecl), BoundFooValue);
5584
5585 const Environment &Env2 = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p2");
5586
5587 // Test that `BoundFooDecl` retains the value we expect, after the join.
5588 BoundFooValue = Env2.getValue(D: *BoundFooDecl);
5589 EXPECT_EQ(Env2.getValue(*BazDecl), BoundFooValue);
5590 });
5591}
5592
5593TEST(TransferTest, StructuredBindingAssignRefFromTupleLikeType) {
5594 std::string Code = R"(
5595 namespace std {
5596 using size_t = int;
5597 template <class> struct tuple_size;
5598 template <std::size_t, class> struct tuple_element;
5599 template <class...> class tuple;
5600
5601 namespace {
5602 template <class T, T v>
5603 struct size_helper { static const T value = v; };
5604 } // namespace
5605
5606 template <class... T>
5607 struct tuple_size<tuple<T...>> : size_helper<std::size_t, sizeof...(T)> {};
5608
5609 template <std::size_t I, class... T>
5610 struct tuple_element<I, tuple<T...>> {
5611 using type = __type_pack_element<I, T...>;
5612 };
5613
5614 template <class...> class tuple {};
5615
5616 template <std::size_t I, class... T>
5617 typename tuple_element<I, tuple<T...>>::type get(tuple<T...>);
5618 } // namespace std
5619
5620 std::tuple<bool, int> &getTuple();
5621
5622 void target(bool B) {
5623 auto &[BoundFoo, BoundBar] = getTuple();
5624 bool Baz;
5625 // Include if-then-else to test interaction of `BindingDecl` with join.
5626 if (B) {
5627 Baz = BoundFoo;
5628 (void)BoundBar;
5629 // [[p1]]
5630 } else {
5631 Baz = BoundFoo;
5632 }
5633 (void)0;
5634 // [[p2]]
5635 }
5636 )";
5637 runDataflow(
5638 Code,
5639 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5640 ASTContext &ASTCtx) {
5641 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p1", "p2"));
5642 const Environment &Env1 = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p1");
5643
5644 const ValueDecl *BoundFooDecl = findBindingDecl(ASTCtx, Name: "BoundFoo");
5645 ASSERT_THAT(BoundFooDecl, NotNull());
5646
5647 const ValueDecl *BoundBarDecl = findBindingDecl(ASTCtx, Name: "BoundBar");
5648 ASSERT_THAT(BoundBarDecl, NotNull());
5649
5650 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
5651 ASSERT_THAT(BazDecl, NotNull());
5652
5653 const Value *BoundFooValue = Env1.getValue(D: *BoundFooDecl);
5654 ASSERT_THAT(BoundFooValue, NotNull());
5655 EXPECT_TRUE(isa<BoolValue>(BoundFooValue));
5656
5657 const Value *BoundBarValue = Env1.getValue(D: *BoundBarDecl);
5658 ASSERT_THAT(BoundBarValue, NotNull());
5659 EXPECT_TRUE(isa<IntegerValue>(BoundBarValue));
5660
5661 // Test that a `DeclRefExpr` to a `BindingDecl` (with reference type)
5662 // works as expected. We don't test aliasing properties of the
5663 // reference, because we don't model `std::get` and so have no way to
5664 // equate separate references into the tuple.
5665 EXPECT_EQ(Env1.getValue(*BazDecl), BoundFooValue);
5666
5667 const Environment &Env2 = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p2");
5668
5669 // Test that `BoundFooDecl` retains the value we expect, after the join.
5670 BoundFooValue = Env2.getValue(D: *BoundFooDecl);
5671 EXPECT_EQ(Env2.getValue(*BazDecl), BoundFooValue);
5672 });
5673}
5674
5675TEST(TransferTest, BinaryOperatorComma) {
5676 std::string Code = R"(
5677 void target(int Foo, int Bar) {
5678 int &Baz = (Foo, Bar);
5679 // [[p]]
5680 }
5681 )";
5682 runDataflow(
5683 Code,
5684 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5685 ASTContext &ASTCtx) {
5686 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
5687 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
5688
5689 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
5690 ASSERT_THAT(BarDecl, NotNull());
5691
5692 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
5693 ASSERT_THAT(BazDecl, NotNull());
5694
5695 const StorageLocation *BarLoc = Env.getStorageLocation(D: *BarDecl);
5696 ASSERT_THAT(BarLoc, NotNull());
5697
5698 const StorageLocation *BazLoc = Env.getStorageLocation(D: *BazDecl);
5699 EXPECT_EQ(BazLoc, BarLoc);
5700 });
5701}
5702
5703TEST(TransferTest, ConditionalOperatorValue) {
5704 std::string Code = R"(
5705 void target(bool Cond, bool B1, bool B2) {
5706 bool JoinSame = Cond ? B1 : B1;
5707 bool JoinDifferent = Cond ? B1 : B2;
5708 // [[p]]
5709 }
5710 )";
5711 runDataflow(
5712 Code,
5713 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5714 ASTContext &ASTCtx) {
5715 Environment Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p").fork();
5716
5717 auto &B1 = getValueForDecl<BoolValue>(ASTCtx, Env, Name: "B1");
5718 auto &B2 = getValueForDecl<BoolValue>(ASTCtx, Env, Name: "B2");
5719 auto &JoinSame = getValueForDecl<BoolValue>(ASTCtx, Env, Name: "JoinSame");
5720 auto &JoinDifferent =
5721 getValueForDecl<BoolValue>(ASTCtx, Env, Name: "JoinDifferent");
5722
5723 EXPECT_EQ(&JoinSame, &B1);
5724
5725 const Formula &JoinDifferentEqB1 =
5726 Env.arena().makeEquals(LHS: JoinDifferent.formula(), RHS: B1.formula());
5727 EXPECT_TRUE(Env.allows(JoinDifferentEqB1));
5728 EXPECT_FALSE(Env.proves(JoinDifferentEqB1));
5729
5730 const Formula &JoinDifferentEqB2 =
5731 Env.arena().makeEquals(LHS: JoinDifferent.formula(), RHS: B2.formula());
5732 EXPECT_TRUE(Env.allows(JoinDifferentEqB2));
5733 EXPECT_FALSE(Env.proves(JoinDifferentEqB1));
5734 });
5735}
5736
5737TEST(TransferTest, ConditionalOperatorLocation) {
5738 std::string Code = R"(
5739 void target(bool Cond, int I1, int I2) {
5740 int &JoinSame = Cond ? I1 : I1;
5741 int &JoinDifferent = Cond ? I1 : I2;
5742 // [[p]]
5743 }
5744 )";
5745 runDataflow(
5746 Code,
5747 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5748 ASTContext &ASTCtx) {
5749 Environment Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p").fork();
5750
5751 StorageLocation &I1 = getLocForDecl(ASTCtx, Env, Name: "I1");
5752 StorageLocation &I2 = getLocForDecl(ASTCtx, Env, Name: "I2");
5753 StorageLocation &JoinSame = getLocForDecl(ASTCtx, Env, Name: "JoinSame");
5754 StorageLocation &JoinDifferent =
5755 getLocForDecl(ASTCtx, Env, Name: "JoinDifferent");
5756
5757 EXPECT_EQ(&JoinSame, &I1);
5758
5759 EXPECT_NE(&JoinDifferent, &I1);
5760 EXPECT_NE(&JoinDifferent, &I2);
5761 });
5762}
5763
5764TEST(TransferTest, ConditionalOperatorOnConstantExpr) {
5765 // This is a regression test: We used to crash when a `ConstantExpr` was used
5766 // in the branches of a conditional operator.
5767 std::string Code = R"cc(
5768 consteval bool identity(bool B) { return B; }
5769 void target(bool Cond) {
5770 bool JoinTrueTrue = Cond ? identity(true) : identity(true);
5771 bool JoinTrueFalse = Cond ? identity(true) : identity(false);
5772 // [[p]]
5773 }
5774 )cc";
5775 runDataflow(
5776 Code,
5777 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5778 ASTContext &ASTCtx) {
5779 Environment Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p").fork();
5780
5781 auto &JoinTrueTrue =
5782 getValueForDecl<BoolValue>(ASTCtx, Env, Name: "JoinTrueTrue");
5783 // FIXME: This test documents the current behavior, namely that we
5784 // don't actually use the constant result of the `ConstantExpr` and
5785 // instead treat it like a normal function call.
5786 EXPECT_EQ(JoinTrueTrue.formula().kind(), Formula::Kind::AtomRef);
5787 // EXPECT_TRUE(JoinTrueTrue.formula().literal());
5788
5789 auto &JoinTrueFalse =
5790 getValueForDecl<BoolValue>(ASTCtx, Env, Name: "JoinTrueFalse");
5791 EXPECT_EQ(JoinTrueFalse.formula().kind(), Formula::Kind::AtomRef);
5792 },
5793 Std: LangStandard::lang_cxx20);
5794}
5795
5796TEST(TransferTest, IfStmtBranchExtendsFlowCondition) {
5797 std::string Code = R"(
5798 void target(bool Foo) {
5799 if (Foo) {
5800 (void)0;
5801 // [[if_then]]
5802 } else {
5803 (void)0;
5804 // [[if_else]]
5805 }
5806 }
5807 )";
5808 runDataflow(
5809 Code,
5810 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5811 ASTContext &ASTCtx) {
5812 ASSERT_THAT(Results.keys(), UnorderedElementsAre("if_then", "if_else"));
5813 const Environment &ThenEnv =
5814 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "if_then");
5815 const Environment &ElseEnv =
5816 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "if_else");
5817
5818 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
5819 ASSERT_THAT(FooDecl, NotNull());
5820
5821 auto &ThenFooVal= getFormula(D: *FooDecl, Env: ThenEnv);
5822 EXPECT_TRUE(ThenEnv.proves(ThenFooVal));
5823
5824 auto &ElseFooVal = getFormula(D: *FooDecl, Env: ElseEnv);
5825 EXPECT_TRUE(ElseEnv.proves(ElseEnv.arena().makeNot(ElseFooVal)));
5826 });
5827}
5828
5829TEST(TransferTest, WhileStmtBranchExtendsFlowCondition) {
5830 std::string Code = R"(
5831 void target(bool Foo) {
5832 while (Foo) {
5833 (void)0;
5834 // [[loop_body]]
5835 }
5836 (void)0;
5837 // [[after_loop]]
5838 }
5839 )";
5840 runDataflow(
5841 Code,
5842 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5843 ASTContext &ASTCtx) {
5844 ASSERT_THAT(Results.keys(),
5845 UnorderedElementsAre("loop_body", "after_loop"));
5846 const Environment &LoopBodyEnv =
5847 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "loop_body");
5848 const Environment &AfterLoopEnv =
5849 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "after_loop");
5850
5851 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
5852 ASSERT_THAT(FooDecl, NotNull());
5853
5854 auto &LoopBodyFooVal = getFormula(D: *FooDecl, Env: LoopBodyEnv);
5855 EXPECT_TRUE(LoopBodyEnv.proves(LoopBodyFooVal));
5856
5857 auto &AfterLoopFooVal = getFormula(D: *FooDecl, Env: AfterLoopEnv);
5858 EXPECT_TRUE(
5859 AfterLoopEnv.proves(AfterLoopEnv.arena().makeNot(AfterLoopFooVal)));
5860 });
5861}
5862
5863TEST(TransferTest, DoWhileStmtBranchExtendsFlowCondition) {
5864 std::string Code = R"(
5865 void target(bool Foo) {
5866 bool Bar = true;
5867 do {
5868 (void)0;
5869 // [[loop_body]]
5870 Bar = false;
5871 } while (Foo);
5872 (void)0;
5873 // [[after_loop]]
5874 }
5875 )";
5876 runDataflow(
5877 Code,
5878 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5879 ASTContext &ASTCtx) {
5880 ASSERT_THAT(Results.keys(),
5881 UnorderedElementsAre("loop_body", "after_loop"));
5882 const Environment &LoopBodyEnv =
5883 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "loop_body");
5884 const Environment &AfterLoopEnv =
5885 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "after_loop");
5886 auto &A = AfterLoopEnv.arena();
5887
5888 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
5889 ASSERT_THAT(FooDecl, NotNull());
5890
5891 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
5892 ASSERT_THAT(BarDecl, NotNull());
5893
5894 auto &LoopBodyFooVal= getFormula(D: *FooDecl, Env: LoopBodyEnv);
5895 auto &LoopBodyBarVal = getFormula(D: *BarDecl, Env: LoopBodyEnv);
5896 EXPECT_TRUE(
5897 LoopBodyEnv.proves(A.makeOr(LoopBodyBarVal, LoopBodyFooVal)));
5898
5899 auto &AfterLoopFooVal = getFormula(D: *FooDecl, Env: AfterLoopEnv);
5900 auto &AfterLoopBarVal = getFormula(D: *BarDecl, Env: AfterLoopEnv);
5901 EXPECT_TRUE(AfterLoopEnv.proves(A.makeNot(AfterLoopFooVal)));
5902 EXPECT_TRUE(AfterLoopEnv.proves(A.makeNot(AfterLoopBarVal)));
5903 });
5904}
5905
5906TEST(TransferTest, ForStmtBranchExtendsFlowCondition) {
5907 std::string Code = R"(
5908 void target(bool Foo) {
5909 for (; Foo;) {
5910 (void)0;
5911 // [[loop_body]]
5912 }
5913 (void)0;
5914 // [[after_loop]]
5915 }
5916 )";
5917 runDataflow(
5918 Code,
5919 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5920 ASTContext &ASTCtx) {
5921 ASSERT_THAT(Results.keys(),
5922 UnorderedElementsAre("loop_body", "after_loop"));
5923 const Environment &LoopBodyEnv =
5924 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "loop_body");
5925 const Environment &AfterLoopEnv =
5926 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "after_loop");
5927
5928 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
5929 ASSERT_THAT(FooDecl, NotNull());
5930
5931 auto &LoopBodyFooVal= getFormula(D: *FooDecl, Env: LoopBodyEnv);
5932 EXPECT_TRUE(LoopBodyEnv.proves(LoopBodyFooVal));
5933
5934 auto &AfterLoopFooVal = getFormula(D: *FooDecl, Env: AfterLoopEnv);
5935 EXPECT_TRUE(
5936 AfterLoopEnv.proves(AfterLoopEnv.arena().makeNot(AfterLoopFooVal)));
5937 });
5938}
5939
5940TEST(TransferTest, ForStmtBranchWithoutConditionDoesNotExtendFlowCondition) {
5941 std::string Code = R"(
5942 void target(bool Foo) {
5943 for (;;) {
5944 (void)0;
5945 // [[loop_body]]
5946 }
5947 }
5948 )";
5949 runDataflow(
5950 Code,
5951 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5952 ASTContext &ASTCtx) {
5953 ASSERT_THAT(Results.keys(), UnorderedElementsAre("loop_body"));
5954 const Environment &LoopBodyEnv =
5955 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "loop_body");
5956
5957 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
5958 ASSERT_THAT(FooDecl, NotNull());
5959
5960 auto &LoopBodyFooVal= getFormula(D: *FooDecl, Env: LoopBodyEnv);
5961 EXPECT_FALSE(LoopBodyEnv.proves(LoopBodyFooVal));
5962 });
5963}
5964
5965TEST(TransferTest, ContextSensitiveOptionDisabled) {
5966 std::string Code = R"(
5967 bool GiveBool();
5968 void SetBool(bool &Var) { Var = true; }
5969
5970 void target() {
5971 bool Foo = GiveBool();
5972 SetBool(Foo);
5973 // [[p]]
5974 }
5975 )";
5976 runDataflow(
5977 Code,
5978 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
5979 ASTContext &ASTCtx) {
5980 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
5981 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
5982
5983 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
5984 ASSERT_THAT(FooDecl, NotNull());
5985
5986 auto &FooVal = getFormula(D: *FooDecl, Env);
5987 EXPECT_FALSE(Env.proves(FooVal));
5988 EXPECT_FALSE(Env.proves(Env.arena().makeNot(FooVal)));
5989 },
5990 Options: {.BuiltinOpts: BuiltinOptions{/*.ContextSensitiveOpts=*/std::nullopt}});
5991}
5992
5993TEST(TransferTest, ContextSensitiveReturnReference) {
5994 std::string Code = R"(
5995 class S {};
5996 S& target(bool b, S &s) {
5997 return s;
5998 // [[p]]
5999 }
6000 )";
6001 runDataflow(
6002 Code,
6003 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6004 ASTContext &ASTCtx) {
6005 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6006
6007 const ValueDecl *SDecl = findValueDecl(ASTCtx, Name: "s");
6008 ASSERT_THAT(SDecl, NotNull());
6009
6010 auto *SLoc = Env.getStorageLocation(D: *SDecl);
6011 ASSERT_THAT(SLoc, NotNull());
6012
6013 ASSERT_THAT(Env.getReturnStorageLocation(), Eq(SLoc));
6014 },
6015 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6016}
6017
6018// This test is a regression test, based on a real crash.
6019TEST(TransferTest, ContextSensitiveReturnReferenceWithConditionalOperator) {
6020 std::string Code = R"(
6021 class S {};
6022 S& target(bool b, S &s) {
6023 return b ? s : s;
6024 // [[p]]
6025 }
6026 )";
6027 runDataflow(
6028 Code,
6029 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6030 ASTContext &ASTCtx) {
6031 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6032 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6033
6034 const ValueDecl *SDecl = findValueDecl(ASTCtx, Name: "s");
6035 ASSERT_THAT(SDecl, NotNull());
6036
6037 auto *SLoc = Env.getStorageLocation(D: *SDecl);
6038 EXPECT_THAT(SLoc, NotNull());
6039
6040 auto *Loc = Env.getReturnStorageLocation();
6041 EXPECT_THAT(Loc, NotNull());
6042
6043 EXPECT_EQ(Loc, SLoc);
6044 },
6045 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6046}
6047
6048TEST(TransferTest, ContextSensitiveReturnOneOfTwoReferences) {
6049 std::string Code = R"(
6050 class S {};
6051 S &callee(bool b, S &s1_parm, S &s2_parm) {
6052 if (b)
6053 return s1_parm;
6054 else
6055 return s2_parm;
6056 }
6057 void target(bool b) {
6058 S s1;
6059 S s2;
6060 S &return_s1 = s1;
6061 S &return_s2 = s2;
6062 S &return_dont_know = callee(b, s1, s2);
6063 // [[p]]
6064 }
6065 )";
6066 runDataflow(
6067 Code,
6068 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6069 ASTContext &ASTCtx) {
6070 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6071
6072 const ValueDecl *S1 = findValueDecl(ASTCtx, Name: "s1");
6073 ASSERT_THAT(S1, NotNull());
6074 const ValueDecl *S2 = findValueDecl(ASTCtx, Name: "s2");
6075 ASSERT_THAT(S2, NotNull());
6076 const ValueDecl *ReturnS1 = findValueDecl(ASTCtx, Name: "return_s1");
6077 ASSERT_THAT(ReturnS1, NotNull());
6078 const ValueDecl *ReturnS2 = findValueDecl(ASTCtx, Name: "return_s2");
6079 ASSERT_THAT(ReturnS2, NotNull());
6080 const ValueDecl *ReturnDontKnow =
6081 findValueDecl(ASTCtx, Name: "return_dont_know");
6082 ASSERT_THAT(ReturnDontKnow, NotNull());
6083
6084 StorageLocation *S1Loc = Env.getStorageLocation(D: *S1);
6085 StorageLocation *S2Loc = Env.getStorageLocation(D: *S2);
6086
6087 EXPECT_THAT(Env.getStorageLocation(*ReturnS1), Eq(S1Loc));
6088 EXPECT_THAT(Env.getStorageLocation(*ReturnS2), Eq(S2Loc));
6089
6090 // In the case where we don't have a consistent storage location for
6091 // the return value, the framework creates a new storage location, which
6092 // should be different from the storage locations of `s1` and `s2`.
6093 EXPECT_THAT(Env.getStorageLocation(*ReturnDontKnow), Ne(S1Loc));
6094 EXPECT_THAT(Env.getStorageLocation(*ReturnDontKnow), Ne(S2Loc));
6095 },
6096 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6097}
6098
6099TEST(TransferTest, ContextSensitiveDepthZero) {
6100 std::string Code = R"(
6101 bool GiveBool();
6102 void SetBool(bool &Var) { Var = true; }
6103
6104 void target() {
6105 bool Foo = GiveBool();
6106 SetBool(Foo);
6107 // [[p]]
6108 }
6109 )";
6110 runDataflow(
6111 Code,
6112 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6113 ASTContext &ASTCtx) {
6114 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6115 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6116
6117 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6118 ASSERT_THAT(FooDecl, NotNull());
6119
6120 auto &FooVal = getFormula(D: *FooDecl, Env);
6121 EXPECT_FALSE(Env.proves(FooVal));
6122 EXPECT_FALSE(Env.proves(Env.arena().makeNot(FooVal)));
6123 },
6124 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{/*.Depth=*/0}}});
6125}
6126
6127TEST(TransferTest, ContextSensitiveSetTrue) {
6128 std::string Code = R"(
6129 bool GiveBool();
6130 void SetBool(bool &Var) { Var = true; }
6131
6132 void target() {
6133 bool Foo = GiveBool();
6134 SetBool(Foo);
6135 // [[p]]
6136 }
6137 )";
6138 runDataflow(
6139 Code,
6140 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6141 ASTContext &ASTCtx) {
6142 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6143 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6144
6145 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6146 ASSERT_THAT(FooDecl, NotNull());
6147
6148 auto &FooVal = getFormula(D: *FooDecl, Env);
6149 EXPECT_TRUE(Env.proves(FooVal));
6150 },
6151 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6152}
6153
6154TEST(TransferTest, ContextSensitiveSetFalse) {
6155 std::string Code = R"(
6156 bool GiveBool();
6157 void SetBool(bool &Var) { Var = false; }
6158
6159 void target() {
6160 bool Foo = GiveBool();
6161 SetBool(Foo);
6162 // [[p]]
6163 }
6164 )";
6165 runDataflow(
6166 Code,
6167 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6168 ASTContext &ASTCtx) {
6169 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6170 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6171
6172 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6173 ASSERT_THAT(FooDecl, NotNull());
6174
6175 auto &FooVal = getFormula(D: *FooDecl, Env);
6176 EXPECT_TRUE(Env.proves(Env.arena().makeNot(FooVal)));
6177 },
6178 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6179}
6180
6181TEST(TransferTest, ContextSensitiveSetBothTrueAndFalse) {
6182 std::string Code = R"(
6183 bool GiveBool();
6184 void SetBool(bool &Var, bool Val) { Var = Val; }
6185
6186 void target() {
6187 bool Foo = GiveBool();
6188 bool Bar = GiveBool();
6189 SetBool(Foo, true);
6190 SetBool(Bar, false);
6191 // [[p]]
6192 }
6193 )";
6194 runDataflow(
6195 Code,
6196 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6197 ASTContext &ASTCtx) {
6198 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6199 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6200 auto &A = Env.arena();
6201
6202 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6203 ASSERT_THAT(FooDecl, NotNull());
6204
6205 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
6206 ASSERT_THAT(BarDecl, NotNull());
6207
6208 auto &FooVal = getFormula(D: *FooDecl, Env);
6209 EXPECT_TRUE(Env.proves(FooVal));
6210 EXPECT_FALSE(Env.proves(A.makeNot(FooVal)));
6211
6212 auto &BarVal = getFormula(D: *BarDecl, Env);
6213 EXPECT_FALSE(Env.proves(BarVal));
6214 EXPECT_TRUE(Env.proves(A.makeNot(BarVal)));
6215 },
6216 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6217}
6218
6219TEST(TransferTest, ContextSensitiveSetTwoLayersDepthOne) {
6220 std::string Code = R"(
6221 bool GiveBool();
6222 void SetBool1(bool &Var) { Var = true; }
6223 void SetBool2(bool &Var) { SetBool1(Var); }
6224
6225 void target() {
6226 bool Foo = GiveBool();
6227 SetBool2(Foo);
6228 // [[p]]
6229 }
6230 )";
6231 runDataflow(
6232 Code,
6233 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6234 ASTContext &ASTCtx) {
6235 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6236 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6237
6238 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6239 ASSERT_THAT(FooDecl, NotNull());
6240
6241 auto &FooVal = getFormula(D: *FooDecl, Env);
6242 EXPECT_FALSE(Env.proves(FooVal));
6243 EXPECT_FALSE(Env.proves(Env.arena().makeNot(FooVal)));
6244 },
6245 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{/*.Depth=*/1}}});
6246}
6247
6248TEST(TransferTest, ContextSensitiveSetTwoLayersDepthTwo) {
6249 std::string Code = R"(
6250 bool GiveBool();
6251 void SetBool1(bool &Var) { Var = true; }
6252 void SetBool2(bool &Var) { SetBool1(Var); }
6253
6254 void target() {
6255 bool Foo = GiveBool();
6256 SetBool2(Foo);
6257 // [[p]]
6258 }
6259 )";
6260 runDataflow(
6261 Code,
6262 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6263 ASTContext &ASTCtx) {
6264 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6265 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6266
6267 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6268 ASSERT_THAT(FooDecl, NotNull());
6269
6270 auto &FooVal = getFormula(D: *FooDecl, Env);
6271 EXPECT_TRUE(Env.proves(FooVal));
6272 },
6273 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{/*.Depth=*/2}}});
6274}
6275
6276TEST(TransferTest, ContextSensitiveSetThreeLayersDepthTwo) {
6277 std::string Code = R"(
6278 bool GiveBool();
6279 void SetBool1(bool &Var) { Var = true; }
6280 void SetBool2(bool &Var) { SetBool1(Var); }
6281 void SetBool3(bool &Var) { SetBool2(Var); }
6282
6283 void target() {
6284 bool Foo = GiveBool();
6285 SetBool3(Foo);
6286 // [[p]]
6287 }
6288 )";
6289 runDataflow(
6290 Code,
6291 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6292 ASTContext &ASTCtx) {
6293 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6294 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6295
6296 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6297 ASSERT_THAT(FooDecl, NotNull());
6298
6299 auto &FooVal = getFormula(D: *FooDecl, Env);
6300 EXPECT_FALSE(Env.proves(FooVal));
6301 EXPECT_FALSE(Env.proves(Env.arena().makeNot(FooVal)));
6302 },
6303 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{/*.Depth=*/2}}});
6304}
6305
6306TEST(TransferTest, ContextSensitiveSetThreeLayersDepthThree) {
6307 std::string Code = R"(
6308 bool GiveBool();
6309 void SetBool1(bool &Var) { Var = true; }
6310 void SetBool2(bool &Var) { SetBool1(Var); }
6311 void SetBool3(bool &Var) { SetBool2(Var); }
6312
6313 void target() {
6314 bool Foo = GiveBool();
6315 SetBool3(Foo);
6316 // [[p]]
6317 }
6318 )";
6319 runDataflow(
6320 Code,
6321 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6322 ASTContext &ASTCtx) {
6323 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6324 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6325
6326 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6327 ASSERT_THAT(FooDecl, NotNull());
6328
6329 auto &FooVal = getFormula(D: *FooDecl, Env);
6330 EXPECT_TRUE(Env.proves(FooVal));
6331 },
6332 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{/*.Depth=*/3}}});
6333}
6334
6335TEST(TransferTest, ContextSensitiveMutualRecursion) {
6336 std::string Code = R"(
6337 bool Pong(bool X, bool Y);
6338
6339 bool Ping(bool X, bool Y) {
6340 if (X) {
6341 return Y;
6342 } else {
6343 return Pong(!X, Y);
6344 }
6345 }
6346
6347 bool Pong(bool X, bool Y) {
6348 if (Y) {
6349 return X;
6350 } else {
6351 return Ping(X, !Y);
6352 }
6353 }
6354
6355 void target() {
6356 bool Foo = Ping(false, false);
6357 // [[p]]
6358 }
6359 )";
6360 runDataflow(
6361 Code,
6362 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6363 ASTContext &ASTCtx) {
6364 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6365 // The analysis doesn't crash...
6366 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6367
6368 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6369 ASSERT_THAT(FooDecl, NotNull());
6370
6371 auto &FooVal = getFormula(D: *FooDecl, Env);
6372 // ... but it also can't prove anything here.
6373 EXPECT_FALSE(Env.proves(FooVal));
6374 EXPECT_FALSE(Env.proves(Env.arena().makeNot(FooVal)));
6375 },
6376 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{/*.Depth=*/4}}});
6377}
6378
6379TEST(TransferTest, ContextSensitiveSetMultipleLines) {
6380 std::string Code = R"(
6381 void SetBools(bool &Var1, bool &Var2) {
6382 Var1 = true;
6383 Var2 = false;
6384 }
6385
6386 void target() {
6387 bool Foo = false;
6388 bool Bar = true;
6389 SetBools(Foo, Bar);
6390 // [[p]]
6391 }
6392 )";
6393 runDataflow(
6394 Code,
6395 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6396 ASTContext &ASTCtx) {
6397 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6398 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6399
6400 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6401 ASSERT_THAT(FooDecl, NotNull());
6402
6403 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
6404 ASSERT_THAT(BarDecl, NotNull());
6405
6406 auto &FooVal = getFormula(D: *FooDecl, Env);
6407 EXPECT_TRUE(Env.proves(FooVal));
6408 EXPECT_FALSE(Env.proves(Env.arena().makeNot(FooVal)));
6409
6410 auto &BarVal = getFormula(D: *BarDecl, Env);
6411 EXPECT_FALSE(Env.proves(BarVal));
6412 EXPECT_TRUE(Env.proves(Env.arena().makeNot(BarVal)));
6413 },
6414 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6415}
6416
6417TEST(TransferTest, ContextSensitiveSetMultipleBlocks) {
6418 std::string Code = R"(
6419 void IfCond(bool Cond, bool &Then, bool &Else) {
6420 if (Cond) {
6421 Then = true;
6422 } else {
6423 Else = true;
6424 }
6425 }
6426
6427 void target() {
6428 bool Foo = false;
6429 bool Bar = false;
6430 bool Baz = false;
6431 IfCond(Foo, Bar, Baz);
6432 // [[p]]
6433 }
6434 )";
6435 runDataflow(
6436 Code,
6437 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6438 ASTContext &ASTCtx) {
6439 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6440 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6441
6442 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
6443 ASSERT_THAT(BarDecl, NotNull());
6444
6445 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
6446 ASSERT_THAT(BazDecl, NotNull());
6447
6448 auto &BarVal = getFormula(D: *BarDecl, Env);
6449 EXPECT_FALSE(Env.proves(BarVal));
6450 EXPECT_TRUE(Env.proves(Env.arena().makeNot(BarVal)));
6451
6452 auto &BazVal = getFormula(D: *BazDecl, Env);
6453 EXPECT_TRUE(Env.proves(BazVal));
6454 EXPECT_FALSE(Env.proves(Env.arena().makeNot(BazVal)));
6455 },
6456 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6457}
6458
6459TEST(TransferTest, ContextSensitiveReturnVoid) {
6460 std::string Code = R"(
6461 void Noop() { return; }
6462
6463 void target() {
6464 Noop();
6465 // [[p]]
6466 }
6467 )";
6468 runDataflow(
6469 Code,
6470 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6471 ASTContext &ASTCtx) {
6472 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6473 // This just tests that the analysis doesn't crash.
6474 },
6475 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6476}
6477
6478TEST(TransferTest, ContextSensitiveReturnTrue) {
6479 std::string Code = R"(
6480 bool GiveBool() { return true; }
6481
6482 void target() {
6483 bool Foo = GiveBool();
6484 // [[p]]
6485 }
6486 )";
6487 runDataflow(
6488 Code,
6489 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6490 ASTContext &ASTCtx) {
6491 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6492 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6493
6494 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6495 ASSERT_THAT(FooDecl, NotNull());
6496
6497 auto &FooVal = getFormula(D: *FooDecl, Env);
6498 EXPECT_TRUE(Env.proves(FooVal));
6499 },
6500 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6501}
6502
6503TEST(TransferTest, ContextSensitiveReturnFalse) {
6504 std::string Code = R"(
6505 bool GiveBool() { return false; }
6506
6507 void target() {
6508 bool Foo = GiveBool();
6509 // [[p]]
6510 }
6511 )";
6512 runDataflow(
6513 Code,
6514 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6515 ASTContext &ASTCtx) {
6516 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6517 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6518
6519 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6520 ASSERT_THAT(FooDecl, NotNull());
6521
6522 auto &FooVal = getFormula(D: *FooDecl, Env);
6523 EXPECT_TRUE(Env.proves(Env.arena().makeNot(FooVal)));
6524 },
6525 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6526}
6527
6528TEST(TransferTest, ContextSensitiveReturnArg) {
6529 std::string Code = R"(
6530 bool GiveBool();
6531 bool GiveBack(bool Arg) { return Arg; }
6532
6533 void target() {
6534 bool Foo = GiveBool();
6535 bool Bar = GiveBack(Foo);
6536 bool Baz = Foo == Bar;
6537 // [[p]]
6538 }
6539 )";
6540 runDataflow(
6541 Code,
6542 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6543 ASTContext &ASTCtx) {
6544 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6545 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6546
6547 const ValueDecl *BazDecl = findValueDecl(ASTCtx, Name: "Baz");
6548 ASSERT_THAT(BazDecl, NotNull());
6549
6550 auto &BazVal = getFormula(D: *BazDecl, Env);
6551 EXPECT_TRUE(Env.proves(BazVal));
6552 },
6553 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6554}
6555
6556TEST(TransferTest, ContextSensitiveReturnInt) {
6557 std::string Code = R"(
6558 int identity(int x) { return x; }
6559
6560 void target() {
6561 int y = identity(42);
6562 // [[p]]
6563 }
6564 )";
6565 runDataflow(
6566 Code,
6567 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6568 ASTContext &ASTCtx) {
6569 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6570 // This just tests that the analysis doesn't crash.
6571 },
6572 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6573}
6574
6575TEST(TransferTest, ContextSensitiveReturnRecord) {
6576 std::string Code = R"(
6577 struct S {
6578 bool B;
6579 };
6580
6581 S makeS(bool BVal) { return {BVal}; }
6582
6583 void target() {
6584 S FalseS = makeS(false);
6585 S TrueS = makeS(true);
6586 // [[p]]
6587 }
6588 )";
6589 runDataflow(
6590 Code,
6591 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6592 ASTContext &ASTCtx) {
6593 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6594
6595 auto &FalseSLoc =
6596 getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "FalseS");
6597 auto &TrueSLoc =
6598 getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "TrueS");
6599
6600 EXPECT_EQ(getFieldValue(&FalseSLoc, "B", ASTCtx, Env),
6601 &Env.getBoolLiteralValue(false));
6602 EXPECT_EQ(getFieldValue(&TrueSLoc, "B", ASTCtx, Env),
6603 &Env.getBoolLiteralValue(true));
6604 },
6605 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6606}
6607
6608TEST(TransferTest, ContextSensitiveReturnSelfReferentialRecord) {
6609 std::string Code = R"(
6610 struct S {
6611 S() { self = this; }
6612 S *self;
6613 };
6614
6615 S makeS() {
6616 // RVO guarantees that this will be constructed directly into `MyS`.
6617 return S();
6618 }
6619
6620 void target() {
6621 S MyS = makeS();
6622 // [[p]]
6623 }
6624 )";
6625 runDataflow(
6626 Code,
6627 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6628 ASTContext &ASTCtx) {
6629 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6630
6631 auto &MySLoc = getLocForDecl<RecordStorageLocation>(ASTCtx, Env, Name: "MyS");
6632
6633 auto *SelfVal =
6634 cast<PointerValue>(Val: getFieldValue(Loc: &MySLoc, Name: "self", ASTCtx, Env));
6635 EXPECT_EQ(&SelfVal->getPointeeLoc(), &MySLoc);
6636 },
6637 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6638}
6639
6640TEST(TransferTest, ContextSensitiveMethodLiteral) {
6641 std::string Code = R"(
6642 class MyClass {
6643 public:
6644 bool giveBool() { return true; }
6645 };
6646
6647 void target() {
6648 MyClass MyObj;
6649 bool Foo = MyObj.giveBool();
6650 // [[p]]
6651 }
6652 )";
6653 runDataflow(
6654 Code,
6655 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6656 ASTContext &ASTCtx) {
6657 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6658 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6659
6660 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6661 ASSERT_THAT(FooDecl, NotNull());
6662
6663 auto &FooVal = getFormula(D: *FooDecl, Env);
6664 EXPECT_TRUE(Env.proves(FooVal));
6665 },
6666 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6667}
6668
6669TEST(TransferTest, ContextSensitiveMethodGetter) {
6670 std::string Code = R"(
6671 class MyClass {
6672 public:
6673 bool getField() { return Field; }
6674
6675 bool Field;
6676 };
6677
6678 void target() {
6679 MyClass MyObj;
6680 MyObj.Field = true;
6681 bool Foo = MyObj.getField();
6682 // [[p]]
6683 }
6684 )";
6685 runDataflow(
6686 Code,
6687 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6688 ASTContext &ASTCtx) {
6689 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6690 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6691
6692 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6693 ASSERT_THAT(FooDecl, NotNull());
6694
6695 auto &FooVal = getFormula(D: *FooDecl, Env);
6696 EXPECT_TRUE(Env.proves(FooVal));
6697 },
6698 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6699}
6700
6701TEST(TransferTest, ContextSensitiveMethodSetter) {
6702 std::string Code = R"(
6703 class MyClass {
6704 public:
6705 void setField(bool Val) { Field = Val; }
6706
6707 bool Field;
6708 };
6709
6710 void target() {
6711 MyClass MyObj;
6712 MyObj.setField(true);
6713 bool Foo = MyObj.Field;
6714 // [[p]]
6715 }
6716 )";
6717 runDataflow(
6718 Code,
6719 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6720 ASTContext &ASTCtx) {
6721 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6722 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6723
6724 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6725 ASSERT_THAT(FooDecl, NotNull());
6726
6727 auto &FooVal = getFormula(D: *FooDecl, Env);
6728 EXPECT_TRUE(Env.proves(FooVal));
6729 },
6730 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6731}
6732
6733TEST(TransferTest, ContextSensitiveMethodGetterAndSetter) {
6734 std::string Code = R"(
6735 class MyClass {
6736 public:
6737 bool getField() { return Field; }
6738 void setField(bool Val) { Field = Val; }
6739
6740 private:
6741 bool Field;
6742 };
6743
6744 void target() {
6745 MyClass MyObj;
6746 MyObj.setField(true);
6747 bool Foo = MyObj.getField();
6748 // [[p]]
6749 }
6750 )";
6751 runDataflow(
6752 Code,
6753 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6754 ASTContext &ASTCtx) {
6755 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6756 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6757
6758 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6759 ASSERT_THAT(FooDecl, NotNull());
6760
6761 auto &FooVal = getFormula(D: *FooDecl, Env);
6762 EXPECT_TRUE(Env.proves(FooVal));
6763 },
6764 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6765}
6766
6767
6768TEST(TransferTest, ContextSensitiveMethodTwoLayersVoid) {
6769 std::string Code = R"(
6770 class MyClass {
6771 public:
6772 void Inner() { MyField = true; }
6773 void Outer() { Inner(); }
6774
6775 bool MyField;
6776 };
6777
6778 void target() {
6779 MyClass MyObj;
6780 MyObj.Outer();
6781 bool Foo = MyObj.MyField;
6782 // [[p]]
6783 }
6784 )";
6785 runDataflow(
6786 Code,
6787 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6788 ASTContext &ASTCtx) {
6789 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6790 ;
6791 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6792
6793 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6794 ASSERT_THAT(FooDecl, NotNull());
6795
6796 auto &FooVal = getFormula(D: *FooDecl, Env);
6797 EXPECT_TRUE(Env.proves(FooVal));
6798 },
6799 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6800}
6801
6802TEST(TransferTest, ContextSensitiveMethodTwoLayersReturn) {
6803 std::string Code = R"(
6804 class MyClass {
6805 public:
6806 bool Inner() { return MyField; }
6807 bool Outer() { return Inner(); }
6808
6809 bool MyField;
6810 };
6811
6812 void target() {
6813 MyClass MyObj;
6814 MyObj.MyField = true;
6815 bool Foo = MyObj.Outer();
6816 // [[p]]
6817 }
6818 )";
6819 runDataflow(
6820 Code,
6821 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6822 ASTContext &ASTCtx) {
6823 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6824 ;
6825 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6826
6827 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6828 ASSERT_THAT(FooDecl, NotNull());
6829
6830 auto &FooVal = getFormula(D: *FooDecl, Env);
6831 EXPECT_TRUE(Env.proves(FooVal));
6832 },
6833 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6834}
6835
6836TEST(TransferTest, ContextSensitiveConstructorBody) {
6837 std::string Code = R"(
6838 class MyClass {
6839 public:
6840 MyClass() { MyField = true; }
6841
6842 bool MyField;
6843 };
6844
6845 void target() {
6846 MyClass MyObj;
6847 bool Foo = MyObj.MyField;
6848 // [[p]]
6849 }
6850 )";
6851 runDataflow(
6852 Code,
6853 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6854 ASTContext &ASTCtx) {
6855 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6856 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6857
6858 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6859 ASSERT_THAT(FooDecl, NotNull());
6860
6861 auto &FooVal = getFormula(D: *FooDecl, Env);
6862 EXPECT_TRUE(Env.proves(FooVal));
6863 },
6864 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6865}
6866
6867TEST(TransferTest, ContextSensitiveConstructorInitializer) {
6868 std::string Code = R"(
6869 class MyClass {
6870 public:
6871 MyClass() : MyField(true) {}
6872
6873 bool MyField;
6874 };
6875
6876 void target() {
6877 MyClass MyObj;
6878 bool Foo = MyObj.MyField;
6879 // [[p]]
6880 }
6881 )";
6882 runDataflow(
6883 Code,
6884 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6885 ASTContext &ASTCtx) {
6886 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6887 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6888
6889 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6890 ASSERT_THAT(FooDecl, NotNull());
6891
6892 auto &FooVal = getFormula(D: *FooDecl, Env);
6893 EXPECT_TRUE(Env.proves(FooVal));
6894 },
6895 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6896}
6897
6898TEST(TransferTest, ContextSensitiveConstructorDefault) {
6899 std::string Code = R"(
6900 class MyClass {
6901 public:
6902 MyClass() = default;
6903
6904 bool MyField = true;
6905 };
6906
6907 void target() {
6908 MyClass MyObj;
6909 bool Foo = MyObj.MyField;
6910 // [[p]]
6911 }
6912 )";
6913 runDataflow(
6914 Code,
6915 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6916 ASTContext &ASTCtx) {
6917 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6918 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6919
6920 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
6921 ASSERT_THAT(FooDecl, NotNull());
6922
6923 auto &FooVal = getFormula(D: *FooDecl, Env);
6924 EXPECT_TRUE(Env.proves(FooVal));
6925 },
6926 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6927}
6928
6929TEST(TransferTest, ContextSensitiveSelfReferentialClass) {
6930 // Test that the `this` pointer seen in the constructor has the same value
6931 // as the address of the variable the object is constructed into.
6932 std::string Code = R"(
6933 class MyClass {
6934 public:
6935 MyClass() : Self(this) {}
6936 MyClass *Self;
6937 };
6938
6939 void target() {
6940 MyClass MyObj;
6941 MyClass *SelfPtr = MyObj.Self;
6942 // [[p]]
6943 }
6944 )";
6945 runDataflow(
6946 Code,
6947 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6948 ASTContext &ASTCtx) {
6949 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
6950
6951 const ValueDecl *MyObjDecl = findValueDecl(ASTCtx, Name: "MyObj");
6952 ASSERT_THAT(MyObjDecl, NotNull());
6953
6954 const ValueDecl *SelfDecl = findValueDecl(ASTCtx, Name: "SelfPtr");
6955 ASSERT_THAT(SelfDecl, NotNull());
6956
6957 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
6958 auto &SelfVal = *cast<PointerValue>(Val: Env.getValue(D: *SelfDecl));
6959 EXPECT_EQ(Env.getStorageLocation(*MyObjDecl), &SelfVal.getPointeeLoc());
6960 },
6961 Options: {.BuiltinOpts: BuiltinOptions{.ContextSensitiveOpts: ContextSensitiveOptions{}}});
6962}
6963
6964TEST(TransferTest, UnnamedBitfieldInitializer) {
6965 std::string Code = R"(
6966 struct B {};
6967 struct A {
6968 unsigned a;
6969 unsigned : 4;
6970 unsigned c;
6971 B b;
6972 };
6973 void target() {
6974 A a = {};
6975 A test = a;
6976 (void)test.c;
6977 }
6978 )";
6979 runDataflow(
6980 Code,
6981 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
6982 ASTContext &ASTCtx) {
6983 // This doesn't need a body because this test was crashing the framework
6984 // before handling correctly Unnamed bitfields in `InitListExpr`.
6985 });
6986}
6987
6988// Repro for a crash that used to occur with chained short-circuiting logical
6989// operators.
6990TEST(TransferTest, ChainedLogicalOps) {
6991 std::string Code = R"(
6992 bool target() {
6993 bool b = true || false || false || false;
6994 // [[p]]
6995 return b;
6996 }
6997 )";
6998 runDataflow(
6999 Code,
7000 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7001 ASTContext &ASTCtx) {
7002 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
7003 auto &B = getValueForDecl<BoolValue>(ASTCtx, Env, Name: "b").formula();
7004 EXPECT_TRUE(Env.proves(B));
7005 });
7006}
7007
7008// Repro for a crash that used to occur when we call a `noreturn` function
7009// within one of the operands of a `&&` or `||` operator.
7010TEST(TransferTest, NoReturnFunctionInsideShortCircuitedBooleanOp) {
7011 std::string Code = R"(
7012 __attribute__((noreturn)) int doesnt_return();
7013 bool some_condition();
7014 void target(bool b1, bool b2) {
7015 // Neither of these should crash. In addition, if we don't terminate the
7016 // program, we know that the operators need to trigger the short-circuit
7017 // logic, so `NoreturnOnRhsOfAnd` will be false and `NoreturnOnRhsOfOr`
7018 // will be true.
7019 bool NoreturnOnRhsOfAnd = b1 && doesnt_return() > 0;
7020 bool NoreturnOnRhsOfOr = b2 || doesnt_return() > 0;
7021
7022 // Calling a `noreturn` function on the LHS of an `&&` or `||` makes the
7023 // entire expression unreachable. So we know that in both of the following
7024 // cases, if `target()` terminates, the `else` branch was taken.
7025 bool NoreturnOnLhsMakesAndUnreachable = false;
7026 if (some_condition())
7027 doesnt_return() > 0 && some_condition();
7028 else
7029 NoreturnOnLhsMakesAndUnreachable = true;
7030
7031 bool NoreturnOnLhsMakesOrUnreachable = false;
7032 if (some_condition())
7033 doesnt_return() > 0 || some_condition();
7034 else
7035 NoreturnOnLhsMakesOrUnreachable = true;
7036
7037 // [[p]]
7038 }
7039 )";
7040 runDataflow(
7041 Code,
7042 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7043 ASTContext &ASTCtx) {
7044 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
7045 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
7046 auto &A = Env.arena();
7047
7048 // Check that [[p]] is reachable with a non-false flow condition.
7049 EXPECT_FALSE(Env.proves(A.makeLiteral(false)));
7050
7051 auto &B1 = getValueForDecl<BoolValue>(ASTCtx, Env, Name: "b1").formula();
7052 EXPECT_TRUE(Env.proves(A.makeNot(B1)));
7053
7054 auto &NoreturnOnRhsOfAnd =
7055 getValueForDecl<BoolValue>(ASTCtx, Env, Name: "NoreturnOnRhsOfAnd").formula();
7056 EXPECT_TRUE(Env.proves(A.makeNot(NoreturnOnRhsOfAnd)));
7057
7058 auto &B2 = getValueForDecl<BoolValue>(ASTCtx, Env, Name: "b2").formula();
7059 EXPECT_TRUE(Env.proves(B2));
7060
7061 auto &NoreturnOnRhsOfOr =
7062 getValueForDecl<BoolValue>(ASTCtx, Env, Name: "NoreturnOnRhsOfOr")
7063 .formula();
7064 EXPECT_TRUE(Env.proves(NoreturnOnRhsOfOr));
7065
7066 auto &NoreturnOnLhsMakesAndUnreachable = getValueForDecl<BoolValue>(
7067 ASTCtx, Env, Name: "NoreturnOnLhsMakesAndUnreachable").formula();
7068 EXPECT_TRUE(Env.proves(NoreturnOnLhsMakesAndUnreachable));
7069
7070 auto &NoreturnOnLhsMakesOrUnreachable = getValueForDecl<BoolValue>(
7071 ASTCtx, Env, Name: "NoreturnOnLhsMakesOrUnreachable").formula();
7072 EXPECT_TRUE(Env.proves(NoreturnOnLhsMakesOrUnreachable));
7073 });
7074}
7075
7076TEST(TransferTest, NewExpressions) {
7077 std::string Code = R"(
7078 void target() {
7079 int *p = new int(42);
7080 // [[after_new]]
7081 }
7082 )";
7083 runDataflow(
7084 Code,
7085 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7086 ASTContext &ASTCtx) {
7087 const Environment &Env =
7088 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "after_new");
7089
7090 auto &P = getValueForDecl<PointerValue>(ASTCtx, Env, Name: "p");
7091
7092 EXPECT_THAT(Env.getValue(P.getPointeeLoc()), NotNull());
7093 });
7094}
7095
7096TEST(TransferTest, NewExpressions_Structs) {
7097 std::string Code = R"(
7098 struct Inner {
7099 int InnerField;
7100 };
7101
7102 struct Outer {
7103 Inner OuterField;
7104 };
7105
7106 void target() {
7107 Outer *p = new Outer;
7108 // Access the fields to make sure the analysis actually generates children
7109 // for them in the `RecordStorageLocation`.
7110 p->OuterField.InnerField;
7111 // [[after_new]]
7112 }
7113 )";
7114 runDataflow(
7115 Code,
7116 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7117 ASTContext &ASTCtx) {
7118 const Environment &Env =
7119 getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "after_new");
7120
7121 const ValueDecl *OuterField = findValueDecl(ASTCtx, Name: "OuterField");
7122 const ValueDecl *InnerField = findValueDecl(ASTCtx, Name: "InnerField");
7123
7124 auto &P = getValueForDecl<PointerValue>(ASTCtx, Env, Name: "p");
7125
7126 auto &OuterLoc = cast<RecordStorageLocation>(Val&: P.getPointeeLoc());
7127 auto &OuterFieldLoc =
7128 *cast<RecordStorageLocation>(Val: OuterLoc.getChild(D: *OuterField));
7129 auto &InnerFieldLoc = *OuterFieldLoc.getChild(D: *InnerField);
7130
7131 EXPECT_THAT(Env.getValue(InnerFieldLoc), NotNull());
7132 });
7133}
7134
7135TEST(TransferTest, FunctionToPointerDecayHasValue) {
7136 std::string Code = R"(
7137 struct A { static void static_member_func(); };
7138 void target() {
7139 // To check that we're treating function-to-pointer decay correctly,
7140 // create two pointers, then verify they refer to the same storage
7141 // location.
7142 // We need to do the test this way because even if an initializer (in this
7143 // case, the function-to-pointer decay) does not create a value, we still
7144 // create a value for the variable.
7145 void (*non_member_p1)() = target;
7146 void (*non_member_p2)() = target;
7147
7148 // Do the same thing but for a static member function.
7149 void (*member_p1)() = A::static_member_func;
7150 void (*member_p2)() = A::static_member_func;
7151 // [[p]]
7152 }
7153 )";
7154 runDataflow(
7155 Code,
7156 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7157 ASTContext &ASTCtx) {
7158 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
7159
7160 auto &NonMemberP1 =
7161 getValueForDecl<PointerValue>(ASTCtx, Env, Name: "non_member_p1");
7162 auto &NonMemberP2 =
7163 getValueForDecl<PointerValue>(ASTCtx, Env, Name: "non_member_p2");
7164 EXPECT_EQ(&NonMemberP1.getPointeeLoc(), &NonMemberP2.getPointeeLoc());
7165
7166 auto &MemberP1 =
7167 getValueForDecl<PointerValue>(ASTCtx, Env, Name: "member_p1");
7168 auto &MemberP2 =
7169 getValueForDecl<PointerValue>(ASTCtx, Env, Name: "member_p2");
7170 EXPECT_EQ(&MemberP1.getPointeeLoc(), &MemberP2.getPointeeLoc());
7171 });
7172}
7173
7174// Check that a builtin function is not associated with a value. (It's only
7175// possible to call builtin functions directly, not take their address.)
7176TEST(TransferTest, BuiltinFunctionModeled) {
7177 std::string Code = R"(
7178 void target() {
7179 __builtin_expect(0, 0);
7180 // [[p]]
7181 }
7182 )";
7183 runDataflow(
7184 Code,
7185 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7186 ASTContext &ASTCtx) {
7187 using ast_matchers::selectFirst;
7188 using ast_matchers::match;
7189 using ast_matchers::traverse;
7190 using ast_matchers::implicitCastExpr;
7191 using ast_matchers::hasCastKind;
7192
7193 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
7194
7195 auto *ImplicitCast = selectFirst<ImplicitCastExpr>(
7196 BoundTo: "implicit_cast",
7197 Results: match(Matcher: traverse(TK: TK_AsIs,
7198 InnerMatcher: implicitCastExpr(hasCastKind(Kind: CK_BuiltinFnToFnPtr))
7199 .bind(ID: "implicit_cast")),
7200 Context&: ASTCtx));
7201
7202 ASSERT_THAT(ImplicitCast, NotNull());
7203 EXPECT_THAT(Env.getValue(*ImplicitCast), IsNull());
7204 });
7205}
7206
7207// Check that a callee of a member operator call is modeled as a `PointerValue`.
7208// Member operator calls are unusual in that their callee is a pointer that
7209// stems from a `FunctionToPointerDecay`. In calls to non-operator non-static
7210// member functions, the callee is a `MemberExpr` (which does not have pointer
7211// type).
7212// We want to make sure that we produce a pointer value for the callee in this
7213// specific scenario and that its storage location is durable (for convergence).
7214TEST(TransferTest, MemberOperatorCallModelsPointerForCallee) {
7215 std::string Code = R"(
7216 struct S {
7217 bool operator!=(S s);
7218 };
7219 void target() {
7220 S s;
7221 (void)(s != s);
7222 (void)(s != s);
7223 // [[p]]
7224 }
7225 )";
7226 runDataflow(
7227 Code,
7228 [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7229 ASTContext &ASTCtx) {
7230 using ast_matchers::selectFirst;
7231 using ast_matchers::match;
7232 using ast_matchers::traverse;
7233 using ast_matchers::cxxOperatorCallExpr;
7234
7235 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
7236
7237 auto Matches = match(
7238 Matcher: traverse(TK: TK_AsIs, InnerMatcher: cxxOperatorCallExpr().bind(ID: "call")), Context&: ASTCtx);
7239
7240 ASSERT_EQ(Matches.size(), 2UL);
7241
7242 auto *Call1 = Matches[0].getNodeAs<CXXOperatorCallExpr>(ID: "call");
7243 auto *Call2 = Matches[1].getNodeAs<CXXOperatorCallExpr>(ID: "call");
7244
7245 ASSERT_THAT(Call1, NotNull());
7246 ASSERT_THAT(Call2, NotNull());
7247
7248 EXPECT_EQ(cast<ImplicitCastExpr>(Call1->getCallee())->getCastKind(),
7249 CK_FunctionToPointerDecay);
7250 EXPECT_EQ(cast<ImplicitCastExpr>(Call2->getCallee())->getCastKind(),
7251 CK_FunctionToPointerDecay);
7252
7253 auto *Ptr1 = cast<PointerValue>(Env.getValue(*Call1->getCallee()));
7254 auto *Ptr2 = cast<PointerValue>(Env.getValue(*Call2->getCallee()));
7255
7256 ASSERT_EQ(&Ptr1->getPointeeLoc(), &Ptr2->getPointeeLoc());
7257 });
7258}
7259
7260// Check that fields of anonymous records are modeled.
7261TEST(TransferTest, AnonymousStruct) {
7262 std::string Code = R"(
7263 struct S {
7264 struct {
7265 bool b;
7266 };
7267 };
7268 void target() {
7269 S s;
7270 s.b = true;
7271 // [[p]]
7272 }
7273 )";
7274 runDataflow(
7275 Code,
7276 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7277 ASTContext &ASTCtx) {
7278 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
7279 const ValueDecl *SDecl = findValueDecl(ASTCtx, Name: "s");
7280 const ValueDecl *BDecl = findValueDecl(ASTCtx, Name: "b");
7281 const IndirectFieldDecl *IndirectField =
7282 findIndirectFieldDecl(ASTCtx, Name: "b");
7283
7284 auto *S = cast<RecordStorageLocation>(Val: Env.getStorageLocation(D: *SDecl));
7285 auto &AnonStruct = *cast<RecordStorageLocation>(
7286 Val: S->getChild(D: *cast<ValueDecl>(Val: IndirectField->chain().front())));
7287
7288 auto *B = cast<BoolValue>(Val: getFieldValue(Loc: &AnonStruct, Field: *BDecl, Env));
7289 ASSERT_TRUE(Env.proves(B->formula()));
7290 });
7291}
7292
7293TEST(TransferTest, AnonymousStructWithInitializer) {
7294 std::string Code = R"(
7295 struct target {
7296 target() {
7297 (void)0;
7298 // [[p]]
7299 }
7300 struct {
7301 bool b = true;
7302 };
7303 };
7304 )";
7305 runDataflow(
7306 Code,
7307 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7308 ASTContext &ASTCtx) {
7309 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
7310 const ValueDecl *BDecl = findValueDecl(ASTCtx, Name: "b");
7311 const IndirectFieldDecl *IndirectField =
7312 findIndirectFieldDecl(ASTCtx, Name: "b");
7313
7314 auto *ThisLoc =
7315 cast<RecordStorageLocation>(Val: Env.getThisPointeeStorageLocation());
7316 auto &AnonStruct = *cast<RecordStorageLocation>(Val: ThisLoc->getChild(
7317 D: *cast<ValueDecl>(Val: IndirectField->chain().front())));
7318
7319 auto *B = cast<BoolValue>(Val: getFieldValue(Loc: &AnonStruct, Field: *BDecl, Env));
7320 ASSERT_TRUE(Env.proves(B->formula()));
7321 });
7322}
7323
7324TEST(TransferTest, AnonymousStructWithReferenceField) {
7325 std::string Code = R"(
7326 int global_i = 0;
7327 struct target {
7328 target() {
7329 (void)0;
7330 // [[p]]
7331 }
7332 struct {
7333 int &i = global_i;
7334 };
7335 };
7336 )";
7337 runDataflow(
7338 Code,
7339 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7340 ASTContext &ASTCtx) {
7341 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
7342 const ValueDecl *GlobalIDecl = findValueDecl(ASTCtx, Name: "global_i");
7343 const ValueDecl *IDecl = findValueDecl(ASTCtx, Name: "i");
7344 const IndirectFieldDecl *IndirectField =
7345 findIndirectFieldDecl(ASTCtx, Name: "i");
7346
7347 auto *ThisLoc =
7348 cast<RecordStorageLocation>(Val: Env.getThisPointeeStorageLocation());
7349 auto &AnonStruct = *cast<RecordStorageLocation>(Val: ThisLoc->getChild(
7350 D: *cast<ValueDecl>(Val: IndirectField->chain().front())));
7351
7352 ASSERT_EQ(AnonStruct.getChild(*IDecl),
7353 Env.getStorageLocation(*GlobalIDecl));
7354 });
7355}
7356
7357TEST(TransferTest, EvaluateBlockWithUnreachablePreds) {
7358 // This is a crash repro.
7359 // `false` block may not have been processed when we try to evaluate the `||`
7360 // after visiting `true`, because it is not necessary (and therefore the edge
7361 // is marked unreachable). Trying to get the analysis state via
7362 // `getEnvironment` for the subexpression still should not crash.
7363 std::string Code = R"(
7364 int target(int i) {
7365 if ((i < 0 && true) || false) {
7366 return 0;
7367 }
7368 return 0;
7369 }
7370 )";
7371 runDataflow(
7372 Code,
7373 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7374 ASTContext &ASTCtx) {});
7375}
7376
7377TEST(TransferTest, LambdaCaptureByCopy) {
7378 std::string Code = R"(
7379 void target(int Foo, int Bar) {
7380 [Foo]() {
7381 (void)0;
7382 // [[p]]
7383 }();
7384 }
7385 )";
7386 runDataflowOnLambda(
7387 Code,
7388 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7389 ASTContext &ASTCtx) {
7390 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
7391 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
7392
7393 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
7394 ASSERT_THAT(FooDecl, NotNull());
7395
7396 const StorageLocation *FooLoc = Env.getStorageLocation(D: *FooDecl);
7397 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
7398
7399 const Value *FooVal = Env.getValue(Loc: *FooLoc);
7400 EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
7401
7402 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
7403 ASSERT_THAT(BarDecl, NotNull());
7404
7405 const StorageLocation *BarLoc = Env.getStorageLocation(D: *BarDecl);
7406 EXPECT_THAT(BarLoc, IsNull());
7407 });
7408}
7409
7410TEST(TransferTest, LambdaCaptureByReference) {
7411 std::string Code = R"(
7412 void target(int Foo, int Bar) {
7413 [&Foo]() {
7414 (void)0;
7415 // [[p]]
7416 }();
7417 }
7418 )";
7419 runDataflowOnLambda(
7420 Code,
7421 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7422 ASTContext &ASTCtx) {
7423 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
7424 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
7425
7426 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
7427 ASSERT_THAT(FooDecl, NotNull());
7428
7429 const StorageLocation *FooLoc = Env.getStorageLocation(D: *FooDecl);
7430 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
7431
7432 const Value *FooVal = Env.getValue(Loc: *FooLoc);
7433 EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
7434
7435 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
7436 ASSERT_THAT(BarDecl, NotNull());
7437
7438 const StorageLocation *BarLoc = Env.getStorageLocation(D: *BarDecl);
7439 EXPECT_THAT(BarLoc, IsNull());
7440 });
7441}
7442
7443TEST(TransferTest, LambdaCaptureWithInitializer) {
7444 std::string Code = R"(
7445 void target(int Bar) {
7446 [Foo=Bar]() {
7447 (void)0;
7448 // [[p]]
7449 }();
7450 }
7451 )";
7452 runDataflowOnLambda(
7453 Code,
7454 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7455 ASTContext &ASTCtx) {
7456 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
7457 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
7458
7459 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
7460 ASSERT_THAT(FooDecl, NotNull());
7461
7462 const StorageLocation *FooLoc = Env.getStorageLocation(D: *FooDecl);
7463 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
7464
7465 const Value *FooVal = Env.getValue(Loc: *FooLoc);
7466 EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
7467
7468 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
7469 ASSERT_THAT(BarDecl, NotNull());
7470
7471 const StorageLocation *BarLoc = Env.getStorageLocation(D: *BarDecl);
7472 EXPECT_THAT(BarLoc, IsNull());
7473 });
7474}
7475
7476TEST(TransferTest, LambdaCaptureByCopyImplicit) {
7477 std::string Code = R"(
7478 void target(int Foo, int Bar) {
7479 [=]() {
7480 Foo;
7481 // [[p]]
7482 }();
7483 }
7484 )";
7485 runDataflowOnLambda(
7486 Code,
7487 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7488 ASTContext &ASTCtx) {
7489 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
7490 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
7491
7492 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
7493 ASSERT_THAT(FooDecl, NotNull());
7494
7495 const StorageLocation *FooLoc = Env.getStorageLocation(D: *FooDecl);
7496 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
7497
7498 const Value *FooVal = Env.getValue(Loc: *FooLoc);
7499 EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
7500
7501 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
7502 ASSERT_THAT(BarDecl, NotNull());
7503
7504 // There is no storage location for `Bar` because it isn't used in the
7505 // body of the lambda.
7506 const StorageLocation *BarLoc = Env.getStorageLocation(D: *BarDecl);
7507 EXPECT_THAT(BarLoc, IsNull());
7508 });
7509}
7510
7511TEST(TransferTest, LambdaCaptureByReferenceImplicit) {
7512 std::string Code = R"(
7513 void target(int Foo, int Bar) {
7514 [&]() {
7515 Foo;
7516 // [[p]]
7517 }();
7518 }
7519 )";
7520 runDataflowOnLambda(
7521 Code,
7522 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7523 ASTContext &ASTCtx) {
7524 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
7525 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
7526
7527 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
7528 ASSERT_THAT(FooDecl, NotNull());
7529
7530 const StorageLocation *FooLoc = Env.getStorageLocation(D: *FooDecl);
7531 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
7532
7533 const Value *FooVal = Env.getValue(Loc: *FooLoc);
7534 EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
7535
7536 const ValueDecl *BarDecl = findValueDecl(ASTCtx, Name: "Bar");
7537 ASSERT_THAT(BarDecl, NotNull());
7538
7539 // There is no storage location for `Bar` because it isn't used in the
7540 // body of the lambda.
7541 const StorageLocation *BarLoc = Env.getStorageLocation(D: *BarDecl);
7542 EXPECT_THAT(BarLoc, IsNull());
7543 });
7544}
7545
7546TEST(TransferTest, LambdaCaptureThis) {
7547 std::string Code = R"(
7548 struct Bar {
7549 int Foo;
7550
7551 void target() {
7552 [this]() {
7553 Foo;
7554 // [[p]]
7555 }();
7556 }
7557 };
7558 )";
7559 runDataflowOnLambda(
7560 Code,
7561 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7562 ASTContext &ASTCtx) {
7563 ASSERT_THAT(Results.keys(), UnorderedElementsAre("p"));
7564 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
7565
7566 const RecordStorageLocation *ThisPointeeLoc =
7567 Env.getThisPointeeStorageLocation();
7568 ASSERT_THAT(ThisPointeeLoc, NotNull());
7569
7570 const ValueDecl *FooDecl = findValueDecl(ASTCtx, Name: "Foo");
7571 ASSERT_THAT(FooDecl, NotNull());
7572
7573 const StorageLocation *FooLoc = ThisPointeeLoc->getChild(D: *FooDecl);
7574 ASSERT_TRUE(isa_and_nonnull<ScalarStorageLocation>(FooLoc));
7575
7576 const Value *FooVal = Env.getValue(Loc: *FooLoc);
7577 EXPECT_TRUE(isa_and_nonnull<IntegerValue>(FooVal));
7578 });
7579}
7580
7581// This test verifies correct modeling of a relational dependency that goes
7582// through unmodeled functions (the simple `cond()` in this case).
7583TEST(TransferTest, ConditionalRelation) {
7584 std::string Code = R"(
7585 bool cond();
7586 void target() {
7587 bool a = true;
7588 bool b = true;
7589 if (cond()) {
7590 a = false;
7591 if (cond()) {
7592 b = false;
7593 }
7594 }
7595 (void)0;
7596 // [[p]]
7597 }
7598 )";
7599 runDataflow(
7600 Code,
7601 VerifyResults: [](const llvm::StringMap<DataflowAnalysisState<NoopLattice>> &Results,
7602 ASTContext &ASTCtx) {
7603 const Environment &Env = getEnvironmentAtAnnotation(AnnotationStates: Results, Annotation: "p");
7604 auto &A = Env.arena();
7605 auto &VarA = getValueForDecl<BoolValue>(ASTCtx, Env, Name: "a").formula();
7606 auto &VarB = getValueForDecl<BoolValue>(ASTCtx, Env, Name: "b").formula();
7607
7608 EXPECT_FALSE(Env.allows(A.makeAnd(VarA, A.makeNot(VarB))));
7609 });
7610}
7611
7612// This is a crash repro.
7613// We used to crash while transferring `S().i` because Clang contained a bug
7614// causing the AST to be malformed.
7615TEST(TransferTest, AnonymousUnionMemberExprInTemplate) {
7616 using ast_matchers::functionDecl;
7617 using ast_matchers::hasName;
7618 using ast_matchers::unless;
7619
7620 std::string Code = R"cc(
7621 struct S {
7622 struct {
7623 int i;
7624 };
7625 };
7626
7627 template <class>
7628 void target() {
7629 S().i;
7630 }
7631
7632 template void target<int>();
7633 )cc";
7634 auto Matcher = functionDecl(hasName(Name: "target"), unless(isTemplated()));
7635 ASSERT_THAT_ERROR(checkDataflowWithNoopAnalysis(Code, Matcher),
7636 llvm::Succeeded());
7637}
7638
7639} // namespace
7640

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of clang/unittests/Analysis/FlowSensitive/TransferTest.cpp