clang-tools  14.0.0git
ASTTests.cpp
Go to the documentation of this file.
1 //===-- ASTTests.cpp --------------------------------------------*- C++ -*-===//
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 "AST.h"
10 
11 #include "Annotations.h"
12 #include "ParsedAST.h"
13 #include "TestTU.h"
14 #include "clang/AST/ASTTypeTraits.h"
15 #include "clang/AST/Attr.h"
16 #include "clang/AST/Decl.h"
17 #include "clang/AST/DeclBase.h"
18 #include "clang/Basic/AttrKinds.h"
19 #include "clang/Basic/SourceManager.h"
20 #include "llvm/ADT/StringRef.h"
21 #include "llvm/Support/Casting.h"
22 #include "gmock/gmock.h"
23 #include "gtest/gtest.h"
24 #include <cstddef>
25 #include <string>
26 #include <vector>
27 
28 namespace clang {
29 namespace clangd {
30 namespace {
31 using testing::Contains;
32 using testing::Each;
33 
34 TEST(GetDeducedType, KwAutoKwDecltypeExpansion) {
35  struct Test {
36  StringRef AnnotatedCode;
37  const char *DeducedType;
38  } Tests[] = {
39  {"^auto i = 0;", "int"},
40  {"^auto f(){ return 1;};", "int"},
41  {
42  R"cpp( // auto on struct in a namespace
43  namespace ns1 { struct S {}; }
44  ^auto v = ns1::S{};
45  )cpp",
46  "struct ns1::S",
47  },
48  {
49  R"cpp( // decltype on struct
50  namespace ns1 { struct S {}; }
51  ns1::S i;
52  ^decltype(i) j;
53  )cpp",
54  "ns1::S",
55  },
56  {
57  R"cpp(// decltype(auto) on struct&
58  namespace ns1 {
59  struct S {};
60  } // namespace ns1
61 
62  ns1::S i;
63  ns1::S& j = i;
64  ^decltype(auto) k = j;
65  )cpp",
66  "struct ns1::S &",
67  },
68  {
69  R"cpp( // auto on template class
70  class X;
71  template<typename T> class Foo {};
72  ^auto v = Foo<X>();
73  )cpp",
74  "class Foo<class X>",
75  },
76  {
77  R"cpp( // auto on initializer list.
78  namespace std
79  {
80  template<class _E>
81  class [[initializer_list]] {};
82  }
83 
84  ^auto i = {1,2};
85  )cpp",
86  "class std::initializer_list<int>",
87  },
88  {
89  R"cpp( // auto in function return type with trailing return type
90  struct Foo {};
91  ^auto test() -> decltype(Foo()) {
92  return Foo();
93  }
94  )cpp",
95  "struct Foo",
96  },
97  {
98  R"cpp( // decltype in trailing return type
99  struct Foo {};
100  auto test() -> ^decltype(Foo()) {
101  return Foo();
102  }
103  )cpp",
104  "struct Foo",
105  },
106  {
107  R"cpp( // auto in function return type
108  struct Foo {};
109  ^auto test() {
110  return Foo();
111  }
112  )cpp",
113  "struct Foo",
114  },
115  {
116  R"cpp( // auto& in function return type
117  struct Foo {};
118  ^auto& test() {
119  static Foo x;
120  return x;
121  }
122  )cpp",
123  "struct Foo",
124  },
125  {
126  R"cpp( // auto* in function return type
127  struct Foo {};
128  ^auto* test() {
129  Foo *x;
130  return x;
131  }
132  )cpp",
133  "struct Foo",
134  },
135  {
136  R"cpp( // const auto& in function return type
137  struct Foo {};
138  const ^auto& test() {
139  static Foo x;
140  return x;
141  }
142  )cpp",
143  "struct Foo",
144  },
145  {
146  R"cpp( // decltype(auto) in function return (value)
147  struct Foo {};
148  ^decltype(auto) test() {
149  return Foo();
150  }
151  )cpp",
152  "struct Foo",
153  },
154  {
155  R"cpp( // decltype(auto) in function return (ref)
156  struct Foo {};
157  ^decltype(auto) test() {
158  static Foo x;
159  return (x);
160  }
161  )cpp",
162  "struct Foo &",
163  },
164  {
165  R"cpp( // decltype(auto) in function return (const ref)
166  struct Foo {};
167  ^decltype(auto) test() {
168  static const Foo x;
169  return (x);
170  }
171  )cpp",
172  "const struct Foo &",
173  },
174  {
175  R"cpp( // auto on alias
176  struct Foo {};
177  using Bar = Foo;
178  ^auto x = Bar();
179  )cpp",
180  // FIXME: it'd be nice if this resolved to the alias instead
181  "struct Foo",
182  },
183  };
184  for (Test T : Tests) {
185  Annotations File(T.AnnotatedCode);
186  auto AST = TestTU::withCode(File.code()).build();
187  SourceManagerForFile SM("foo.cpp", File.code());
188 
189  SCOPED_TRACE(File.code());
190  EXPECT_FALSE(File.points().empty());
191  for (Position Pos : File.points()) {
192  auto Location = sourceLocationInMainFile(SM.get(), Pos);
193  ASSERT_TRUE(!!Location) << llvm::toString(Location.takeError());
195  ASSERT_TRUE(DeducedType);
196  EXPECT_EQ(DeducedType->getAsString(), T.DeducedType);
197  }
198  }
199 }
200 
201 TEST(ClangdAST, GetQualification) {
202  // Tries to insert the decl `Foo` into position of each decl named `insert`.
203  // This is done to get an appropriate DeclContext for the insertion location.
204  // Qualifications are the required nested name specifier to spell `Foo` at the
205  // `insert`ion location.
206  // VisibleNamespaces are assumed to be visible at every insertion location.
207  const struct {
208  llvm::StringRef Test;
209  std::vector<llvm::StringRef> Qualifications;
210  std::vector<std::string> VisibleNamespaces;
211  } Cases[] = {
212  {
213  R"cpp(
214  namespace ns1 { namespace ns2 { class Foo {}; } }
215  void insert(); // ns1::ns2::Foo
216  namespace ns1 {
217  void insert(); // ns2::Foo
218  namespace ns2 {
219  void insert(); // Foo
220  }
221  using namespace ns2;
222  void insert(); // Foo
223  }
224  using namespace ns1;
225  void insert(); // ns2::Foo
226  using namespace ns2;
227  void insert(); // Foo
228  )cpp",
229  {"ns1::ns2::", "ns2::", "", "", "ns2::", ""},
230  {},
231  },
232  {
233  R"cpp(
234  namespace ns1 { namespace ns2 { class Bar { void Foo(); }; } }
235  void insert(); // ns1::ns2::Bar::Foo
236  namespace ns1 {
237  void insert(); // ns2::Bar::Foo
238  namespace ns2 {
239  void insert(); // Bar::Foo
240  }
241  using namespace ns2;
242  void insert(); // Bar::Foo
243  }
244  using namespace ns1;
245  void insert(); // ns2::Bar::Foo
246  using namespace ns2;
247  void insert(); // Bar::Foo
248  )cpp",
249  {"ns1::ns2::Bar::", "ns2::Bar::", "Bar::", "Bar::", "ns2::Bar::",
250  "Bar::"},
251  {},
252  },
253  {
254  R"cpp(
255  namespace ns1 { namespace ns2 { void Foo(); } }
256  void insert(); // ns2::Foo
257  namespace ns1 {
258  void insert(); // ns2::Foo
259  namespace ns2 {
260  void insert(); // Foo
261  }
262  }
263  )cpp",
264  {"ns2::", "ns2::", ""},
265  {"ns1::"},
266  },
267  {
268  R"cpp(
269  namespace ns {
270  extern "C" {
271  typedef int Foo;
272  }
273  }
274  void insert(); // ns::Foo
275  )cpp",
276  {"ns::"},
277  {},
278  },
279  };
280  for (const auto &Case : Cases) {
281  Annotations Test(Case.Test);
282  TestTU TU = TestTU::withCode(Test.code());
283  ParsedAST AST = TU.build();
284  std::vector<const Decl *> InsertionPoints;
285  const NamedDecl *TargetDecl;
286  findDecl(AST, [&](const NamedDecl &ND) {
287  if (ND.getNameAsString() == "Foo") {
288  TargetDecl = &ND;
289  return true;
290  }
291 
292  if (ND.getNameAsString() == "insert")
293  InsertionPoints.push_back(&ND);
294  return false;
295  });
296 
297  ASSERT_EQ(InsertionPoints.size(), Case.Qualifications.size());
298  for (size_t I = 0, E = InsertionPoints.size(); I != E; ++I) {
299  const Decl *D = InsertionPoints[I];
300  if (Case.VisibleNamespaces.empty()) {
301  EXPECT_EQ(getQualification(AST.getASTContext(),
302  D->getLexicalDeclContext(), D->getBeginLoc(),
303  TargetDecl),
304  Case.Qualifications[I]);
305  } else {
306  EXPECT_EQ(getQualification(AST.getASTContext(),
307  D->getLexicalDeclContext(), TargetDecl,
308  Case.VisibleNamespaces),
309  Case.Qualifications[I]);
310  }
311  }
312  }
313 }
314 
315 TEST(ClangdAST, PrintType) {
316  const struct {
317  llvm::StringRef Test;
318  std::vector<llvm::StringRef> Types;
319  } Cases[] = {
320  {
321  R"cpp(
322  namespace ns1 { namespace ns2 { class Foo {}; } }
323  void insert(); // ns1::ns2::Foo
324  namespace ns1 {
325  void insert(); // ns2::Foo
326  namespace ns2 {
327  void insert(); // Foo
328  }
329  }
330  )cpp",
331  {"ns1::ns2::Foo", "ns2::Foo", "Foo"},
332  },
333  {
334  R"cpp(
335  namespace ns1 {
336  typedef int Foo;
337  }
338  void insert(); // ns1::Foo
339  namespace ns1 {
340  void insert(); // Foo
341  }
342  )cpp",
343  {"ns1::Foo", "Foo"},
344  },
345  };
346  for (const auto &Case : Cases) {
347  Annotations Test(Case.Test);
348  TestTU TU = TestTU::withCode(Test.code());
349  ParsedAST AST = TU.build();
350  std::vector<const DeclContext *> InsertionPoints;
351  const TypeDecl *TargetDecl = nullptr;
352  findDecl(AST, [&](const NamedDecl &ND) {
353  if (ND.getNameAsString() == "Foo") {
354  if (const auto *TD = llvm::dyn_cast<TypeDecl>(&ND)) {
355  TargetDecl = TD;
356  return true;
357  }
358  } else if (ND.getNameAsString() == "insert")
359  InsertionPoints.push_back(ND.getDeclContext());
360  return false;
361  });
362 
363  ASSERT_EQ(InsertionPoints.size(), Case.Types.size());
364  for (size_t I = 0, E = InsertionPoints.size(); I != E; ++I) {
365  const auto *DC = InsertionPoints[I];
366  EXPECT_EQ(printType(AST.getASTContext().getTypeDeclType(TargetDecl), *DC),
367  Case.Types[I]);
368  }
369  }
370 }
371 
372 TEST(ClangdAST, IsDeeplyNested) {
373  Annotations Test(
374  R"cpp(
375  namespace ns {
376  class Foo {
377  void bar() {
378  class Bar {};
379  }
380  };
381  })cpp");
382  TestTU TU = TestTU::withCode(Test.code());
383  ParsedAST AST = TU.build();
384 
385  EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST, "Foo"), /*MaxDepth=*/1));
386  EXPECT_FALSE(
387  isDeeplyNested(&findUnqualifiedDecl(AST, "Foo"), /*MaxDepth=*/2));
388 
389  EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST, "bar"), /*MaxDepth=*/2));
390  EXPECT_FALSE(
391  isDeeplyNested(&findUnqualifiedDecl(AST, "bar"), /*MaxDepth=*/3));
392 
393  EXPECT_TRUE(isDeeplyNested(&findUnqualifiedDecl(AST, "Bar"), /*MaxDepth=*/3));
394  EXPECT_FALSE(
395  isDeeplyNested(&findUnqualifiedDecl(AST, "Bar"), /*MaxDepth=*/4));
396 }
397 
398 MATCHER_P(attrKind, K, "") { return arg->getKind() == K; }
399 
400 MATCHER(implicitAttr, "") { return arg->isImplicit(); }
401 
402 TEST(ClangdAST, GetAttributes) {
403  const char *Code = R"cpp(
404  class X{};
405  class [[nodiscard]] Y{};
406  void f(int * a, int * __attribute__((nonnull)) b);
407  void foo(bool c) {
408  if (c)
409  [[unlikely]] return;
410  }
411  )cpp";
412  ParsedAST AST = TestTU::withCode(Code).build();
413  auto DeclAttrs = [&](llvm::StringRef Name) {
414  return getAttributes(DynTypedNode::create(findUnqualifiedDecl(AST, Name)));
415  };
416  // Implicit attributes may be present (e.g. visibility on windows).
417  ASSERT_THAT(DeclAttrs("X"), Each(implicitAttr()));
418  ASSERT_THAT(DeclAttrs("Y"), Contains(attrKind(attr::WarnUnusedResult)));
419  ASSERT_THAT(DeclAttrs("f"), Each(implicitAttr()));
420  ASSERT_THAT(DeclAttrs("a"), Each(implicitAttr()));
421  ASSERT_THAT(DeclAttrs("b"), Contains(attrKind(attr::NonNull)));
422 
423  Stmt *FooBody = cast<FunctionDecl>(findDecl(AST, "foo")).getBody();
424  IfStmt *FooIf = cast<IfStmt>(cast<CompoundStmt>(FooBody)->body_front());
425  ASSERT_THAT(getAttributes(DynTypedNode::create(*FooIf)),
426  Each(implicitAttr()));
427  ASSERT_THAT(getAttributes(DynTypedNode::create(*FooIf->getThen())),
428  Contains(attrKind(attr::Unlikely)));
429 }
430 
431 } // namespace
432 } // namespace clangd
433 } // namespace clang
clang::clangd::findDecl
const NamedDecl & findDecl(ParsedAST &AST, llvm::StringRef QName)
Definition: TestTU.cpp:215
clang::clangd::TEST
TEST(BackgroundQueueTest, Priority)
Definition: BackgroundIndexTests.cpp:751
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
clang::clangd::TestTU::build
ParsedAST build() const
Definition: TestTU.cpp:109
Location
Definition: Modularize.cpp:382
clang::clangd::ParsedAST::getASTContext
ASTContext & getASTContext()
Note that the returned ast will not contain decls from the preamble that were not deserialized during...
Definition: ParsedAST.cpp:539
TestTU.h
clang::clangd::getDeducedType
llvm::Optional< QualType > getDeducedType(ASTContext &ASTCtx, SourceLocation Loc)
Retrieves the deduced type at a given location (auto, decltype).
Definition: AST.cpp:475
clang::clangd::findUnqualifiedDecl
const NamedDecl & findUnqualifiedDecl(ParsedAST &AST, llvm::StringRef Name)
Definition: TestTU.cpp:256
clang::clangd::ParsedAST::build
static llvm::Optional< ParsedAST > build(llvm::StringRef Filename, const ParseInputs &Inputs, std::unique_ptr< clang::CompilerInvocation > CI, llvm::ArrayRef< Diag > CompilerInvocationDiags, std::shared_ptr< const PreambleData > Preamble)
Attempts to run Clang and store the parsed AST.
Definition: ParsedAST.cpp:248
AnnotatedCode
std::string AnnotatedCode
Definition: FindTargetTests.cpp:1026
Code
std::string Code
Definition: FindTargetTests.cpp:67
ns1::ns2::D
@ D
Definition: CategoricalFeature.h:3
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
if
if(CLANGD_ENABLE_REMOTE) generate_protos(RemoteIndexProto "Index.proto") generate_protos(MonitoringServiceProto "MonitoringService.proto" GRPC) generate_protos(RemoteIndexServiceProto "Service.proto" DEPENDS "Index.proto" GRPC) target_link_libraries(RemoteIndexServiceProto PRIVATE RemoteIndexProto MonitoringServiceProto) include_directories($
Definition: clangd/index/remote/CMakeLists.txt:1
Name
static constexpr llvm::StringLiteral Name
Definition: UppercaseLiteralSuffixCheck.cpp:28
clang::clangd::TestTU::withCode
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:37
Annotations.h
clang::clangd::getQualification
std::string getQualification(ASTContext &Context, const DeclContext *DestContext, SourceLocation InsertionPoint, const NamedDecl *ND)
Gets the nested name specifier necessary for spelling ND in DestContext, at InsertionPoint.
Definition: AST.cpp:510
DeducedType
QualType DeducedType
Definition: AST.cpp:471
clang::clangd::isDeeplyNested
bool isDeeplyNested(const Decl *D, unsigned MaxDepth)
Checks whether D is more than MaxDepth away from translation unit scope.
Definition: AST.cpp:556
clang::clangd::MATCHER_P
MATCHER_P(Named, N, "")
Definition: BackgroundIndexTests.cpp:31
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::tidy::cppcoreguidelines::toString
static llvm::StringRef toString(SpecialMemberFunctionsCheck::SpecialMemberFunctionKind K)
Definition: SpecialMemberFunctionsCheck.cpp:55
SM
const SourceManager & SM
Definition: IncludeCleaner.cpp:108
Pos
Position Pos
Definition: SourceCode.cpp:657
clang::clangd::getAttributes
std::vector< const Attr * > getAttributes(const DynTypedNode &N)
Return attributes attached directly to a node.
Definition: AST.cpp:486
clang::clangd::printType
std::string printType(const QualType QT, const DeclContext &CurContext)
Returns a QualType as string.
Definition: AST.cpp:354
clang::clangd::sourceLocationInMainFile
llvm::Expected< SourceLocation > sourceLocationInMainFile(const SourceManager &SM, Position P)
Return the file location, corresponding to P.
Definition: SourceCode.cpp:456
K
Kind K
Definition: Rename.cpp:442
clang::clangd::MATCHER
MATCHER(Declared, "")
Definition: BackgroundIndexTests.cpp:33
AST.h
ParsedAST.h