[RISCV] Fix the cost of `llvm.vector.reduce.and` (#119160)
[llvm-project.git] / clang / unittests / Analysis / FlowSensitive / DataflowEnvironmentTest.cpp
blob737277e167edd10264fa875a5a9cef15bcc33260
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 //===----------------------------------------------------------------------===//
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>
26 namespace {
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;
36 class EnvironmentTest : public ::testing::Test {
37 protected:
38 EnvironmentTest() : DAContext(std::make_unique<WatchedLiteralsSolver>()) {}
40 DataflowAnalysisContext DAContext;
43 TEST_F(EnvironmentTest, FlowCondition) {
44 Environment Env(DAContext);
45 auto &A = Env.arena();
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)));
52 auto &X = A.makeAtomRef(A.makeAtom());
53 EXPECT_FALSE(Env.proves(X));
54 EXPECT_TRUE(Env.allows(X));
56 Env.assume(X);
57 EXPECT_TRUE(Env.proves(X));
58 EXPECT_TRUE(Env.allows(X));
60 auto &NotX = A.makeNot(X);
61 EXPECT_FALSE(Env.proves(NotX));
62 EXPECT_FALSE(Env.allows(NotX));
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.
71 using namespace ast_matchers;
73 std::string Code = R"cc(
74 struct S {
75 int f();
77 void target() {
78 // Method call on a temporary produces an `ExprWithCleanups`.
79 S().f();
80 (1);
82 )cc";
84 auto Unit =
85 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++17"});
86 auto &Context = Unit->getASTContext();
88 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
90 const ExprWithCleanups *WithCleanups = selectFirst<ExprWithCleanups>(
91 "cleanups",
92 match(exprWithCleanups(hasType(isInteger())).bind("cleanups"), Context));
93 ASSERT_NE(WithCleanups, nullptr);
95 const ParenExpr *Paren = selectFirst<ParenExpr>(
96 "paren", match(parenExpr(hasType(isInteger())).bind("paren"), Context));
97 ASSERT_NE(Paren, nullptr);
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);
105 IntegerValue *Val2 =
106 cast<IntegerValue>(Env.createValue(Unit->getASTContext().IntTy));
107 Env.setValue(*Paren, *Val2);
108 EXPECT_EQ(Env.getValue(*Paren), Val2);
111 TEST_F(EnvironmentTest, CreateValueRecursiveType) {
112 using namespace ast_matchers;
114 std::string Code = R"cc(
115 struct Recursive {
116 bool X;
117 Recursive *R;
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";
123 auto Unit =
124 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
125 auto &Context = Unit->getASTContext();
127 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
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());
140 const FieldDecl *R = selectFirst<FieldDecl>("field-r", Results);
141 ASSERT_THAT(R, NotNull());
143 Results = match(functionDecl(hasName("Usage")).bind("fun"), Context);
144 const auto *Fun = selectFirst<FunctionDecl>("fun", Results);
145 ASSERT_THAT(Fun, NotNull());
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());
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
164 using namespace ast_matchers;
166 std::string Code = R"cc(
167 void f(int &ref) {}
168 )cc";
170 auto Unit =
171 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
172 auto &Context = Unit->getASTContext();
174 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
176 const ValueDecl *Ref = findValueDecl(Context, "ref");
178 Environment Env1(DAContext);
179 StorageLocation &Loc1 = Env1.createStorageLocation(Context.IntTy);
180 Env1.setStorageLocation(*Ref, Loc1);
182 Environment Env2(DAContext);
183 StorageLocation &Loc2 = Env2.createStorageLocation(Context.IntTy);
184 Env2.setStorageLocation(*Ref, Loc2);
186 EXPECT_NE(&Loc1, &Loc2);
188 Environment::ValueModel Model;
189 Environment EnvJoined =
190 Environment::join(Env1, Env2, Model, Environment::DiscardExprState);
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);
198 TEST_F(EnvironmentTest, InitGlobalVarsFun) {
199 using namespace ast_matchers;
201 std::string Code = R"cc(
202 int Global = 0;
203 int Target () { return Global; }
204 )cc";
206 auto Unit =
207 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
208 auto &Context = Unit->getASTContext();
210 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
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());
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());
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;
232 std::string Code = R"cc(
233 struct S {
234 S() {}
235 int X = 3;
236 int Y = X;
238 S foo();
239 )cc";
241 auto Unit =
242 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
243 auto &Context = Unit->getASTContext();
245 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
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());
261 auto Fields = Rec->fields();
262 FieldDecl *XDecl = nullptr;
263 for (FieldDecl *Field : Fields) {
264 if (Field->getNameAsString() == "X") {
265 XDecl = Field;
266 break;
269 ASSERT_THAT(XDecl, NotNull());
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());
279 TEST_F(EnvironmentTest, InitGlobalVarsFieldFun) {
280 using namespace ast_matchers;
282 std::string Code = R"cc(
283 struct S { int Bar; };
284 S Global = {0};
285 int Target () { return Global.Bar; }
286 )cc";
288 auto Unit =
289 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
290 auto &Context = Unit->getASTContext();
292 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
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());
303 ASSERT_TRUE(GlobalDecl->getType()->isStructureType());
304 auto GlobalFields = GlobalDecl->getType()->getAsRecordDecl()->fields();
306 FieldDecl *BarDecl = nullptr;
307 for (FieldDecl *Field : GlobalFields) {
308 if (Field->getNameAsString() == "Bar") {
309 BarDecl = Field;
310 break;
312 FAIL() << "Unexpected field: " << Field->getNameAsString();
314 ASSERT_THAT(BarDecl, NotNull());
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));
325 TEST_F(EnvironmentTest, InitGlobalVarsConstructor) {
326 using namespace ast_matchers;
328 std::string Code = R"cc(
329 int Global = 0;
330 struct Target {
331 Target() : Field(Global) {}
332 int Field;
334 )cc";
336 auto Unit =
337 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
338 auto &Context = Unit->getASTContext();
340 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
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());
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());
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;
365 std::string Code = R"cc(
366 struct S {
367 int accessor() {return member;}
369 int member = 0;
372 template <auto method>
373 int Target(S* S) {
374 return (S->*method)();
377 // We want to analyze the instantiation of Target for the accessor.
378 int Instantiator () {S S; return Target<&S::accessor>(&S); }
379 )cc";
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();
386 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
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());
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));
409 // This is a repro of a failure case seen in the wild.
410 TEST_F(EnvironmentTest, CXXDefaultInitExprResultObjIsWrappedExprResultObj) {
411 using namespace ast_matchers;
413 std::string Code = R"cc(
414 struct Inner {};
416 struct S {
417 S() {}
419 Inner i = {};
421 )cc";
423 auto Unit =
424 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
425 auto &Context = Unit->getASTContext();
427 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
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);
439 Environment Env(DAContext, *Constructor);
440 Env.initialize();
441 EXPECT_EQ(&Env.getResultObjectLocation(*DefaultInit),
442 &Env.getResultObjectLocation(*DefaultInit->getExpr()));
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;
456 std::string Code = R"(
457 struct Base {
458 Base(int b) {}
460 struct Derived : Base {
461 using Base::Base;
464 Derived d = Derived(0);
467 auto Unit =
468 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++20"});
469 auto &Context = Unit->getASTContext();
471 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
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);
483 EXPECT_EQ(InheritedCtorInit->child_begin(), InheritedCtorInit->child_end());
485 Environment Env(DAContext, *Constructor);
486 Env.initialize();
488 RecordStorageLocation &Loc = Env.getResultObjectLocation(*InheritedCtorInit);
489 EXPECT_NE(&Loc, nullptr);
491 EXPECT_EQ(&Loc, Env.getThisPointeeStorageLocation());
494 TEST_F(EnvironmentTest, Stmt) {
495 using namespace ast_matchers;
497 std::string Code = R"cc(
498 struct S { int i; };
499 void foo() {
500 S AnS = S{1};
502 )cc";
503 auto Unit =
504 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
505 auto &Context = Unit->getASTContext();
507 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
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());
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);
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; }()};
532 )cc";
534 auto Unit =
535 tooling::buildASTFromCodeWithArgs(Code, {"-fsyntax-only", "-std=c++11"});
536 auto &Context = Unit->getASTContext();
538 ASSERT_EQ(Context.getDiagnostics().getClient()->getNumErrors(), 0U);
540 auto *LambdaCallOperator = selectFirst<CXXMethodDecl>(
541 "method", match(cxxMethodDecl(hasName("operator()"),
542 ofClass(cxxRecordDecl(isLambda())))
543 .bind("method"),
544 Context));
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());
553 } // namespace