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