clang-tools  9.0.0
ClangdUnitTests.cpp
Go to the documentation of this file.
1 //===-- ClangdUnitTests.cpp - ClangdUnit tests ------------------*- 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 "Annotations.h"
10 #include "ClangdUnit.h"
11 #include "SourceCode.h"
12 #include "TestTU.h"
13 #include "clang/Basic/TokenKinds.h"
14 #include "clang/Tooling/Syntax/Tokens.h"
15 #include "llvm/Support/ScopedPrinter.h"
16 #include "gmock/gmock.h"
17 #include "gtest/gtest.h"
18 
19 namespace clang {
20 namespace clangd {
21 namespace {
22 
23 using ::testing::ElementsAre;
24 
25 TEST(ClangdUnitTest, GetBeginningOfIdentifier) {
26  std::string Preamble = R"cpp(
27 struct Bar { int func(); };
28 #define MACRO(X) void f() { X; }
29 Bar* bar;
30  )cpp";
31  // First ^ is the expected beginning, last is the search position.
32  for (std::string Text : std::vector<std::string>{
33  "int ^f^oo();", // inside identifier
34  "int ^foo();", // beginning of identifier
35  "int ^foo^();", // end of identifier
36  "int foo(^);", // non-identifier
37  "^int foo();", // beginning of file (can't back up)
38  "int ^f0^0();", // after a digit (lexing at N-1 is wrong)
39  "int ^λλ^λ();", // UTF-8 handled properly when backing up
40 
41  // identifier in macro arg
42  "MACRO(bar->^func())", // beginning of identifier
43  "MACRO(bar->^fun^c())", // inside identifier
44  "MACRO(bar->^func^())", // end of identifier
45  "MACRO(^bar->func())", // begin identifier
46  "MACRO(^bar^->func())", // end identifier
47  "^MACRO(bar->func())", // beginning of macro name
48  "^MAC^RO(bar->func())", // inside macro name
49  "^MACRO^(bar->func())", // end of macro name
50  }) {
51  std::string WithPreamble = Preamble + Text;
52  Annotations TestCase(WithPreamble);
53  auto AST = TestTU::withCode(TestCase.code()).build();
54  const auto &SourceMgr = AST.getSourceManager();
55  SourceLocation Actual = getBeginningOfIdentifier(
56  AST, TestCase.points().back(), SourceMgr.getMainFileID());
57  Position ActualPos = offsetToPosition(
58  TestCase.code(),
59  SourceMgr.getFileOffset(SourceMgr.getSpellingLoc(Actual)));
60  EXPECT_EQ(TestCase.points().front(), ActualPos) << Text;
61  }
62 }
63 
64 MATCHER_P(DeclNamed, Name, "") {
65  if (NamedDecl *ND = dyn_cast<NamedDecl>(arg))
66  if (ND->getName() == Name)
67  return true;
68  if (auto *Stream = result_listener->stream()) {
69  llvm::raw_os_ostream OS(*Stream);
70  arg->dump(OS);
71  }
72  return false;
73 }
74 
75 TEST(ClangdUnitTest, TopLevelDecls) {
76  TestTU TU;
77  TU.HeaderCode = R"(
78  int header1();
79  int header2;
80  )";
81  TU.Code = "int main();";
82  auto AST = TU.build();
83  EXPECT_THAT(AST.getLocalTopLevelDecls(), ElementsAre(DeclNamed("main")));
84 }
85 
86 TEST(ClangdUnitTest, DoesNotGetIncludedTopDecls) {
87  TestTU TU;
88  TU.HeaderCode = R"cpp(
89  #define LL void foo(){}
90  template<class T>
91  struct H {
92  H() {}
93  LL
94  };
95  )cpp";
96  TU.Code = R"cpp(
97  int main() {
98  H<int> h;
99  h.foo();
100  }
101  )cpp";
102  auto AST = TU.build();
103  EXPECT_THAT(AST.getLocalTopLevelDecls(), ElementsAre(DeclNamed("main")));
104 }
105 
106 TEST(ClangdUnitTest, TokensAfterPreamble) {
107  TestTU TU;
108  TU.AdditionalFiles["foo.h"] = R"(
109  int foo();
110  )";
111  TU.Code = R"cpp(
112  #include "foo.h"
113  first_token;
114  void test() {
115  }
116  last_token
117 )cpp";
118  auto AST = TU.build();
119  const syntax::TokenBuffer &T = AST.getTokens();
120  const auto &SM = AST.getSourceManager();
121 
122  ASSERT_GT(T.expandedTokens().size(), 2u);
123  // Check first token after the preamble.
124  EXPECT_EQ(T.expandedTokens().front().text(SM), "first_token");
125  // Last token is always 'eof'.
126  EXPECT_EQ(T.expandedTokens().back().kind(), tok::eof);
127  // Check the token before 'eof'.
128  EXPECT_EQ(T.expandedTokens().drop_back().back().text(SM), "last_token");
129 
130  // The spelled tokens for the main file should have everything.
131  auto Spelled = T.spelledTokens(SM.getMainFileID());
132  ASSERT_FALSE(Spelled.empty());
133  EXPECT_EQ(Spelled.front().kind(), tok::hash);
134  EXPECT_EQ(Spelled.back().text(SM), "last_token");
135 }
136 
137 
138 TEST(ClangdUnitTest, NoCrashOnTokensWithTidyCheck) {
139  TestTU TU;
140  // this check runs the preprocessor, we need to make sure it does not break
141  // our recording logic.
142  TU.ClangTidyChecks = "modernize-use-trailing-return-type";
143  TU.Code = "inline int foo() {}";
144 
145  auto AST = TU.build();
146  const syntax::TokenBuffer &T = AST.getTokens();
147  const auto &SM = AST.getSourceManager();
148 
149  ASSERT_GT(T.expandedTokens().size(), 7u);
150  // Check first token after the preamble.
151  EXPECT_EQ(T.expandedTokens().front().text(SM), "inline");
152  // Last token is always 'eof'.
153  EXPECT_EQ(T.expandedTokens().back().kind(), tok::eof);
154  // Check the token before 'eof'.
155  EXPECT_EQ(T.expandedTokens().drop_back().back().text(SM), "}");
156 }
157 
158 } // namespace
159 } // namespace clangd
160 } // namespace clang
MATCHER_P(Named, N, "")
std::string Text
static llvm::Optional< ParsedAST > build(std::unique_ptr< clang::CompilerInvocation > CI, std::shared_ptr< const PreambleData > Preamble, std::unique_ptr< llvm::MemoryBuffer > Buffer, IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS, const SymbolIndex *Index, const ParseOptions &Opts)
Attempts to run Clang and store parsed AST.
Definition: ClangdUnit.cpp:287
ArrayRef< Decl * > getLocalTopLevelDecls()
This function returns top-level decls present in the main file of the AST.
Definition: ClangdUnit.cpp:494
const syntax::TokenBuffer & getTokens() const
Tokens recorded while parsing the main file.
Definition: ClangdUnit.h:121
SourceLocation getBeginningOfIdentifier(const ParsedAST &Unit, const Position &Pos, const FileID FID)
Get the beginning SourceLocation at a specified Pos.
Definition: ClangdUnit.cpp:660
Position offsetToPosition(llvm::StringRef Code, size_t Offset)
Turn an offset in Code into a [line, column] pair.
Definition: SourceCode.cpp:174
TEST(BackgroundQueueTest, Priority)
static constexpr llvm::StringLiteral Name
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:33
SourceManager & getSourceManager()
Definition: ClangdUnit.h:99
const PreambleData * Preamble
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//