clang-tools  10.0.0git
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
MATCHER_P(Named, N, "")
std::string HeaderCode
Definition: TestTU.h:50
ArrayRef< Decl * > getLocalTopLevelDecls()
This function returns top-level decls present in the main file of the AST.
Definition: ParsedAST.cpp:440
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
const syntax::TokenBuffer & getTokens() const
Tokens recorded while parsing the main file.
Definition: ParsedAST.h:102
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > buildTestFS(llvm::StringMap< std::string > const &Files, llvm::StringMap< time_t > const &Timestamps)
Definition: TestFS.cpp:22
TEST(BackgroundQueueTest, Priority)
IgnoringDiagConsumer IgnoreDiags
std::string testPath(PathRef File)
Definition: TestFS.cpp:82
static constexpr llvm::StringLiteral Name
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:33
SourceManager & getSourceManager()
Definition: ParsedAST.h:73
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
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
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