1 | //===- unittests/AST/ASTExprTest.cpp --- AST Expr tests -------------------===// |
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 | // This file contains tests for AST Expr related methods. |
10 | // |
11 | //===----------------------------------------------------------------------===// |
12 | |
13 | #include "ASTPrint.h" |
14 | #include "clang/AST/ASTContext.h" |
15 | #include "clang/AST/Expr.h" |
16 | #include "clang/AST/IgnoreExpr.h" |
17 | #include "clang/ASTMatchers/ASTMatchFinder.h" |
18 | #include "clang/Tooling/Tooling.h" |
19 | #include "gtest/gtest.h" |
20 | |
21 | using namespace clang; |
22 | |
23 | using clang::ast_matchers::cxxRecordDecl; |
24 | using clang::ast_matchers::hasName; |
25 | using clang::ast_matchers::match; |
26 | using clang::ast_matchers::varDecl; |
27 | using clang::tooling::buildASTFromCode; |
28 | |
29 | static IntegerLiteral *createIntLiteral(ASTContext &Ctx, uint32_t Value) { |
30 | const int numBits = 32; |
31 | return IntegerLiteral::Create(Ctx, llvm::APInt(numBits, Value), Ctx.IntTy, |
32 | {}); |
33 | } |
34 | |
35 | const CXXRecordDecl *getCXXRecordDeclNode(ASTUnit *AST, |
36 | const std::string &Name) { |
37 | auto Result = |
38 | match(Matcher: cxxRecordDecl(hasName(Name)).bind(ID: "record" ), Context&: AST->getASTContext()); |
39 | EXPECT_FALSE(Result.empty()); |
40 | return Result[0].getNodeAs<CXXRecordDecl>(ID: "record" ); |
41 | } |
42 | |
43 | const VarDecl *getVariableNode(ASTUnit *AST, const std::string &Name) { |
44 | auto Result = match(Matcher: varDecl(hasName(Name)).bind(ID: "var" ), Context&: AST->getASTContext()); |
45 | EXPECT_EQ(Result.size(), 1u); |
46 | return Result[0].getNodeAs<VarDecl>(ID: "var" ); |
47 | } |
48 | |
49 | TEST(ASTExpr, IgnoreExprCallbackForwarded) { |
50 | constexpr char Code[] = "" ; |
51 | auto AST = tooling::buildASTFromCodeWithArgs(Code, /*Args=*/{"-std=c++20" }); |
52 | ASTContext &Ctx = AST->getASTContext(); |
53 | |
54 | struct IgnoreParens { |
55 | Expr *operator()(Expr *E) & { return nullptr; } |
56 | Expr *operator()(Expr *E) && { |
57 | if (auto *PE = dyn_cast<ParenExpr>(Val: E)) { |
58 | return PE->getSubExpr(); |
59 | } |
60 | return E; |
61 | } |
62 | }; |
63 | |
64 | { |
65 | auto *IntExpr = createIntLiteral(Ctx, Value: 10); |
66 | ParenExpr *PE = |
67 | new (Ctx) ParenExpr(SourceLocation{}, SourceLocation{}, IntExpr); |
68 | EXPECT_EQ(IntExpr, IgnoreExprNodes(PE, IgnoreParens{})); |
69 | } |
70 | |
71 | { |
72 | IgnoreParens CB{}; |
73 | auto *IntExpr = createIntLiteral(Ctx, Value: 10); |
74 | ParenExpr *PE = |
75 | new (Ctx) ParenExpr(SourceLocation{}, SourceLocation{}, IntExpr); |
76 | EXPECT_EQ(nullptr, IgnoreExprNodes(PE, CB)); |
77 | } |
78 | } |
79 | |
80 | TEST(ASTExpr, InitListIsConstantInitialized) { |
81 | auto AST = buildASTFromCode(Code: R"cpp( |
82 | struct Empty {}; |
83 | struct Foo : Empty { int x, y; }; |
84 | int gv; |
85 | )cpp" ); |
86 | ASTContext &Ctx = AST->getASTContext(); |
87 | const CXXRecordDecl *Empty = getCXXRecordDeclNode(AST: AST.get(), Name: "Empty" ); |
88 | const CXXRecordDecl *Foo = getCXXRecordDeclNode(AST: AST.get(), Name: "Foo" ); |
89 | |
90 | SourceLocation Loc{}; |
91 | InitListExpr *BaseInit = new (Ctx) InitListExpr(Ctx, Loc, {}, Loc); |
92 | BaseInit->setType(Ctx.getRecordType(Empty)); |
93 | Expr *Exprs[3] = { |
94 | BaseInit, |
95 | createIntLiteral(Ctx, Value: 13), |
96 | createIntLiteral(Ctx, Value: 42), |
97 | }; |
98 | InitListExpr *FooInit = new (Ctx) InitListExpr(Ctx, Loc, Exprs, Loc); |
99 | FooInit->setType(Ctx.getRecordType(Foo)); |
100 | EXPECT_TRUE(FooInit->isConstantInitializer(Ctx, false)); |
101 | |
102 | // Replace the last initializer with something non-constant and make sure |
103 | // this returns false. Previously we had a bug where we didn't count base |
104 | // initializers, and only iterated over fields. |
105 | const VarDecl *GV = getVariableNode(AST: AST.get(), Name: "gv" ); |
106 | auto *Ref = new (Ctx) DeclRefExpr(Ctx, const_cast<VarDecl *>(GV), false, |
107 | Ctx.IntTy, VK_LValue, Loc); |
108 | (void)FooInit->updateInit(C: Ctx, Init: 2, expr: Ref); |
109 | EXPECT_FALSE(FooInit->isConstantInitializer(Ctx, false)); |
110 | } |
111 | |