clang-tools 10.0.0

ParsedASTTests.cpp
Go to the documentation of this file.
1 //===-- ParsedASTTests.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 // These tests cover clangd's logic to build a TU, which generally uses the APIs
10 // in ParsedAST and Preamble, via the TestTU helper.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "AST.h"
15 #include "Annotations.h"
16 #include "Compiler.h"
17 #include "Diagnostics.h"
18 #include "ParsedAST.h"
19 #include "SourceCode.h"
20 #include "TestFS.h"
21 #include "TestTU.h"
22 #include "clang/AST/DeclTemplate.h"
23 #include "clang/Basic/TokenKinds.h"
24 #include "clang/Tooling/Syntax/Tokens.h"
25 #include "llvm/Support/ScopedPrinter.h"
26 #include "gmock/gmock-matchers.h"
27 #include "gmock/gmock.h"
28 #include "gtest/gtest.h"
29
30 namespace clang {
31 namespace clangd {
32 namespace {
33
34 using ::testing::AllOf;
35 using ::testing::ElementsAre;
36 using ::testing::ElementsAreArray;
37
38 MATCHER_P(DeclNamed, Name, "") {
39 if (NamedDecl *ND = dyn_cast<NamedDecl>(arg))
40 if (ND->getName() == Name)
41 return true;
42 if (auto *Stream = result_listener->stream()) {
43 llvm::raw_os_ostream OS(*Stream);
44 arg->dump(OS);
45 }
46 return false;
47 }
48
49 // Matches if the Decl has template args equal to ArgName. If the decl is a
50 // NamedDecl and ArgName is an empty string it also matches.
51 MATCHER_P(WithTemplateArgs, ArgName, "") {
52 if (const FunctionDecl *FD = dyn_cast<FunctionDecl>(arg)) {
53 if (const auto *Args = FD->getTemplateSpecializationArgs()) {
54 std::string SpecializationArgs;
55 // Without the PrintingPolicy "bool" will be printed as "_Bool".
56 LangOptions LO;
57 PrintingPolicy Policy(LO);
58 Policy.adjustForCPlusPlus();
59 for (const auto &Arg : Args->asArray()) {
60 if (SpecializationArgs.size() > 0)
61 SpecializationArgs += ",";
62 SpecializationArgs += Arg.getAsType().getAsString(Policy);
63 }
64 if (Args->size() == 0)
65 return ArgName == SpecializationArgs;
66 return ArgName == "<" + SpecializationArgs + ">";
67 }
68 }
69 if (const NamedDecl *ND = dyn_cast<NamedDecl>(arg))
70 return printTemplateSpecializationArgs(*ND) == ArgName;
71 return false;
72 }
73
74 TEST(ParsedASTTest, TopLevelDecls) {
75 TestTU TU;
76 TU.HeaderCode = R"(
77 int header1();
78 int header2;
79 )";
80 TU.Code = "int main();";
81 auto AST = TU.build();
82 EXPECT_THAT(AST.getLocalTopLevelDecls(), ElementsAre(DeclNamed("main")));
83 }
84
85 TEST(ParsedASTTest, DoesNotGetIncludedTopDecls) {
86 TestTU TU;
87 TU.HeaderCode = R"cpp(
88 #define LL void foo(){}
89 template<class T>
90 struct H {
91 H() {}
92 LL
93 };
94 )cpp";
95 TU.Code = R"cpp(
96 int main() {
97 H<int> h;
98 h.foo();
99 }
100 )cpp";
101 auto AST = TU.build();
102 EXPECT_THAT(AST.getLocalTopLevelDecls(), ElementsAre(DeclNamed("main")));
103 }
104
105 TEST(ParsedASTTest, DoesNotGetImplicitTemplateTopDecls) {
106 TestTU TU;
107 TU.Code = R"cpp(
108 template<typename T>
109 void f(T) {}
110 void s() {
111 f(10UL);
112 }
113 )cpp";
114
115 auto AST = TU.build();
116 EXPECT_THAT(AST.getLocalTopLevelDecls(),
117 ElementsAre(DeclNamed("f"), DeclNamed("s")));
118 }
119
120 TEST(ParsedASTTest,
121 GetsExplicitInstantiationAndSpecializationTemplateTopDecls) {
122 TestTU TU;
123 TU.Code = R"cpp(
124 template <typename T>
125 void f(T) {}
126 template<>
127 void f(bool);
128 template void f(double);
129
130 template <class T>
131 struct V {};
132 template<class T>
133 struct V<T*> {};
134 template <>
135 struct V<bool> {};
136
137 template<class T>
138 T foo = T(10);
139 int i = foo<int>;
140 double d = foo<double>;
141
142 template <class T>
143 int foo<T*> = 0;
144 template <>
145 int foo<bool> = 0;
146 )cpp";
147 // FIXME: Auto-completion in a template requires disabling delayed template
148 // parsing.
149 TU.ExtraArgs.push_back("-fno-delayed-template-parsing");
150
151 auto AST = TU.build();
152 EXPECT_THAT(
153 AST.getLocalTopLevelDecls(),
154 ElementsAreArray({AllOf(DeclNamed("f"), WithTemplateArgs("")),
155 AllOf(DeclNamed("f"), WithTemplateArgs("<bool>")),
156 AllOf(DeclNamed("f"), WithTemplateArgs("<double>")),
157 AllOf(DeclNamed("V"), WithTemplateArgs("")),
158 AllOf(DeclNamed("V"), WithTemplateArgs("<T *>")),
159 AllOf(DeclNamed("V"), WithTemplateArgs("<bool>")),
160 AllOf(DeclNamed("foo"), WithTemplateArgs("")),
161 AllOf(DeclNamed("i"), WithTemplateArgs("")),
162 AllOf(DeclNamed("d"), WithTemplateArgs("")),
163 AllOf(DeclNamed("foo"), WithTemplateArgs("<T *>")),
164 AllOf(DeclNamed("foo"), WithTemplateArgs("<bool>"))}));
165 }
166
167 TEST(ParsedASTTest, TokensAfterPreamble) {
168 TestTU TU;
169 TU.AdditionalFiles["foo.h"] = R"(
170 int foo();
171 )";
172 TU.Code = R"cpp(
173 #include "foo.h"
174 first_token;
175 void test() {
176 }
177 last_token
178 )cpp";
179 auto AST = TU.build();
180 const syntax::TokenBuffer &T = AST.getTokens();
181 const auto &SM = AST.getSourceManager();
182
183 ASSERT_GT(T.expandedTokens().size(), 2u);
184 // Check first token after the preamble.
185 EXPECT_EQ(T.expandedTokens().front().text(SM), "first_token");
186 // Last token is always 'eof'.
187 EXPECT_EQ(T.expandedTokens().back().kind(), tok::eof);
188 // Check the token before 'eof'.
189 EXPECT_EQ(T.expandedTokens().drop_back().back().text(SM), "last_token");
190
191 // The spelled tokens for the main file should have everything.
192 auto Spelled = T.spelledTokens(SM.getMainFileID());
193 ASSERT_FALSE(Spelled.empty());
194 EXPECT_EQ(Spelled.front().kind(), tok::hash);
195 EXPECT_EQ(Spelled.back().text(SM), "last_token");
196 }
197
198 TEST(ParsedASTTest, NoCrashOnTokensWithTidyCheck) {
199 TestTU TU;
200 // this check runs the preprocessor, we need to make sure it does not break
201 // our recording logic.
202 TU.ClangTidyChecks = "modernize-use-trailing-return-type";
203 TU.Code = "inline int foo() {}";
204
205 auto AST = TU.build();
206 const syntax::TokenBuffer &T = AST.getTokens();
207 const auto &SM = AST.getSourceManager();
208
209 ASSERT_GT(T.expandedTokens().size(), 7u);
210 // Check first token after the preamble.
211 EXPECT_EQ(T.expandedTokens().front().text(SM), "inline");
212 // Last token is always 'eof'.
213 EXPECT_EQ(T.expandedTokens().back().kind(), tok::eof);
214 // Check the token before 'eof'.
215 EXPECT_EQ(T.expandedTokens().drop_back().back().text(SM), "}");
216 }
217
218 TEST(ParsedASTTest, CanBuildInvocationWithUnknownArgs) {
219 // Unknown flags should not prevent a build of compiler invocation.
220 ParseInputs Inputs;
221 Inputs.FS = buildTestFS({{testPath("foo.cpp"), "void test() {}"}});
222 Inputs.CompileCommand.CommandLine = {"clang", "-fsome-unknown-flag",
223 testPath("foo.cpp")};
224 IgnoreDiagnostics IgnoreDiags;
225 EXPECT_NE(buildCompilerInvocation(Inputs, IgnoreDiags), nullptr);
226
227 // Unknown forwarded to -cc1 should not a failure either.
228 Inputs.CompileCommand.CommandLine = {
229 "clang", "-Xclang", "-fsome-unknown-flag", testPath("foo.cpp")};
230 EXPECT_NE(buildCompilerInvocation(Inputs, IgnoreDiags), nullptr);
231 }
232
233 TEST(ParsedASTTest, CollectsMainFileMacroExpansions) {
234 Annotations TestCase(R"cpp(
235 #define ^MACRO_ARGS(X, Y) X Y
236 // - preamble ends
237 ^ID(int A);
238 // Macro arguments included.
239 ^MACRO_ARGS(^MACRO_ARGS(^MACRO_EXP(int), A), ^ID(= 2));
240
241 // Macro names inside other macros not included.
242 #define ^MACRO_ARGS2(X, Y) X Y
243 #define ^FOO BAR
244 #define ^BAR 1
245 int A = ^FOO;
246
247 // Macros from token concatenations not included.
248 #define ^CONCAT(X) X##A()
249 #define ^PREPEND(X) MACRO##X()
250 #define ^MACROA() 123
251 int B = ^CONCAT(MACRO);
252 int D = ^PREPEND(A)
253
254 // Macros included not from preamble not included.
255 #include "foo.inc"
256
257 #define ^assert(COND) if (!(COND)) { printf("%s", #COND); exit(0); }
258
259 void test() {
260 // Includes macro expansions in arguments that are expressions
261 ^assert(0 <= ^BAR);
262 }
263
264 #ifdef ^UNDEFINED
265 #endif
266
267 #define ^MULTIPLE_DEFINITION 1
268 #undef ^MULTIPLE_DEFINITION
269
270 #define ^MULTIPLE_DEFINITION 2
271 #undef ^MULTIPLE_DEFINITION
272 )cpp");
273 auto TU = TestTU::withCode(TestCase.code());
274 TU.HeaderCode = R"cpp(
275 #define ID(X) X
276 #define MACRO_EXP(X) ID(X)
277 MACRO_EXP(int B);
278 )cpp";
279 TU.AdditionalFiles["foo.inc"] = R"cpp(
280 int C = ID(1);
281 #define DEF 1
282 int D = DEF;
283 )cpp";
284 ParsedAST AST = TU.build();
285 std::vector<Position> MacroExpansionPositions;
286 for (const auto &SIDToRefs : AST.getMacros().MacroRefs) {
287 for (const auto &R : SIDToRefs.second)
288 MacroExpansionPositions.push_back(R.start);
289 }
290 for (const auto &R : AST.getMacros().UnknownMacros)
291 MacroExpansionPositions.push_back(R.start);
292 EXPECT_THAT(MacroExpansionPositions,
293 testing::UnorderedElementsAreArray(TestCase.points()));
294 }
295
296 } // namespace
297 } // namespace clangd
298 } // namespace clang
clang::clangd::MATCHER_P
MATCHER_P(Named, N, "")
Definition: BackgroundIndexTests.cpp:26
AST.h
clang::clangd::TestTU::HeaderCode
std::string HeaderCode
Definition: TestTU.h:50
SourceCode.h
clang::clangd::ParsedAST::getLocalTopLevelDecls
ArrayRef< Decl * > getLocalTopLevelDecls()
This function returns top-level decls present in the main file of the AST.
Definition: ParsedAST.cpp:440
TestFS.h
clang::clangd::printTemplateSpecializationArgs
std::string printTemplateSpecializationArgs(const NamedDecl &ND)
Prints template arguments of a decl as written in the source code, including enclosing &#39;<&#39; and &#39;>&#39;...
Definition: AST.cpp:249
TestTU.h
clang::clangd::ParsedAST::getTokens
const syntax::TokenBuffer & getTokens() const
Tokens recorded while parsing the main file.
Definition: ParsedAST.h:102
clang::clangd::buildTestFS
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > buildTestFS(llvm::StringMap< std::string > const &Files, llvm::StringMap< time_t > const &Timestamps)
Definition: TestFS.cpp:22
Annotations.h
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:647
IgnoreDiags
IgnoringDiagConsumer IgnoreDiags
Definition: HeadersTests.cpp:124
clang::clangd::testPath
std::string testPath(PathRef File)
Definition: TestFS.cpp:82
Compiler.h
Diagnostics.h
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:27
clang::clangd::TestTU::withCode
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:33
clang::clangd::ParsedAST::getSourceManager
SourceManager & getSourceManager()
Definition: ParsedAST.h:73
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
ParsedAST.h
clang::clangd::buildCompilerInvocation
std::unique_ptr< CompilerInvocation > buildCompilerInvocation(const ParseInputs &Inputs, clang::DiagnosticConsumer &D, std::vector< std::string > *CC1Args)
Builds compiler invocation that could be used to build AST or preamble.
Definition: Compiler.cpp:44
clang::clangd::ParsedAST::build
static llvm::Optional< ParsedAST > build(std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, const SymbolIndex *Index, const ParseOptions &Opts)
Attempts to run Clang and store parsed AST.
Definition: ParsedAST.cpp:218