xref: /llvm-project/clang/unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.cpp (revision 49cb1701389ac3cff3e2c552531bfd3a607311d8)
1 //===- unittests/Analysis/FlowSensitive/DataflowEnvironmentTest.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 "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
10 #include "TestingSupport.h"
11 #include "clang/AST/DeclCXX.h"
12 #include "clang/AST/ExprCXX.h"
13 #include "clang/AST/Stmt.h"
14 #include "clang/ASTMatchers/ASTMatchFinder.h"
15 #include "clang/ASTMatchers/ASTMatchers.h"
16 #include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
17 #include "clang/Analysis/FlowSensitive/StorageLocation.h"
18 #include "clang/Analysis/FlowSensitive/Value.h"
19 #include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
20 #include "clang/Tooling/Tooling.h"
21 #include "gmock/gmock.h"
22 #include "gtest/gtest.h"
23 #include <memory>
24 #include <string>
25 
26 namespace {
27 
28 using namespace clang;
29 using namespace dataflow;
30 using ::clang::dataflow::test::findValueDecl;
31 using ::clang::dataflow::test::getFieldValue;
32 using ::testing::Contains;
33 using ::testing::IsNull;
34 using ::testing::NotNull;
35 
36 class EnvironmentTest : public ::testing::Test {
37 protected:
38   EnvironmentTest() : DAContext(std::make_unique<WatchedLiteralsSolver>()) {}
39 
40   DataflowAnalysisContext DAContext;
41 };
42 
43 TEST_F(EnvironmentTest, FlowCondition) {
44   Environment Env(DAContext);
45   auto &A = Env.arena();
46 
47   EXPECT_TRUE(Env.proves(A.makeLiteral(true)));
48   EXPECT_TRUE(Env.allows(A.makeLiteral(true)));
49   EXPECT_FALSE(Env.proves(A.makeLiteral(false)));
50   EXPECT_FALSE(Env.allows(A.makeLiteral(false)));
51 
52   auto &X = A.makeAtomRef(A.makeAtom());
53   EXPECT_FALSE(Env.proves(X));
54   EXPECT_TRUE(Env.allows(X));
55 
56   Env.assume(X);
57   EXPECT_TRUE(Env.proves(X));
58   EXPECT_TRUE(Env.allows(X));
59 
60   auto &NotX = A.makeNot(X);
61   EXPECT_FALSE(Env.proves(NotX));
62   EXPECT_FALSE(Env.allows(NotX));
63 }
64 
65 TEST_F(EnvironmentTest, SetAndGetValueOnCfgOmittedNodes) {
66   // Check that we can set a value on an expression that is omitted from the CFG
67   // (see `ignoreCFGOmittedNodes()`), then retrieve that same value from the
68   // expression. This is a regression test; `setValue()` and `getValue()`
69   // previously did not use `ignoreCFGOmittedNodes()` consistently.
70 
71   using namespace ast_matchers;
72 
73   std::string Code = R"cc(
74     struct S {
75       int f();
76     };
77     void target() {
78       // Method call on a temporary produces an `ExprWithCleanups`.
79       S().f();
80       (1);
81     }
82   )cc";
83 
84   auto Unit =
85       tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
86   auto &Context = Unit->getASTContext();
87 
88   ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
89 
90   const ExprWithCleanups *WithCleanups = selectFirst<ExprWithCleanups>(
91       "cleanups",
92       match(exprWithCleanups(hasType(isInteger())).bind("cleanups"), Context));
93   ASSERT_NE(WithCleanups, nullptr);
94 
95   const ParenExpr *Paren = selectFirst<ParenExpr>(
96       "paren", match(parenExpr(hasType(isInteger())).bind("paren"), Context));
97   ASSERT_NE(Paren, nullptr);
98 
99   Environment Env(DAContext);
100   IntegerValue *Val1 =
101       cast<IntegerValue>(Env.createValue(Unit->getASTContext().IntTy));
102   Env.setValue(*WithCleanups, *Val1);
103   EXPECT_EQ(Env.getValue(*WithCleanups), Val1);
104 
105   IntegerValue *Val2 =
106       cast<IntegerValue>(Env.createValue(Unit->getASTContext().IntTy));
107   Env.setValue(*Paren, *Val2);
108   EXPECT_EQ(Env.getValue(*Paren), Val2);
109 }
110 
111 TEST_F(EnvironmentTest, CreateValueRecursiveType) {
112   using namespace ast_matchers;
113 
114   std::string Code = R"cc(
115     struct Recursive {
116       bool X;
117       Recursive *R;
118     };
119     // Use both fields to force them to be created with `createValue`.
120     void Usage(Recursive R) { (void)R.X; (void)R.R; }
121   )cc";
122 
123   auto Unit =
124       tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
125   auto &Context = Unit->getASTContext();
126 
127   ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
128 
129   auto Results =
130       match(qualType(hasDeclaration(recordDecl(
131                          hasName("Recursive"),
132                          has(fieldDecl(hasName("R")).bind("field-r")))))
133                 .bind("target"),
134             Context);
135   const QualType *TyPtr = selectFirst<QualType>("target", Results);
136   ASSERT_THAT(TyPtr, NotNull());
137   QualType Ty = *TyPtr;
138   ASSERT_FALSE(Ty.isNull());
139 
140   const FieldDecl *R = selectFirst<FieldDecl>("field-r", Results);
141   ASSERT_THAT(R, NotNull());
142 
143   Results = match(functionDecl(hasName("Usage")).bind("fun"), Context);
144   const auto *Fun = selectFirst<FunctionDecl>("fun", Results);
145   ASSERT_THAT(Fun, NotNull());
146 
147   // Verify that the struct and the field (`R`) with first appearance of the
148   // type is created successfully.
149   Environment Env(DAContext, *Fun);
150   Env.initialize();
151   auto &SLoc = cast<RecordStorageLocation>(Env.createObject(Ty));
152   PointerValue *PV = cast_or_null<PointerValue>(getFieldValue(&SLoc, *R, Env));
153   EXPECT_THAT(PV, NotNull());
154 }
155 
156 TEST_F(EnvironmentTest, DifferentReferenceLocInJoin) {
157   // This tests the case where the storage location for a reference-type
158   // variable is different for two states being joined. We used to believe this
159   // could not happen and therefore had an assertion disallowing this; this test
160   // exists to demonstrate that we can handle this condition without a failing
161   // assertion. See also the discussion here:
162   // https://discourse.llvm.org/t/70086/6
163 
164   using namespace ast_matchers;
165 
166   std::string Code = R"cc(
167     void f(int &ref) {}
168   )cc";
169 
170   auto Unit =
171       tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
172   auto &Context = Unit->getASTContext();
173 
174   ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
175 
176   const ValueDecl *Ref = findValueDecl(Context, "ref");
177 
178   Environment Env1(DAContext);
179   StorageLocation &Loc1 = Env1.createStorageLocation(Context.IntTy);
180   Env1.setStorageLocation(*Ref, Loc1);
181 
182   Environment Env2(DAContext);
183   StorageLocation &Loc2 = Env2.createStorageLocation(Context.IntTy);
184   Env2.setStorageLocation(*Ref, Loc2);
185 
186   EXPECT_NE(&Loc1, &Loc2);
187 
188   Environment::ValueModel Model;
189   Environment EnvJoined =
190       Environment::join(Env1, Env2, Model, Environment::DiscardExprState);
191 
192   // Joining environments with different storage locations for the same
193   // declaration results in the declaration being removed from the joined
194   // environment.
195   EXPECT_EQ(EnvJoined.getStorageLocation(*Ref), nullptr);
196 }
197 
198 TEST_F(EnvironmentTest, InitGlobalVarsFun) {
199   using namespace ast_matchers;
200 
201   std::string Code = R"cc(
202      int Global = 0;
203      int Target () { return Global; }
204   )cc";
205 
206   auto Unit =
207       tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
208   auto &Context = Unit->getASTContext();
209 
210   ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
211 
212   auto Results =
213       match(decl(anyOf(varDecl(hasName("Global")).bind("global"),
214                        functionDecl(hasName("Target")).bind("target"))),
215             Context);
216   const auto *Fun = selectFirst<FunctionDecl>("target", Results);
217   const auto *Var = selectFirst<VarDecl>("global", Results);
218   ASSERT_THAT(Fun, NotNull());
219   ASSERT_THAT(Var, NotNull());
220 
221   // Verify the global variable is populated when we analyze `Target`.
222   Environment Env(DAContext, *Fun);
223   Env.initialize();
224   EXPECT_THAT(Env.getValue(*Var), NotNull());
225 }
226 
227 // Tests that fields mentioned only in default member initializers are included
228 // in the set of tracked fields.
229 TEST_F(EnvironmentTest, IncludeFieldsFromDefaultInitializers) {
230   using namespace ast_matchers;
231 
232   std::string Code = R"cc(
233      struct S {
234        S() {}
235        int X = 3;
236        int Y = X;
237      };
238      S foo();
239   )cc";
240 
241   auto Unit =
242       tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
243   auto &Context = Unit->getASTContext();
244 
245   ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
246 
247   auto Results = match(
248       qualType(hasDeclaration(
249                    cxxRecordDecl(hasName("S"),
250                                  hasMethod(cxxConstructorDecl().bind("target")))
251                        .bind("struct")))
252           .bind("ty"),
253       Context);
254   const auto *Constructor = selectFirst<FunctionDecl>("target", Results);
255   const auto *Rec = selectFirst<RecordDecl>("struct", Results);
256   const auto QTy = *selectFirst<QualType>("ty", Results);
257   ASSERT_THAT(Constructor, NotNull());
258   ASSERT_THAT(Rec, NotNull());
259   ASSERT_FALSE(QTy.isNull());
260 
261   auto Fields = Rec->fields();
262   FieldDecl *XDecl = nullptr;
263   for (FieldDecl *Field : Fields) {
264     if (Field->getNameAsString() == "X") {
265       XDecl = Field;
266       break;
267     }
268   }
269   ASSERT_THAT(XDecl, NotNull());
270 
271   // Verify that the `X` field of `S` is populated when analyzing the
272   // constructor, even though it is not referenced directly in the constructor.
273   Environment Env(DAContext, *Constructor);
274   Env.initialize();
275   auto &Loc = cast<RecordStorageLocation>(Env.createObject(QTy));
276   EXPECT_THAT(getFieldValue(&Loc, *XDecl, Env), NotNull());
277 }
278 
279 TEST_F(EnvironmentTest, InitGlobalVarsFieldFun) {
280   using namespace ast_matchers;
281 
282   std::string Code = R"cc(
283      struct S { int Bar; };
284      S Global = {0};
285      int Target () { return Global.Bar; }
286   )cc";
287 
288   auto Unit =
289       tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
290   auto &Context = Unit->getASTContext();
291 
292   ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
293 
294   auto Results =
295       match(decl(anyOf(varDecl(hasName("Global")).bind("global"),
296                        functionDecl(hasName("Target")).bind("target"))),
297             Context);
298   const auto *Fun = selectFirst<FunctionDecl>("target", Results);
299   const auto *GlobalDecl = selectFirst<VarDecl>("global", Results);
300   ASSERT_THAT(Fun, NotNull());
301   ASSERT_THAT(GlobalDecl, NotNull());
302 
303   ASSERT_TRUE(GlobalDecl->getType()->isStructureType());
304   auto GlobalFields = GlobalDecl->getType()->getAsRecordDecl()->fields();
305 
306   FieldDecl *BarDecl = nullptr;
307   for (FieldDecl *Field : GlobalFields) {
308     if (Field->getNameAsString() == "Bar") {
309       BarDecl = Field;
310       break;
311     }
312     FAIL() << "Unexpected field: " << Field->getNameAsString();
313   }
314   ASSERT_THAT(BarDecl, NotNull());
315 
316   // Verify the global variable is populated when we analyze `Target`.
317   Environment Env(DAContext, *Fun);
318   Env.initialize();
319   const auto *GlobalLoc =
320       cast<RecordStorageLocation>(Env.getStorageLocation(*GlobalDecl));
321   auto *BarVal = getFieldValue(GlobalLoc, *BarDecl, Env);
322   EXPECT_TRUE(isa<IntegerValue>(BarVal));
323 }
324 
325 TEST_F(EnvironmentTest, InitGlobalVarsConstructor) {
326   using namespace ast_matchers;
327 
328   std::string Code = R"cc(
329      int Global = 0;
330      struct Target {
331        Target() : Field(Global) {}
332        int Field;
333      };
334   )cc";
335 
336   auto Unit =
337       tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
338   auto &Context = Unit->getASTContext();
339 
340   ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
341 
342   auto Results =
343       match(decl(anyOf(
344                 varDecl(hasName("Global")).bind("global"),
345                 cxxConstructorDecl(ofClass(hasName("Target"))).bind("target"))),
346             Context);
347   const auto *Ctor = selectFirst<CXXConstructorDecl>("target", Results);
348   const auto *Var = selectFirst<VarDecl>("global", Results);
349   ASSERT_TRUE(Ctor != nullptr);
350   ASSERT_THAT(Var, NotNull());
351 
352   // Verify the global variable is populated when we analyze `Target`.
353   Environment Env(DAContext, *Ctor);
354   Env.initialize();
355   EXPECT_THAT(Env.getValue(*Var), NotNull());
356 }
357 
358 // Pointers to Members are a tricky case of accessor calls, complicated further
359 // when using templates where the pointer to the member is a template argument.
360 // This is a repro of a failure case seen in the wild.
361 TEST_F(EnvironmentTest,
362        ModelMemberForAccessorUsingMethodPointerThroughTemplate) {
363   using namespace ast_matchers;
364 
365   std::string Code = R"cc(
366       struct S {
367         int accessor() {return member;}
368 
369         int member = 0;
370       };
371 
372       template <auto method>
373       int Target(S* S) {
374         return (S->*method)();
375       }
376 
377      // We want to analyze the instantiation of Target for the accessor.
378      int Instantiator () {S S; return Target<&S::accessor>(&S); }
379   )cc";
380 
381   auto Unit =
382       // C++17 for the simplifying use of auto in the template declaration.
383       tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
384   auto &Context = Unit->getASTContext();
385 
386   ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
387 
388   auto Results = match(
389       decl(anyOf(functionDecl(hasName("Target"), isTemplateInstantiation())
390                      .bind("target"),
391                  fieldDecl(hasName("member")).bind("member"),
392                  recordDecl(hasName("S")).bind("struct"))),
393       Context);
394   const auto *Fun = selectFirst<FunctionDecl>("target", Results);
395   const auto *Struct = selectFirst<RecordDecl>("struct", Results);
396   const auto *Member = selectFirst<FieldDecl>("member", Results);
397   ASSERT_THAT(Fun, NotNull());
398   ASSERT_THAT(Struct, NotNull());
399   ASSERT_THAT(Member, NotNull());
400 
401   // Verify that `member` is modeled for `S` when we analyze
402   // `Target<&S::accessor>`.
403   Environment Env(DAContext, *Fun);
404   Env.initialize();
405   EXPECT_THAT(DAContext.getModeledFields(QualType(Struct->getTypeForDecl(), 0)),
406               Contains(Member));
407 }
408 
409 // This is a repro of a failure case seen in the wild.
410 TEST_F(EnvironmentTest, CXXDefaultInitExprResultObjIsWrappedExprResultObj) {
411   using namespace ast_matchers;
412 
413   std::string Code = R"cc(
414       struct Inner {};
415 
416       struct S {
417         S() {}
418 
419         Inner i = {};
420       };
421   )cc";
422 
423   auto Unit =
424       tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
425   auto &Context = Unit->getASTContext();
426 
427   ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
428 
429   auto Results =
430       match(cxxConstructorDecl(
431                 hasAnyConstructorInitializer(cxxCtorInitializer(
432                     withInitializer(expr().bind("default_init_expr")))))
433                 .bind("ctor"),
434             Context);
435   const auto *Constructor = selectFirst<CXXConstructorDecl>("ctor", Results);
436   const auto *DefaultInit =
437       selectFirst<CXXDefaultInitExpr>("default_init_expr", Results);
438 
439   Environment Env(DAContext, *Constructor);
440   Env.initialize();
441   EXPECT_EQ(&Env.getResultObjectLocation(*DefaultInit),
442             &Env.getResultObjectLocation(*DefaultInit->getExpr()));
443 }
444 
445 // This test verifies the behavior of `getResultObjectLocation()` in
446 // scenarios involving inherited constructors.
447 // Since the specific AST node of interest `CXXConstructorDecl` is implicitly
448 // generated, we cannot annotate any statements inside of it as we do in tests
449 // within TransferTest. Thus, the only way to get the right `Environment` is by
450 // explicitly initializing it as we do in tests within EnvironmentTest.
451 // This is why this test is not inside TransferTest, where most of the tests for
452 // `getResultObjectLocation()` are located.
453 TEST_F(EnvironmentTest, ResultObjectLocationForInheritedCtorInitExpr) {
454   using namespace ast_matchers;
455 
456   std::string Code = R"(
457     struct Base {
458       Base(int b) {}
459     };
460     struct Derived : Base {
461       using Base::Base;
462     };
463 
464     Derived d = Derived(0);
465   )";
466 
467   auto Unit =
468       tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++20"});
469   auto &Context = Unit->getASTContext();
470 
471   ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
472 
473   auto Results =
474       match(cxxConstructorDecl(
475                 hasAnyConstructorInitializer(cxxCtorInitializer(
476                     withInitializer(expr().bind("inherited_ctor_init_expr")))))
477                 .bind("ctor"),
478             Context);
479   const auto *Constructor = selectFirst<CXXConstructorDecl>("ctor", Results);
480   const auto *InheritedCtorInit = selectFirst<CXXInheritedCtorInitExpr>(
481       "inherited_ctor_init_expr", Results);
482 
483   EXPECT_EQ(InheritedCtorInit->child_begin(), InheritedCtorInit->child_end());
484 
485   Environment Env(DAContext, *Constructor);
486   Env.initialize();
487 
488   RecordStorageLocation &Loc = Env.getResultObjectLocation(*InheritedCtorInit);
489   EXPECT_NE(&Loc, nullptr);
490 
491   EXPECT_EQ(&Loc, Env.getThisPointeeStorageLocation());
492 }
493 
494 TEST_F(EnvironmentTest, Stmt) {
495   using namespace ast_matchers;
496 
497   std::string Code = R"cc(
498       struct S { int i; };
499       void foo() {
500         S AnS = S{1};
501       }
502     )cc";
503   auto Unit =
504       tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
505   auto &Context = Unit->getASTContext();
506 
507   ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
508 
509   auto *DeclStatement = const_cast<DeclStmt *>(selectFirst<DeclStmt>(
510       "d", match(declStmt(hasSingleDecl(varDecl(hasName("AnS")))).bind("d"),
511                  Context)));
512   ASSERT_THAT(DeclStatement, NotNull());
513   auto *Init = (cast<VarDecl>(*DeclStatement->decl_begin()))->getInit();
514   ASSERT_THAT(Init, NotNull());
515 
516   // Verify that we can retrieve the result object location for the initializer
517   // expression when we analyze the DeclStmt for `AnS`.
518   Environment Env(DAContext, *DeclStatement);
519   // Don't crash when initializing.
520   Env.initialize();
521   // And don't crash when retrieving the result object location.
522   Env.getResultObjectLocation(*Init);
523 }
524 
525 // This is a crash repro.
526 TEST_F(EnvironmentTest, LambdaCapturingThisInFieldInitializer) {
527   using namespace ast_matchers;
528   std::string Code = R"cc(
529       struct S {
530         int f{[this]() { return 1; }()};
531       };
532     )cc";
533 
534   auto Unit =
535       tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
536   auto &Context = Unit->getASTContext();
537 
538   ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
539 
540   auto *LambdaCallOperator = selectFirst<CXXMethodDecl>(
541       "method", match(cxxMethodDecl(hasName("operator()"),
542                                     ofClass(cxxRecordDecl(isLambda())))
543                           .bind("method"),
544                       Context));
545 
546   Environment Env(DAContext, *LambdaCallOperator);
547   // Don't crash when initializing.
548   Env.initialize();
549   // And initialize the captured `this` pointee.
550   ASSERT_NE(nullptr, Env.getThisPointeeStorageLocation());
551 }
552 
553 } // namespace
554