clang-tools  9.0.0
SemanticHighlightingTests.cpp
Go to the documentation of this file.
1 //==- SemanticHighlightingTests.cpp - SemanticHighlighting 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 "ClangdServer.h"
11 #include "Protocol.h"
12 #include "SemanticHighlighting.h"
13 #include "TestFS.h"
14 #include "TestTU.h"
15 #include "gmock/gmock.h"
16 
17 namespace clang {
18 namespace clangd {
19 namespace {
20 
21 std::vector<HighlightingToken>
22 makeHighlightingTokens(llvm::ArrayRef<Range> Ranges, HighlightingKind Kind) {
23  std::vector<HighlightingToken> Tokens(Ranges.size());
24  for (int I = 0, End = Ranges.size(); I < End; ++I) {
25  Tokens[I].R = Ranges[I];
26  Tokens[I].Kind = Kind;
27  }
28 
29  return Tokens;
30 }
31 
32 void checkHighlightings(llvm::StringRef Code) {
33  Annotations Test(Code);
34  auto AST = TestTU::withCode(Test.code()).build();
35  static const std::map<HighlightingKind, std::string> KindToString{
36  {HighlightingKind::Variable, "Variable"},
37  {HighlightingKind::Function, "Function"},
38  {HighlightingKind::Class, "Class"},
39  {HighlightingKind::Enum, "Enum"},
40  {HighlightingKind::Namespace, "Namespace"},
41  {HighlightingKind::EnumConstant, "EnumConstant"},
42  {HighlightingKind::Field, "Field"},
43  {HighlightingKind::Method, "Method"},
44  {HighlightingKind::TemplateParameter, "TemplateParameter"}};
45  std::vector<HighlightingToken> ExpectedTokens;
46  for (const auto &KindString : KindToString) {
47  std::vector<HighlightingToken> Toks = makeHighlightingTokens(
48  Test.ranges(KindString.second), KindString.first);
49  ExpectedTokens.insert(ExpectedTokens.end(), Toks.begin(), Toks.end());
50  }
51 
52  auto ActualTokens = getSemanticHighlightings(AST);
53  EXPECT_THAT(ActualTokens, testing::UnorderedElementsAreArray(ExpectedTokens));
54 }
55 
56 TEST(SemanticHighlighting, GetsCorrectTokens) {
57  const char *TestCases[] = {
58  R"cpp(
59  struct $Class[[AS]] {
60  double $Field[[SomeMember]];
61  };
62  struct {
63  } $Variable[[S]];
64  void $Function[[foo]](int $Variable[[A]], $Class[[AS]] $Variable[[As]]) {
65  auto $Variable[[VeryLongVariableName]] = 12312;
66  $Class[[AS]] $Variable[[AA]];
67  auto $Variable[[L]] = $Variable[[AA]].$Field[[SomeMember]] + $Variable[[A]];
68  auto $Variable[[FN]] = [ $Variable[[AA]]](int $Variable[[A]]) -> void {};
69  $Variable[[FN]](12312);
70  }
71  )cpp",
72  R"cpp(
73  void $Function[[foo]](int);
74  void $Function[[Gah]]();
75  void $Function[[foo]]() {
76  auto $Variable[[Bou]] = $Function[[Gah]];
77  }
78  struct $Class[[A]] {
79  void $Method[[abc]]();
80  };
81  )cpp",
82  R"cpp(
83  namespace $Namespace[[abc]] {
84  template<typename $TemplateParameter[[T]]>
85  struct $Class[[A]] {
86  $TemplateParameter[[T]] $Field[[t]];
87  };
88  }
89  template<typename $TemplateParameter[[T]]>
90  struct $Class[[C]] : $Namespace[[abc]]::$Class[[A]]<$TemplateParameter[[T]]> {
91  typename $TemplateParameter[[T]]::A* $Field[[D]];
92  };
93  $Namespace[[abc]]::$Class[[A]]<int> $Variable[[AA]];
94  typedef $Namespace[[abc]]::$Class[[A]]<int> $Class[[AAA]];
95  struct $Class[[B]] {
96  $Class[[B]]();
97  ~$Class[[B]]();
98  void operator<<($Class[[B]]);
99  $Class[[AAA]] $Field[[AA]];
100  };
101  $Class[[B]]::$Class[[B]]() {}
102  $Class[[B]]::~$Class[[B]]() {}
103  void $Function[[f]] () {
104  $Class[[B]] $Variable[[BB]] = $Class[[B]]();
105  $Variable[[BB]].~$Class[[B]]();
106  $Class[[B]]();
107  }
108  )cpp",
109  R"cpp(
110  enum class $Enum[[E]] {
111  $EnumConstant[[A]],
112  $EnumConstant[[B]],
113  };
114  enum $Enum[[EE]] {
115  $EnumConstant[[Hi]],
116  };
117  struct $Class[[A]] {
118  $Enum[[E]] $Field[[EEE]];
119  $Enum[[EE]] $Field[[EEEE]];
120  };
121  int $Variable[[I]] = $EnumConstant[[Hi]];
122  $Enum[[E]] $Variable[[L]] = $Enum[[E]]::$EnumConstant[[B]];
123  )cpp",
124  R"cpp(
125  namespace $Namespace[[abc]] {
126  namespace {}
127  namespace $Namespace[[bcd]] {
128  struct $Class[[A]] {};
129  namespace $Namespace[[cde]] {
130  struct $Class[[A]] {
131  enum class $Enum[[B]] {
132  $EnumConstant[[Hi]],
133  };
134  };
135  }
136  }
137  }
138  using namespace $Namespace[[abc]]::$Namespace[[bcd]];
139  namespace $Namespace[[vwz]] =
140  $Namespace[[abc]]::$Namespace[[bcd]]::$Namespace[[cde]];
141  $Namespace[[abc]]::$Namespace[[bcd]]::$Class[[A]] $Variable[[AA]];
142  $Namespace[[vwz]]::$Class[[A]]::$Enum[[B]] $Variable[[AAA]] =
143  $Namespace[[vwz]]::$Class[[A]]::$Enum[[B]]::$EnumConstant[[Hi]];
144  ::$Namespace[[vwz]]::$Class[[A]] $Variable[[B]];
145  ::$Namespace[[abc]]::$Namespace[[bcd]]::$Class[[A]] $Variable[[BB]];
146  )cpp",
147  R"cpp(
148  struct $Class[[D]] {
149  double $Field[[C]];
150  };
151  struct $Class[[A]] {
152  double $Field[[B]];
153  $Class[[D]] $Field[[E]];
154  static double $Variable[[S]];
155  void $Method[[foo]]() {
156  $Field[[B]] = 123;
157  this->$Field[[B]] = 156;
158  this->$Method[[foo]]();
159  $Method[[foo]]();
160  $Variable[[S]] = 90.1;
161  }
162  };
163  void $Function[[foo]]() {
164  $Class[[A]] $Variable[[AA]];
165  $Variable[[AA]].$Field[[B]] += 2;
166  $Variable[[AA]].$Method[[foo]]();
167  $Variable[[AA]].$Field[[E]].$Field[[C]];
168  $Class[[A]]::$Variable[[S]] = 90;
169  }
170  )cpp",
171  R"cpp(
172  struct $Class[[AA]] {
173  int $Field[[A]];
174  }
175  int $Variable[[B]];
176  $Class[[AA]] $Variable[[A]]{$Variable[[B]]};
177  )cpp",
178  R"cpp(
179  namespace $Namespace[[a]] {
180  struct $Class[[A]] {};
181  }
182  typedef $Namespace[[a]]::$Class[[A]] $Class[[B]];
183  using $Class[[BB]] = $Namespace[[a]]::$Class[[A]];
184  enum class $Enum[[E]] {};
185  typedef $Enum[[E]] $Enum[[C]];
186  typedef $Enum[[C]] $Enum[[CC]];
187  using $Enum[[CD]] = $Enum[[CC]];
188  $Enum[[CC]] $Function[[f]]($Class[[B]]);
189  $Enum[[CD]] $Function[[f]]($Class[[BB]]);
190  )cpp",
191  R"cpp(
192  template<typename $TemplateParameter[[T]], typename = void>
193  class $Class[[A]] {
194  $TemplateParameter[[T]] $Field[[AA]];
195  $TemplateParameter[[T]] $Method[[foo]]();
196  };
197  template<class $TemplateParameter[[TT]]>
198  class $Class[[B]] {
199  $Class[[A]]<$TemplateParameter[[TT]]> $Field[[AA]];
200  };
201  template<class $TemplateParameter[[TT]], class $TemplateParameter[[GG]]>
202  class $Class[[BB]] {};
203  template<class $TemplateParameter[[T]]>
204  class $Class[[BB]]<$TemplateParameter[[T]], int> {};
205  template<class $TemplateParameter[[T]]>
206  class $Class[[BB]]<$TemplateParameter[[T]], $TemplateParameter[[T]]*> {};
207 
208  template<template<class> class $TemplateParameter[[T]], class $TemplateParameter[[C]]>
209  $TemplateParameter[[T]]<$TemplateParameter[[C]]> $Function[[f]]();
210 
211  template<typename>
212  class $Class[[Foo]] {};
213 
214  template<typename $TemplateParameter[[T]]>
215  void $Function[[foo]]($TemplateParameter[[T]] ...);
216  )cpp"};
217  for (const auto &TestCase : TestCases) {
218  checkHighlightings(TestCase);
219  }
220 }
221 
222 TEST(SemanticHighlighting, GeneratesHighlightsWhenFileChange) {
223  class HighlightingsCounterDiagConsumer : public DiagnosticsConsumer {
224  public:
225  std::atomic<int> Count = {0};
226 
227  void onDiagnosticsReady(PathRef, std::vector<Diag>) override {}
228  void onHighlightingsReady(
229  PathRef File, std::vector<HighlightingToken> Highlightings) override {
230  ++Count;
231  }
232  };
233 
234  auto FooCpp = testPath("foo.cpp");
235  MockFSProvider FS;
236  FS.Files[FooCpp] = "";
237 
238  MockCompilationDatabase MCD;
239  HighlightingsCounterDiagConsumer DiagConsumer;
240  ClangdServer Server(MCD, FS, DiagConsumer, ClangdServer::optsForTest());
241  Server.addDocument(FooCpp, "int a;");
242  ASSERT_TRUE(Server.blockUntilIdleForTest()) << "Waiting for server";
243  ASSERT_EQ(DiagConsumer.Count, 1);
244 }
245 
246 TEST(SemanticHighlighting, toSemanticHighlightingInformation) {
247  auto CreatePosition = [](int Line, int Character) -> Position {
248  Position Pos;
249  Pos.line = Line;
250  Pos.character = Character;
251  return Pos;
252  };
253 
254  std::vector<HighlightingToken> Tokens{
256  Range{CreatePosition(3, 8), CreatePosition(3, 12)}},
258  Range{CreatePosition(3, 4), CreatePosition(3, 7)}},
260  Range{CreatePosition(1, 1), CreatePosition(1, 5)}}};
261  std::vector<SemanticHighlightingInformation> ActualResults =
263  std::vector<SemanticHighlightingInformation> ExpectedResults = {
264  {1, "AAAAAQAEAAA="},
265  {3, "AAAACAAEAAAAAAAEAAMAAQ=="}};
266  EXPECT_EQ(ActualResults, ExpectedResults);
267 }
268 
269 } // namespace
270 } // namespace clangd
271 } // namespace clang
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:23
MockFSProvider FS
static Options optsForTest()
BindArgumentKind Kind
TEST(BackgroundQueueTest, Priority)
std::string testPath(PathRef File)
Definition: TestFS.cpp:82
StringRef Tokens
std::vector< SemanticHighlightingInformation > toSemanticHighlightingInformation(llvm::ArrayRef< HighlightingToken > Tokens)
static TestTU withCode(llvm::StringRef Code)
Definition: TestTU.h:33
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
ClangdServer Server
CharSourceRange Range
SourceRange for the file name.
std::vector< HighlightingToken > getSemanticHighlightings(ParsedAST &AST)
IgnoreDiagnostics DiagConsumer