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