1 | //===--- SemanticHighlighting.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 "SemanticHighlighting.h" |
10 | #include "Config.h" |
11 | #include "FindTarget.h" |
12 | #include "ParsedAST.h" |
13 | #include "Protocol.h" |
14 | #include "SourceCode.h" |
15 | #include "support/Logger.h" |
16 | #include "clang/AST/ASTContext.h" |
17 | #include "clang/AST/Decl.h" |
18 | #include "clang/AST/DeclCXX.h" |
19 | #include "clang/AST/DeclObjC.h" |
20 | #include "clang/AST/DeclTemplate.h" |
21 | #include "clang/AST/DeclarationName.h" |
22 | #include "clang/AST/ExprCXX.h" |
23 | #include "clang/AST/RecursiveASTVisitor.h" |
24 | #include "clang/AST/Type.h" |
25 | #include "clang/AST/TypeLoc.h" |
26 | #include "clang/Basic/LangOptions.h" |
27 | #include "clang/Basic/SourceLocation.h" |
28 | #include "clang/Basic/SourceManager.h" |
29 | #include "clang/Sema/HeuristicResolver.h" |
30 | #include "clang/Tooling/Syntax/Tokens.h" |
31 | #include "llvm/ADT/STLExtras.h" |
32 | #include "llvm/ADT/StringRef.h" |
33 | #include "llvm/Support/Casting.h" |
34 | #include "llvm/Support/Error.h" |
35 | #include <algorithm> |
36 | #include <optional> |
37 | |
38 | namespace clang { |
39 | namespace clangd { |
40 | namespace { |
41 | |
42 | /// Get the last Position on a given line. |
43 | llvm::Expected<Position> endOfLine(llvm::StringRef Code, int Line) { |
44 | auto StartOfLine = positionToOffset(Code, P: Position{.line: Line, .character: 0}); |
45 | if (!StartOfLine) |
46 | return StartOfLine.takeError(); |
47 | StringRef LineText = Code.drop_front(N: *StartOfLine).take_until(F: [](char C) { |
48 | return C == '\n'; |
49 | }); |
50 | return Position{.line: Line, .character: static_cast<int>(lspLength(Code: LineText))}; |
51 | } |
52 | |
53 | /// Some names are not written in the source code and cannot be highlighted, |
54 | /// e.g. anonymous classes. This function detects those cases. |
55 | bool canHighlightName(DeclarationName Name) { |
56 | switch (Name.getNameKind()) { |
57 | case DeclarationName::Identifier: { |
58 | auto *II = Name.getAsIdentifierInfo(); |
59 | return II && !II->getName().empty(); |
60 | } |
61 | case DeclarationName::CXXConstructorName: |
62 | case DeclarationName::CXXDestructorName: |
63 | return true; |
64 | case DeclarationName::ObjCZeroArgSelector: |
65 | case DeclarationName::ObjCOneArgSelector: |
66 | case DeclarationName::ObjCMultiArgSelector: |
67 | // Multi-arg selectors need special handling, and we handle 0/1 arg |
68 | // selectors there too. |
69 | return false; |
70 | case DeclarationName::CXXConversionFunctionName: |
71 | case DeclarationName::CXXOperatorName: |
72 | case DeclarationName::CXXDeductionGuideName: |
73 | case DeclarationName::CXXLiteralOperatorName: |
74 | case DeclarationName::CXXUsingDirective: |
75 | return false; |
76 | } |
77 | llvm_unreachable("invalid name kind" ); |
78 | } |
79 | |
80 | bool isUniqueDefinition(const NamedDecl *Decl) { |
81 | if (auto *Func = dyn_cast<FunctionDecl>(Val: Decl)) |
82 | return Func->isThisDeclarationADefinition(); |
83 | if (auto *Klass = dyn_cast<CXXRecordDecl>(Val: Decl)) |
84 | return Klass->isThisDeclarationADefinition(); |
85 | if (auto *Iface = dyn_cast<ObjCInterfaceDecl>(Val: Decl)) |
86 | return Iface->isThisDeclarationADefinition(); |
87 | if (auto *Proto = dyn_cast<ObjCProtocolDecl>(Val: Decl)) |
88 | return Proto->isThisDeclarationADefinition(); |
89 | if (auto *Var = dyn_cast<VarDecl>(Val: Decl)) |
90 | return Var->isThisDeclarationADefinition(); |
91 | return isa<TemplateTypeParmDecl>(Val: Decl) || |
92 | isa<NonTypeTemplateParmDecl>(Val: Decl) || |
93 | isa<TemplateTemplateParmDecl>(Val: Decl) || isa<ObjCCategoryDecl>(Val: Decl) || |
94 | isa<ObjCImplDecl>(Val: Decl); |
95 | } |
96 | |
97 | std::optional<HighlightingKind> kindForType(const Type *TP, |
98 | const HeuristicResolver *Resolver); |
99 | std::optional<HighlightingKind> kindForDecl(const NamedDecl *D, |
100 | const HeuristicResolver *Resolver) { |
101 | if (auto *USD = dyn_cast<UsingShadowDecl>(Val: D)) { |
102 | if (auto *Target = USD->getTargetDecl()) |
103 | D = Target; |
104 | } |
105 | if (auto *TD = dyn_cast<TemplateDecl>(Val: D)) { |
106 | if (auto *Templated = TD->getTemplatedDecl()) |
107 | D = Templated; |
108 | } |
109 | if (auto *TD = dyn_cast<TypedefNameDecl>(Val: D)) { |
110 | // We try to highlight typedefs as their underlying type. |
111 | if (auto K = |
112 | kindForType(TP: TD->getUnderlyingType().getTypePtrOrNull(), Resolver)) |
113 | return K; |
114 | // And fallback to a generic kind if this fails. |
115 | return HighlightingKind::Typedef; |
116 | } |
117 | // We highlight class decls, constructor decls and destructor decls as |
118 | // `Class` type. The destructor decls are handled in `VisitTagTypeLoc` (we |
119 | // will visit a TypeLoc where the underlying Type is a CXXRecordDecl). |
120 | if (auto *RD = llvm::dyn_cast<RecordDecl>(Val: D)) { |
121 | // We don't want to highlight lambdas like classes. |
122 | if (RD->isLambda()) |
123 | return std::nullopt; |
124 | return HighlightingKind::Class; |
125 | } |
126 | if (isa<ClassTemplateDecl, RecordDecl, CXXConstructorDecl, ObjCInterfaceDecl, |
127 | ObjCImplementationDecl>(Val: D)) |
128 | return HighlightingKind::Class; |
129 | if (isa<ObjCProtocolDecl>(Val: D)) |
130 | return HighlightingKind::Interface; |
131 | if (isa<ObjCCategoryDecl, ObjCCategoryImplDecl>(Val: D)) |
132 | return HighlightingKind::Namespace; |
133 | if (auto *MD = dyn_cast<CXXMethodDecl>(Val: D)) |
134 | return MD->isStatic() ? HighlightingKind::StaticMethod |
135 | : HighlightingKind::Method; |
136 | if (auto *OMD = dyn_cast<ObjCMethodDecl>(Val: D)) |
137 | return OMD->isClassMethod() ? HighlightingKind::StaticMethod |
138 | : HighlightingKind::Method; |
139 | if (isa<FieldDecl, IndirectFieldDecl, ObjCPropertyDecl>(Val: D)) |
140 | return HighlightingKind::Field; |
141 | if (isa<EnumDecl>(Val: D)) |
142 | return HighlightingKind::Enum; |
143 | if (isa<EnumConstantDecl>(Val: D)) |
144 | return HighlightingKind::EnumConstant; |
145 | if (isa<ParmVarDecl>(Val: D)) |
146 | return HighlightingKind::Parameter; |
147 | if (auto *VD = dyn_cast<VarDecl>(Val: D)) { |
148 | if (isa<ImplicitParamDecl>(Val: VD)) // e.g. ObjC Self |
149 | return std::nullopt; |
150 | return VD->isStaticDataMember() |
151 | ? HighlightingKind::StaticField |
152 | : VD->isLocalVarDecl() ? HighlightingKind::LocalVariable |
153 | : HighlightingKind::Variable; |
154 | } |
155 | if (const auto *BD = dyn_cast<BindingDecl>(Val: D)) |
156 | return BD->getDeclContext()->isFunctionOrMethod() |
157 | ? HighlightingKind::LocalVariable |
158 | : HighlightingKind::Variable; |
159 | if (isa<FunctionDecl>(Val: D)) |
160 | return HighlightingKind::Function; |
161 | if (isa<NamespaceDecl>(Val: D) || isa<NamespaceAliasDecl>(Val: D) || |
162 | isa<UsingDirectiveDecl>(Val: D)) |
163 | return HighlightingKind::Namespace; |
164 | if (isa<TemplateTemplateParmDecl>(Val: D) || isa<TemplateTypeParmDecl>(Val: D) || |
165 | isa<NonTypeTemplateParmDecl>(Val: D)) |
166 | return HighlightingKind::TemplateParameter; |
167 | if (isa<ConceptDecl>(Val: D)) |
168 | return HighlightingKind::Concept; |
169 | if (isa<LabelDecl>(Val: D)) |
170 | return HighlightingKind::Label; |
171 | if (const auto *UUVD = dyn_cast<UnresolvedUsingValueDecl>(Val: D)) { |
172 | auto Targets = Resolver->resolveUsingValueDecl(UUVD); |
173 | if (!Targets.empty() && Targets[0] != UUVD) { |
174 | return kindForDecl(D: Targets[0], Resolver); |
175 | } |
176 | return HighlightingKind::Unknown; |
177 | } |
178 | return std::nullopt; |
179 | } |
180 | std::optional<HighlightingKind> kindForType(const Type *TP, |
181 | const HeuristicResolver *Resolver) { |
182 | if (!TP) |
183 | return std::nullopt; |
184 | if (TP->isBuiltinType()) // Builtins are special, they do not have decls. |
185 | return HighlightingKind::Primitive; |
186 | if (auto *TD = dyn_cast<TemplateTypeParmType>(Val: TP)) |
187 | return kindForDecl(TD->getDecl(), Resolver); |
188 | if (isa<ObjCObjectPointerType>(Val: TP)) |
189 | return HighlightingKind::Class; |
190 | if (auto *TD = TP->getAsTagDecl()) |
191 | return kindForDecl(TD, Resolver); |
192 | return std::nullopt; |
193 | } |
194 | |
195 | // Whether T is const in a loose sense - is a variable with this type readonly? |
196 | bool isConst(QualType T) { |
197 | if (T.isNull()) |
198 | return false; |
199 | T = T.getNonReferenceType(); |
200 | if (T.isConstQualified()) |
201 | return true; |
202 | if (const auto *AT = T->getAsArrayTypeUnsafe()) |
203 | return isConst(AT->getElementType()); |
204 | if (isConst(T: T->getPointeeType())) |
205 | return true; |
206 | return false; |
207 | } |
208 | |
209 | // Whether D is const in a loose sense (should it be highlighted as such?) |
210 | // FIXME: This is separate from whether *a particular usage* can mutate D. |
211 | // We may want V in V.size() to be readonly even if V is mutable. |
212 | bool isConst(const Decl *D) { |
213 | if (llvm::isa<EnumConstantDecl>(Val: D) || llvm::isa<NonTypeTemplateParmDecl>(Val: D)) |
214 | return true; |
215 | if (llvm::isa<FieldDecl>(Val: D) || llvm::isa<VarDecl>(Val: D) || |
216 | llvm::isa<MSPropertyDecl>(Val: D) || llvm::isa<BindingDecl>(Val: D)) { |
217 | if (isConst(T: llvm::cast<ValueDecl>(Val: D)->getType())) |
218 | return true; |
219 | } |
220 | if (const auto *OCPD = llvm::dyn_cast<ObjCPropertyDecl>(Val: D)) { |
221 | if (OCPD->isReadOnly()) |
222 | return true; |
223 | } |
224 | if (const auto *MPD = llvm::dyn_cast<MSPropertyDecl>(Val: D)) { |
225 | if (!MPD->hasSetter()) |
226 | return true; |
227 | } |
228 | if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(Val: D)) { |
229 | if (CMD->isConst()) |
230 | return true; |
231 | } |
232 | return false; |
233 | } |
234 | |
235 | // "Static" means many things in C++, only some get the "static" modifier. |
236 | // |
237 | // Meanings that do: |
238 | // - Members associated with the class rather than the instance. |
239 | // This is what 'static' most often means across languages. |
240 | // - static local variables |
241 | // These are similarly "detached from their context" by the static keyword. |
242 | // In practice, these are rarely used inside classes, reducing confusion. |
243 | // |
244 | // Meanings that don't: |
245 | // - Namespace-scoped variables, which have static storage class. |
246 | // This is implicit, so the keyword "static" isn't so strongly associated. |
247 | // If we want a modifier for these, "global scope" is probably the concept. |
248 | // - Namespace-scoped variables/functions explicitly marked "static". |
249 | // There the keyword changes *linkage* , which is a totally different concept. |
250 | // If we want to model this, "file scope" would be a nice modifier. |
251 | // |
252 | // This is confusing, and maybe we should use another name, but because "static" |
253 | // is a standard LSP modifier, having one with that name has advantages. |
254 | bool isStatic(const Decl *D) { |
255 | if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(Val: D)) |
256 | return CMD->isStatic(); |
257 | if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(Val: D)) |
258 | return VD->isStaticDataMember() || VD->isStaticLocal(); |
259 | if (const auto *OPD = llvm::dyn_cast<ObjCPropertyDecl>(Val: D)) |
260 | return OPD->isClassProperty(); |
261 | if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(Val: D)) |
262 | return OMD->isClassMethod(); |
263 | return false; |
264 | } |
265 | |
266 | bool isAbstract(const Decl *D) { |
267 | if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(Val: D)) |
268 | return CMD->isPureVirtual(); |
269 | if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(Val: D)) |
270 | return CRD->hasDefinition() && CRD->isAbstract(); |
271 | return false; |
272 | } |
273 | |
274 | bool isVirtual(const Decl *D) { |
275 | if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(Val: D)) |
276 | return CMD->isVirtual(); |
277 | return false; |
278 | } |
279 | |
280 | bool isDependent(const Decl *D) { |
281 | if (isa<UnresolvedUsingValueDecl>(Val: D)) |
282 | return true; |
283 | return false; |
284 | } |
285 | |
286 | /// Returns true if `Decl` is considered to be from a default/system library. |
287 | /// This currently checks the systemness of the file by include type, although |
288 | /// different heuristics may be used in the future (e.g. sysroot paths). |
289 | bool isDefaultLibrary(const Decl *D) { |
290 | SourceLocation Loc = D->getLocation(); |
291 | if (!Loc.isValid()) |
292 | return false; |
293 | return D->getASTContext().getSourceManager().isInSystemHeader(Loc); |
294 | } |
295 | |
296 | bool isDefaultLibrary(const Type *T) { |
297 | if (!T) |
298 | return false; |
299 | const Type *Underlying = T->getPointeeOrArrayElementType(); |
300 | if (Underlying->isBuiltinType()) |
301 | return true; |
302 | if (auto *TD = dyn_cast<TemplateTypeParmType>(Val: Underlying)) |
303 | return isDefaultLibrary(TD->getDecl()); |
304 | if (auto *TD = Underlying->getAsTagDecl()) |
305 | return isDefaultLibrary(TD); |
306 | return false; |
307 | } |
308 | |
309 | // For a macro usage `DUMP(foo)`, we want: |
310 | // - DUMP --> "macro" |
311 | // - foo --> "variable". |
312 | SourceLocation getHighlightableSpellingToken(SourceLocation L, |
313 | const SourceManager &SM) { |
314 | if (L.isFileID()) |
315 | return SM.isWrittenInMainFile(Loc: L) ? L : SourceLocation{}; |
316 | // Tokens expanded from the macro body contribute no highlightings. |
317 | if (!SM.isMacroArgExpansion(Loc: L)) |
318 | return {}; |
319 | // Tokens expanded from macro args are potentially highlightable. |
320 | return getHighlightableSpellingToken(L: SM.getImmediateSpellingLoc(Loc: L), SM); |
321 | } |
322 | |
323 | unsigned evaluateHighlightPriority(const HighlightingToken &Tok) { |
324 | enum HighlightPriority { Dependent = 0, Resolved = 1 }; |
325 | return (Tok.Modifiers & (1 << uint32_t(HighlightingModifier::DependentName))) |
326 | ? Dependent |
327 | : Resolved; |
328 | } |
329 | |
330 | // Sometimes we get multiple tokens at the same location: |
331 | // |
332 | // - findExplicitReferences() returns a heuristic result for a dependent name |
333 | // (e.g. Method) and CollectExtraHighlighting returning a fallback dependent |
334 | // highlighting (e.g. Unknown+Dependent). |
335 | // - macro arguments are expanded multiple times and have different roles |
336 | // - broken code recovery produces several AST nodes at the same location |
337 | // |
338 | // We should either resolve these to a single token, or drop them all. |
339 | // Our heuristics are: |
340 | // |
341 | // - token kinds that come with "dependent-name" modifiers are less reliable |
342 | // (these tend to be vague, like Type or Unknown) |
343 | // - if we have multiple equally reliable kinds, drop token rather than guess |
344 | // - take the union of modifiers from all tokens |
345 | // |
346 | // In particular, heuristically resolved dependent names get their heuristic |
347 | // kind, plus the dependent modifier. |
348 | std::optional<HighlightingToken> resolveConflict(const HighlightingToken &A, |
349 | const HighlightingToken &B) { |
350 | unsigned Priority1 = evaluateHighlightPriority(Tok: A); |
351 | unsigned Priority2 = evaluateHighlightPriority(Tok: B); |
352 | if (Priority1 == Priority2 && A.Kind != B.Kind) |
353 | return std::nullopt; |
354 | auto Result = Priority1 > Priority2 ? A : B; |
355 | Result.Modifiers = A.Modifiers | B.Modifiers; |
356 | return Result; |
357 | } |
358 | std::optional<HighlightingToken> |
359 | resolveConflict(ArrayRef<HighlightingToken> Tokens) { |
360 | if (Tokens.size() == 1) |
361 | return Tokens[0]; |
362 | |
363 | assert(Tokens.size() >= 2); |
364 | std::optional<HighlightingToken> Winner = |
365 | resolveConflict(A: Tokens[0], B: Tokens[1]); |
366 | for (size_t I = 2; Winner && I < Tokens.size(); ++I) |
367 | Winner = resolveConflict(A: *Winner, B: Tokens[I]); |
368 | return Winner; |
369 | } |
370 | |
371 | /// Filter to remove particular kinds of highlighting tokens and modifiers from |
372 | /// the output. |
373 | class HighlightingFilter { |
374 | public: |
375 | HighlightingFilter() { |
376 | for (auto &Active : ActiveKindLookup) |
377 | Active = true; |
378 | |
379 | ActiveModifiersMask = ~0; |
380 | } |
381 | |
382 | void disableKind(HighlightingKind Kind) { |
383 | ActiveKindLookup[static_cast<size_t>(Kind)] = false; |
384 | } |
385 | |
386 | void disableModifier(HighlightingModifier Modifier) { |
387 | ActiveModifiersMask &= ~(1 << static_cast<uint32_t>(Modifier)); |
388 | } |
389 | |
390 | bool isHighlightKindActive(HighlightingKind Kind) const { |
391 | return ActiveKindLookup[static_cast<size_t>(Kind)]; |
392 | } |
393 | |
394 | uint32_t maskModifiers(uint32_t Modifiers) const { |
395 | return Modifiers & ActiveModifiersMask; |
396 | } |
397 | |
398 | static HighlightingFilter fromCurrentConfig() { |
399 | const Config &C = Config::current(); |
400 | HighlightingFilter Filter; |
401 | for (const auto &Kind : C.SemanticTokens.DisabledKinds) |
402 | if (auto K = highlightingKindFromString(Name: Kind)) |
403 | Filter.disableKind(Kind: *K); |
404 | for (const auto &Modifier : C.SemanticTokens.DisabledModifiers) |
405 | if (auto M = highlightingModifierFromString(Name: Modifier)) |
406 | Filter.disableModifier(Modifier: *M); |
407 | |
408 | return Filter; |
409 | } |
410 | |
411 | private: |
412 | bool ActiveKindLookup[static_cast<size_t>(HighlightingKind::LastKind) + 1]; |
413 | uint32_t ActiveModifiersMask; |
414 | }; |
415 | |
416 | /// Consumes source locations and maps them to text ranges for highlightings. |
417 | class HighlightingsBuilder { |
418 | public: |
419 | HighlightingsBuilder(const ParsedAST &AST, const HighlightingFilter &Filter) |
420 | : TB(AST.getTokens()), SourceMgr(AST.getSourceManager()), |
421 | LangOpts(AST.getLangOpts()), Filter(Filter), |
422 | Resolver(AST.getHeuristicResolver()) {} |
423 | |
424 | HighlightingToken &addToken(SourceLocation Loc, HighlightingKind Kind) { |
425 | auto Range = getRangeForSourceLocation(Loc); |
426 | if (!Range) |
427 | return InvalidHighlightingToken; |
428 | |
429 | return addToken(R: *Range, Kind); |
430 | } |
431 | |
432 | // Most of this function works around |
433 | // https://github.com/clangd/clangd/issues/871. |
434 | void addAngleBracketTokens(SourceLocation LLoc, SourceLocation RLoc) { |
435 | if (!LLoc.isValid() || !RLoc.isValid()) |
436 | return; |
437 | |
438 | auto LRange = getRangeForSourceLocation(Loc: LLoc); |
439 | if (!LRange) |
440 | return; |
441 | |
442 | // RLoc might be pointing at a virtual buffer when it's part of a `>>` |
443 | // token. |
444 | RLoc = SourceMgr.getFileLoc(Loc: RLoc); |
445 | // Make sure token is part of the main file. |
446 | RLoc = getHighlightableSpellingToken(L: RLoc, SM: SourceMgr); |
447 | if (!RLoc.isValid()) |
448 | return; |
449 | |
450 | const auto *RTok = TB.spelledTokenContaining(Loc: RLoc); |
451 | // Handle `>>`. RLoc is either part of `>>` or a spelled token on its own |
452 | // `>`. If it's the former, slice to have length of 1, if latter use the |
453 | // token as-is. |
454 | if (!RTok || RTok->kind() == tok::greatergreater) { |
455 | Position Begin = sourceLocToPosition(SM: SourceMgr, Loc: RLoc); |
456 | Position End = sourceLocToPosition(SM: SourceMgr, Loc: RLoc.getLocWithOffset(Offset: 1)); |
457 | addToken(R: *LRange, Kind: HighlightingKind::Bracket); |
458 | addToken(R: {.start: Begin, .end: End}, Kind: HighlightingKind::Bracket); |
459 | return; |
460 | } |
461 | |
462 | // Easy case, we have the `>` token directly available. |
463 | if (RTok->kind() == tok::greater) { |
464 | if (auto RRange = getRangeForSourceLocation(Loc: RLoc)) { |
465 | addToken(R: *LRange, Kind: HighlightingKind::Bracket); |
466 | addToken(R: *RRange, Kind: HighlightingKind::Bracket); |
467 | } |
468 | return; |
469 | } |
470 | } |
471 | |
472 | HighlightingToken &addToken(Range R, HighlightingKind Kind) { |
473 | if (!Filter.isHighlightKindActive(Kind)) |
474 | return InvalidHighlightingToken; |
475 | |
476 | HighlightingToken HT; |
477 | HT.R = std::move(R); |
478 | HT.Kind = Kind; |
479 | Tokens.push_back(x: std::move(HT)); |
480 | return Tokens.back(); |
481 | } |
482 | |
483 | void (SourceLocation Loc, HighlightingModifier Modifier) { |
484 | if (auto Range = getRangeForSourceLocation(Loc)) |
485 | ExtraModifiers[*Range].push_back(Elt: Modifier); |
486 | } |
487 | |
488 | std::vector<HighlightingToken> collect(ParsedAST &AST) && { |
489 | // Initializer lists can give duplicates of tokens, therefore all tokens |
490 | // must be deduplicated. |
491 | llvm::sort(C&: Tokens); |
492 | auto Last = std::unique(first: Tokens.begin(), last: Tokens.end()); |
493 | Tokens.erase(first: Last, last: Tokens.end()); |
494 | |
495 | // Macros can give tokens that have the same source range but conflicting |
496 | // kinds. In this case all tokens sharing this source range should be |
497 | // removed. |
498 | std::vector<HighlightingToken> NonConflicting; |
499 | NonConflicting.reserve(n: Tokens.size()); |
500 | for (ArrayRef<HighlightingToken> TokRef = Tokens; !TokRef.empty();) { |
501 | ArrayRef<HighlightingToken> Conflicting = |
502 | TokRef.take_while(Pred: [&](const HighlightingToken &T) { |
503 | // TokRef is guaranteed at least one element here because otherwise |
504 | // this predicate would never fire. |
505 | return T.R == TokRef.front().R; |
506 | }); |
507 | if (auto Resolved = resolveConflict(Tokens: Conflicting)) { |
508 | // Apply extra collected highlighting modifiers |
509 | auto Modifiers = ExtraModifiers.find(x: Resolved->R); |
510 | if (Modifiers != ExtraModifiers.end()) { |
511 | for (HighlightingModifier Mod : Modifiers->second) { |
512 | Resolved->addModifier(M: Mod); |
513 | } |
514 | } |
515 | |
516 | Resolved->Modifiers = Filter.maskModifiers(Modifiers: Resolved->Modifiers); |
517 | NonConflicting.push_back(x: *Resolved); |
518 | } |
519 | // TokRef[Conflicting.size()] is the next token with a different range (or |
520 | // the end of the Tokens). |
521 | TokRef = TokRef.drop_front(N: Conflicting.size()); |
522 | } |
523 | |
524 | if (!Filter.isHighlightKindActive(Kind: HighlightingKind::InactiveCode)) |
525 | return NonConflicting; |
526 | |
527 | const auto &SM = AST.getSourceManager(); |
528 | StringRef MainCode = SM.getBufferOrFake(FID: SM.getMainFileID()).getBuffer(); |
529 | |
530 | // Merge token stream with "inactive line" markers. |
531 | std::vector<HighlightingToken> WithInactiveLines; |
532 | auto SortedInactiveRegions = getInactiveRegions(AST); |
533 | llvm::sort(C&: SortedInactiveRegions); |
534 | auto It = NonConflicting.begin(); |
535 | for (const Range &R : SortedInactiveRegions) { |
536 | // Create one token for each line in the inactive range, so it works |
537 | // with line-based diffing. |
538 | assert(R.start.line <= R.end.line); |
539 | for (int Line = R.start.line; Line <= R.end.line; ++Line) { |
540 | // Copy tokens before the inactive line |
541 | for (; It != NonConflicting.end() && It->R.start.line < Line; ++It) |
542 | WithInactiveLines.push_back(x: std::move(*It)); |
543 | // Add a token for the inactive line itself. |
544 | auto EndOfLine = endOfLine(Code: MainCode, Line); |
545 | if (EndOfLine) { |
546 | HighlightingToken HT; |
547 | WithInactiveLines.emplace_back(); |
548 | WithInactiveLines.back().Kind = HighlightingKind::InactiveCode; |
549 | WithInactiveLines.back().R.start.line = Line; |
550 | WithInactiveLines.back().R.end = *EndOfLine; |
551 | } else { |
552 | elog(Fmt: "Failed to determine end of line: {0}" , Vals: EndOfLine.takeError()); |
553 | } |
554 | |
555 | // Skip any other tokens on the inactive line. e.g. |
556 | // `#ifndef Foo` is considered as part of an inactive region when Foo is |
557 | // defined, and there is a Foo macro token. |
558 | // FIXME: we should reduce the scope of the inactive region to not |
559 | // include the directive itself. |
560 | while (It != NonConflicting.end() && It->R.start.line == Line) |
561 | ++It; |
562 | } |
563 | } |
564 | // Copy tokens after the last inactive line |
565 | for (; It != NonConflicting.end(); ++It) |
566 | WithInactiveLines.push_back(x: std::move(*It)); |
567 | return WithInactiveLines; |
568 | } |
569 | |
570 | const HeuristicResolver *getResolver() const { return Resolver; } |
571 | |
572 | private: |
573 | std::optional<Range> getRangeForSourceLocation(SourceLocation Loc) { |
574 | Loc = getHighlightableSpellingToken(L: Loc, SM: SourceMgr); |
575 | if (Loc.isInvalid()) |
576 | return std::nullopt; |
577 | // We might have offsets in the main file that don't correspond to any |
578 | // spelled tokens. |
579 | const auto *Tok = TB.spelledTokenContaining(Loc); |
580 | if (!Tok) |
581 | return std::nullopt; |
582 | return halfOpenToRange(SM: SourceMgr, |
583 | R: Tok->range(SM: SourceMgr).toCharRange(SM: SourceMgr)); |
584 | } |
585 | |
586 | const syntax::TokenBuffer &TB; |
587 | const SourceManager &SourceMgr; |
588 | const LangOptions &LangOpts; |
589 | HighlightingFilter Filter; |
590 | std::vector<HighlightingToken> Tokens; |
591 | std::map<Range, llvm::SmallVector<HighlightingModifier, 1>> ; |
592 | const HeuristicResolver *Resolver; |
593 | // returned from addToken(InvalidLoc) |
594 | HighlightingToken InvalidHighlightingToken; |
595 | }; |
596 | |
597 | std::optional<HighlightingModifier> scopeModifier(const NamedDecl *D) { |
598 | const DeclContext *DC = D->getDeclContext(); |
599 | // Injected "Foo" within the class "Foo" has file scope, not class scope. |
600 | if (auto *R = dyn_cast_or_null<RecordDecl>(Val: D)) |
601 | if (R->isInjectedClassName()) |
602 | DC = DC->getParent(); |
603 | // Lambda captures are considered function scope, not class scope. |
604 | if (llvm::isa<FieldDecl>(Val: D)) |
605 | if (const auto *RD = llvm::dyn_cast<RecordDecl>(Val: DC)) |
606 | if (RD->isLambda()) |
607 | return HighlightingModifier::FunctionScope; |
608 | // Walk up the DeclContext hierarchy until we find something interesting. |
609 | for (; !DC->isFileContext(); DC = DC->getParent()) { |
610 | if (DC->isFunctionOrMethod()) |
611 | return HighlightingModifier::FunctionScope; |
612 | if (DC->isRecord()) |
613 | return HighlightingModifier::ClassScope; |
614 | } |
615 | // Some template parameters (e.g. those for variable templates) don't have |
616 | // meaningful DeclContexts. That doesn't mean they're global! |
617 | if (DC->isTranslationUnit() && D->isTemplateParameter()) |
618 | return std::nullopt; |
619 | // ExternalLinkage threshold could be tweaked, e.g. module-visible as global. |
620 | if (llvm::to_underlying(E: D->getLinkageInternal()) < |
621 | llvm::to_underlying(E: Linkage::External)) |
622 | return HighlightingModifier::FileScope; |
623 | return HighlightingModifier::GlobalScope; |
624 | } |
625 | |
626 | std::optional<HighlightingModifier> scopeModifier(const Type *T) { |
627 | if (!T) |
628 | return std::nullopt; |
629 | if (T->isBuiltinType()) |
630 | return HighlightingModifier::GlobalScope; |
631 | if (auto *TD = dyn_cast<TemplateTypeParmType>(Val: T)) |
632 | return scopeModifier(TD->getDecl()); |
633 | if (auto *TD = T->getAsTagDecl()) |
634 | return scopeModifier(TD); |
635 | return std::nullopt; |
636 | } |
637 | |
638 | /// Produces highlightings, which are not captured by findExplicitReferences, |
639 | /// e.g. highlights dependent names and 'auto' as the underlying type. |
640 | class |
641 | : public RecursiveASTVisitor<CollectExtraHighlightings> { |
642 | using = RecursiveASTVisitor<CollectExtraHighlightings>; |
643 | |
644 | public: |
645 | (HighlightingsBuilder &H) : H(H) {} |
646 | |
647 | bool (CXXConstructExpr *E) { |
648 | highlightMutableReferenceArguments(E->getConstructor(), |
649 | {E->getArgs(), E->getNumArgs()}); |
650 | |
651 | return true; |
652 | } |
653 | |
654 | bool (CXXCtorInitializer *Init) { |
655 | if (Init->isMemberInitializer()) |
656 | if (auto *Member = Init->getMember()) |
657 | highlightMutableReferenceArgument(T: Member->getType(), Arg: Init->getInit()); |
658 | return Base::TraverseConstructorInitializer(Init); |
659 | } |
660 | |
661 | bool (const TypeConstraint *C) { |
662 | if (auto *Args = C->getTemplateArgsAsWritten()) |
663 | H.addAngleBracketTokens(LLoc: Args->getLAngleLoc(), RLoc: Args->getRAngleLoc()); |
664 | return Base::TraverseTypeConstraint(C); |
665 | } |
666 | |
667 | bool (PredefinedExpr *E) { |
668 | H.addToken(Loc: E->getLocation(), Kind: HighlightingKind::LocalVariable) |
669 | .addModifier(M: HighlightingModifier::Static) |
670 | .addModifier(M: HighlightingModifier::Readonly) |
671 | .addModifier(M: HighlightingModifier::FunctionScope); |
672 | return true; |
673 | } |
674 | |
675 | bool (ConceptSpecializationExpr *E) { |
676 | if (auto *Args = E->getTemplateArgsAsWritten()) |
677 | H.addAngleBracketTokens(LLoc: Args->getLAngleLoc(), RLoc: Args->getRAngleLoc()); |
678 | return true; |
679 | } |
680 | |
681 | bool (TemplateDecl *D) { |
682 | if (auto *TPL = D->getTemplateParameters()) |
683 | H.addAngleBracketTokens(LLoc: TPL->getLAngleLoc(), RLoc: TPL->getRAngleLoc()); |
684 | return true; |
685 | } |
686 | |
687 | bool (TagDecl *D) { |
688 | for (unsigned i = 0; i < D->getNumTemplateParameterLists(); ++i) { |
689 | if (auto *TPL = D->getTemplateParameterList(i)) |
690 | H.addAngleBracketTokens(LLoc: TPL->getLAngleLoc(), RLoc: TPL->getRAngleLoc()); |
691 | } |
692 | return true; |
693 | } |
694 | |
695 | bool |
696 | (ClassTemplateSpecializationDecl *D) { |
697 | if (auto *Args = D->getTemplateArgsAsWritten()) |
698 | H.addAngleBracketTokens(LLoc: Args->getLAngleLoc(), RLoc: Args->getRAngleLoc()); |
699 | return true; |
700 | } |
701 | |
702 | bool ( |
703 | ClassTemplatePartialSpecializationDecl *D) { |
704 | if (auto *TPL = D->getTemplateParameters()) |
705 | H.addAngleBracketTokens(LLoc: TPL->getLAngleLoc(), RLoc: TPL->getRAngleLoc()); |
706 | return true; |
707 | } |
708 | |
709 | bool (VarTemplateSpecializationDecl *D) { |
710 | if (auto *Args = D->getTemplateArgsAsWritten()) |
711 | H.addAngleBracketTokens(LLoc: Args->getLAngleLoc(), RLoc: Args->getRAngleLoc()); |
712 | return true; |
713 | } |
714 | |
715 | bool ( |
716 | VarTemplatePartialSpecializationDecl *D) { |
717 | if (auto *TPL = D->getTemplateParameters()) |
718 | H.addAngleBracketTokens(LLoc: TPL->getLAngleLoc(), RLoc: TPL->getRAngleLoc()); |
719 | return true; |
720 | } |
721 | |
722 | bool (DeclRefExpr *E) { |
723 | H.addAngleBracketTokens(LLoc: E->getLAngleLoc(), RLoc: E->getRAngleLoc()); |
724 | return true; |
725 | } |
726 | bool (MemberExpr *E) { |
727 | H.addAngleBracketTokens(LLoc: E->getLAngleLoc(), RLoc: E->getRAngleLoc()); |
728 | return true; |
729 | } |
730 | |
731 | bool (TemplateSpecializationTypeLoc L) { |
732 | H.addAngleBracketTokens(LLoc: L.getLAngleLoc(), RLoc: L.getRAngleLoc()); |
733 | return true; |
734 | } |
735 | |
736 | bool (FunctionDecl *D) { |
737 | if (D->isOverloadedOperator()) { |
738 | const auto AddOpDeclToken = [&](SourceLocation Loc) { |
739 | auto &Token = H.addToken(Loc, Kind: HighlightingKind::Operator) |
740 | .addModifier(M: HighlightingModifier::Declaration); |
741 | if (D->isThisDeclarationADefinition()) |
742 | Token.addModifier(M: HighlightingModifier::Definition); |
743 | }; |
744 | const auto Range = D->getNameInfo().getCXXOperatorNameRange(); |
745 | AddOpDeclToken(Range.getBegin()); |
746 | const auto Kind = D->getOverloadedOperator(); |
747 | if (Kind == OO_Call || Kind == OO_Subscript) |
748 | AddOpDeclToken(Range.getEnd()); |
749 | } |
750 | if (auto *Args = D->getTemplateSpecializationArgsAsWritten()) |
751 | H.addAngleBracketTokens(LLoc: Args->getLAngleLoc(), RLoc: Args->getRAngleLoc()); |
752 | return true; |
753 | } |
754 | |
755 | bool (CXXOperatorCallExpr *E) { |
756 | const auto AddOpToken = [&](SourceLocation Loc) { |
757 | H.addToken(Loc, Kind: HighlightingKind::Operator) |
758 | .addModifier(M: HighlightingModifier::UserDefined); |
759 | }; |
760 | AddOpToken(E->getOperatorLoc()); |
761 | const auto Kind = E->getOperator(); |
762 | if (Kind == OO_Call || Kind == OO_Subscript) { |
763 | if (auto *Callee = E->getCallee()) |
764 | AddOpToken(Callee->getBeginLoc()); |
765 | } |
766 | return true; |
767 | } |
768 | |
769 | bool (UnaryOperator *Op) { |
770 | auto &Token = H.addToken(Loc: Op->getOperatorLoc(), Kind: HighlightingKind::Operator); |
771 | if (Op->getSubExpr()->isTypeDependent()) |
772 | Token.addModifier(M: HighlightingModifier::UserDefined); |
773 | return true; |
774 | } |
775 | |
776 | bool (BinaryOperator *Op) { |
777 | auto &Token = H.addToken(Loc: Op->getOperatorLoc(), Kind: HighlightingKind::Operator); |
778 | if (Op->getLHS()->isTypeDependent() || Op->getRHS()->isTypeDependent()) |
779 | Token.addModifier(M: HighlightingModifier::UserDefined); |
780 | return true; |
781 | } |
782 | |
783 | bool (ConditionalOperator *Op) { |
784 | H.addToken(Op->getQuestionLoc(), HighlightingKind::Operator); |
785 | H.addToken(Op->getColonLoc(), HighlightingKind::Operator); |
786 | return true; |
787 | } |
788 | |
789 | bool (CXXNewExpr *E) { |
790 | auto &Token = H.addToken(Loc: E->getBeginLoc(), Kind: HighlightingKind::Operator); |
791 | if (isa_and_present<CXXMethodDecl>(Val: E->getOperatorNew())) |
792 | Token.addModifier(M: HighlightingModifier::UserDefined); |
793 | return true; |
794 | } |
795 | |
796 | bool (CXXDeleteExpr *E) { |
797 | auto &Token = H.addToken(Loc: E->getBeginLoc(), Kind: HighlightingKind::Operator); |
798 | if (isa_and_present<CXXMethodDecl>(Val: E->getOperatorDelete())) |
799 | Token.addModifier(M: HighlightingModifier::UserDefined); |
800 | return true; |
801 | } |
802 | |
803 | bool (CXXNamedCastExpr *E) { |
804 | const auto &B = E->getAngleBrackets(); |
805 | H.addAngleBracketTokens(LLoc: B.getBegin(), RLoc: B.getEnd()); |
806 | return true; |
807 | } |
808 | |
809 | bool (CallExpr *E) { |
810 | // Highlighting parameters passed by non-const reference does not really |
811 | // make sense for literals... |
812 | if (isa<UserDefinedLiteral>(Val: E)) |
813 | return true; |
814 | |
815 | // FIXME: consider highlighting parameters of some other overloaded |
816 | // operators as well |
817 | llvm::ArrayRef<const Expr *> Args = {E->getArgs(), E->getNumArgs()}; |
818 | if (auto *CallOp = dyn_cast<CXXOperatorCallExpr>(Val: E)) { |
819 | switch (CallOp->getOperator()) { |
820 | case OO_Call: |
821 | case OO_Subscript: |
822 | Args = Args.drop_front(); // Drop object parameter |
823 | break; |
824 | default: |
825 | return true; |
826 | } |
827 | } |
828 | |
829 | highlightMutableReferenceArguments( |
830 | FD: dyn_cast_or_null<FunctionDecl>(Val: E->getCalleeDecl()), Args); |
831 | |
832 | return true; |
833 | } |
834 | |
835 | void (QualType T, const Expr *Arg) { |
836 | if (!Arg) |
837 | return; |
838 | |
839 | // Is this parameter passed by non-const pointer or reference? |
840 | // FIXME The condition T->idDependentType() could be relaxed a bit, |
841 | // e.g. std::vector<T>& is dependent but we would want to highlight it |
842 | bool IsRef = T->isLValueReferenceType(); |
843 | bool IsPtr = T->isPointerType(); |
844 | if ((!IsRef && !IsPtr) || T->getPointeeType().isConstQualified() || |
845 | T->isDependentType()) { |
846 | return; |
847 | } |
848 | |
849 | std::optional<SourceLocation> Location; |
850 | |
851 | // FIXME Add "unwrapping" for ArraySubscriptExpr, |
852 | // e.g. highlight `a` in `a[i]` |
853 | // FIXME Handle dependent expression types |
854 | if (auto *IC = dyn_cast<ImplicitCastExpr>(Val: Arg)) |
855 | Arg = IC->getSubExprAsWritten(); |
856 | if (auto *UO = dyn_cast<UnaryOperator>(Val: Arg)) { |
857 | if (UO->getOpcode() == UO_AddrOf) |
858 | Arg = UO->getSubExpr(); |
859 | } |
860 | if (auto *DR = dyn_cast<DeclRefExpr>(Val: Arg)) |
861 | Location = DR->getLocation(); |
862 | else if (auto *M = dyn_cast<MemberExpr>(Val: Arg)) |
863 | Location = M->getMemberLoc(); |
864 | |
865 | if (Location) |
866 | H.addExtraModifier(Loc: *Location, |
867 | Modifier: IsRef ? HighlightingModifier::UsedAsMutableReference |
868 | : HighlightingModifier::UsedAsMutablePointer); |
869 | } |
870 | |
871 | void |
872 | (const FunctionDecl *FD, |
873 | llvm::ArrayRef<const Expr *const> Args) { |
874 | if (!FD) |
875 | return; |
876 | |
877 | if (auto *ProtoType = FD->getType()->getAs<FunctionProtoType>()) { |
878 | // Iterate over the types of the function parameters. |
879 | // If any of them are non-const reference paramteres, add it as a |
880 | // highlighting modifier to the corresponding expression |
881 | for (size_t I = 0; |
882 | I < std::min(a: size_t(ProtoType->getNumParams()), b: Args.size()); ++I) { |
883 | highlightMutableReferenceArgument(T: ProtoType->getParamType(I), Arg: Args[I]); |
884 | } |
885 | } |
886 | } |
887 | |
888 | bool (DecltypeTypeLoc L) { |
889 | if (auto K = kindForType(L.getTypePtr(), H.getResolver())) { |
890 | auto &Tok = H.addToken(L.getBeginLoc(), *K) |
891 | .addModifier(HighlightingModifier::Deduced); |
892 | if (auto Mod = scopeModifier(L.getTypePtr())) |
893 | Tok.addModifier(*Mod); |
894 | if (isDefaultLibrary(L.getTypePtr())) |
895 | Tok.addModifier(HighlightingModifier::DefaultLibrary); |
896 | } |
897 | return true; |
898 | } |
899 | |
900 | bool (CXXDestructorDecl *D) { |
901 | if (auto *TI = D->getNameInfo().getNamedTypeInfo()) { |
902 | SourceLocation Loc = TI->getTypeLoc().getBeginLoc(); |
903 | H.addExtraModifier(Loc, Modifier: HighlightingModifier::ConstructorOrDestructor); |
904 | H.addExtraModifier(Loc, Modifier: HighlightingModifier::Declaration); |
905 | if (D->isThisDeclarationADefinition()) |
906 | H.addExtraModifier(Loc, Modifier: HighlightingModifier::Definition); |
907 | } |
908 | return true; |
909 | } |
910 | |
911 | bool (CXXMemberCallExpr *CE) { |
912 | // getMethodDecl can return nullptr with member pointers, e.g. |
913 | // `(foo.*pointer_to_member_fun)(arg);` |
914 | if (auto *D = CE->getMethodDecl()) { |
915 | if (isa<CXXDestructorDecl>(Val: D)) { |
916 | if (auto *ME = dyn_cast<MemberExpr>(CE->getCallee())) { |
917 | if (auto *TI = ME->getMemberNameInfo().getNamedTypeInfo()) { |
918 | H.addExtraModifier(Loc: TI->getTypeLoc().getBeginLoc(), |
919 | Modifier: HighlightingModifier::ConstructorOrDestructor); |
920 | } |
921 | } |
922 | } else if (D->isOverloadedOperator()) { |
923 | if (auto *ME = dyn_cast<MemberExpr>(CE->getCallee())) |
924 | H.addToken( |
925 | ME->getMemberNameInfo().getCXXOperatorNameRange().getBegin(), |
926 | HighlightingKind::Operator) |
927 | .addModifier(HighlightingModifier::UserDefined); |
928 | } |
929 | } |
930 | return true; |
931 | } |
932 | |
933 | bool (DeclaratorDecl *D) { |
934 | for (unsigned i = 0; i < D->getNumTemplateParameterLists(); ++i) { |
935 | if (auto *TPL = D->getTemplateParameterList(index: i)) |
936 | H.addAngleBracketTokens(LLoc: TPL->getLAngleLoc(), RLoc: TPL->getRAngleLoc()); |
937 | } |
938 | auto *AT = D->getType()->getContainedAutoType(); |
939 | if (!AT) |
940 | return true; |
941 | auto K = |
942 | kindForType(AT->getDeducedType().getTypePtrOrNull(), H.getResolver()); |
943 | if (!K) |
944 | return true; |
945 | auto *TSI = D->getTypeSourceInfo(); |
946 | if (!TSI) |
947 | return true; |
948 | SourceLocation StartLoc = |
949 | TSI->getTypeLoc().getContainedAutoTypeLoc().getNameLoc(); |
950 | // The AutoType may not have a corresponding token, e.g. in the case of |
951 | // init-captures. In this case, StartLoc overlaps with the location |
952 | // of the decl itself, and producing a token for the type here would result |
953 | // in both it and the token for the decl being dropped due to conflict. |
954 | if (StartLoc == D->getLocation()) |
955 | return true; |
956 | |
957 | auto &Tok = |
958 | H.addToken(StartLoc, *K).addModifier(HighlightingModifier::Deduced); |
959 | const Type *Deduced = AT->getDeducedType().getTypePtrOrNull(); |
960 | if (auto Mod = scopeModifier(T: Deduced)) |
961 | Tok.addModifier(*Mod); |
962 | if (isDefaultLibrary(T: Deduced)) |
963 | Tok.addModifier(HighlightingModifier::DefaultLibrary); |
964 | return true; |
965 | } |
966 | |
967 | // We handle objective-C selectors specially, because one reference can |
968 | // cover several non-contiguous tokens. |
969 | void (const ArrayRef<SourceLocation> &Locs, bool Decl, |
970 | bool Def, bool Class, bool DefaultLibrary) { |
971 | HighlightingKind Kind = |
972 | Class ? HighlightingKind::StaticMethod : HighlightingKind::Method; |
973 | for (SourceLocation Part : Locs) { |
974 | auto &Tok = |
975 | H.addToken(Loc: Part, Kind).addModifier(M: HighlightingModifier::ClassScope); |
976 | if (Decl) |
977 | Tok.addModifier(M: HighlightingModifier::Declaration); |
978 | if (Def) |
979 | Tok.addModifier(M: HighlightingModifier::Definition); |
980 | if (Class) |
981 | Tok.addModifier(M: HighlightingModifier::Static); |
982 | if (DefaultLibrary) |
983 | Tok.addModifier(M: HighlightingModifier::DefaultLibrary); |
984 | } |
985 | } |
986 | |
987 | bool (ObjCMethodDecl *OMD) { |
988 | llvm::SmallVector<SourceLocation> Locs; |
989 | OMD->getSelectorLocs(SelLocs&: Locs); |
990 | highlightObjCSelector(Locs, /*Decl=*/true, |
991 | Def: OMD->isThisDeclarationADefinition(), |
992 | Class: OMD->isClassMethod(), DefaultLibrary: isDefaultLibrary(OMD)); |
993 | return true; |
994 | } |
995 | |
996 | bool (ObjCMessageExpr *OME) { |
997 | llvm::SmallVector<SourceLocation> Locs; |
998 | OME->getSelectorLocs(SelLocs&: Locs); |
999 | bool DefaultLibrary = false; |
1000 | if (ObjCMethodDecl *OMD = OME->getMethodDecl()) |
1001 | DefaultLibrary = isDefaultLibrary(OMD); |
1002 | highlightObjCSelector(Locs, /*Decl=*/false, /*Def=*/false, |
1003 | Class: OME->isClassMessage(), DefaultLibrary); |
1004 | return true; |
1005 | } |
1006 | |
1007 | // Objective-C allows you to use property syntax `self.prop` as sugar for |
1008 | // `[self prop]` and `[self setProp:]` when there's no explicit `@property` |
1009 | // for `prop` as well as for class properties. We treat this like a property |
1010 | // even though semantically it's equivalent to a method expression. |
1011 | void (const ObjCMethodDecl *OMD, |
1012 | SourceLocation Loc) { |
1013 | auto &Tok = H.addToken(Loc, Kind: HighlightingKind::Field) |
1014 | .addModifier(M: HighlightingModifier::ClassScope); |
1015 | if (OMD->isClassMethod()) |
1016 | Tok.addModifier(M: HighlightingModifier::Static); |
1017 | if (isDefaultLibrary(OMD)) |
1018 | Tok.addModifier(M: HighlightingModifier::DefaultLibrary); |
1019 | } |
1020 | |
1021 | bool (ObjCPropertyRefExpr *OPRE) { |
1022 | // We need to handle implicit properties here since they will appear to |
1023 | // reference `ObjCMethodDecl` via an implicit `ObjCMessageExpr`, so normal |
1024 | // highlighting will not work. |
1025 | if (!OPRE->isImplicitProperty()) |
1026 | return true; |
1027 | // A single property expr can reference both a getter and setter, but we can |
1028 | // only provide a single semantic token, so prefer the getter. In most cases |
1029 | // the end result should be the same, although it's technically possible |
1030 | // that the user defines a setter for a system SDK. |
1031 | if (OPRE->isMessagingGetter()) { |
1032 | highlightObjCImplicitPropertyRef(OMD: OPRE->getImplicitPropertyGetter(), |
1033 | Loc: OPRE->getLocation()); |
1034 | return true; |
1035 | } |
1036 | if (OPRE->isMessagingSetter()) { |
1037 | highlightObjCImplicitPropertyRef(OMD: OPRE->getImplicitPropertySetter(), |
1038 | Loc: OPRE->getLocation()); |
1039 | } |
1040 | return true; |
1041 | } |
1042 | |
1043 | bool (OverloadExpr *E) { |
1044 | H.addAngleBracketTokens(LLoc: E->getLAngleLoc(), RLoc: E->getRAngleLoc()); |
1045 | if (!E->decls().empty()) |
1046 | return true; // handled by findExplicitReferences. |
1047 | auto &Tok = H.addToken(Loc: E->getNameLoc(), Kind: HighlightingKind::Unknown) |
1048 | .addModifier(M: HighlightingModifier::DependentName); |
1049 | if (llvm::isa<UnresolvedMemberExpr>(Val: E)) |
1050 | Tok.addModifier(M: HighlightingModifier::ClassScope); |
1051 | // other case is UnresolvedLookupExpr, scope is unknown. |
1052 | return true; |
1053 | } |
1054 | |
1055 | bool (CXXDependentScopeMemberExpr *E) { |
1056 | H.addToken(Loc: E->getMemberNameInfo().getLoc(), Kind: HighlightingKind::Unknown) |
1057 | .addModifier(M: HighlightingModifier::DependentName) |
1058 | .addModifier(M: HighlightingModifier::ClassScope); |
1059 | H.addAngleBracketTokens(LLoc: E->getLAngleLoc(), RLoc: E->getRAngleLoc()); |
1060 | return true; |
1061 | } |
1062 | |
1063 | bool (DependentScopeDeclRefExpr *E) { |
1064 | H.addToken(Loc: E->getNameInfo().getLoc(), Kind: HighlightingKind::Unknown) |
1065 | .addModifier(M: HighlightingModifier::DependentName) |
1066 | .addModifier(M: HighlightingModifier::ClassScope); |
1067 | H.addAngleBracketTokens(LLoc: E->getLAngleLoc(), RLoc: E->getRAngleLoc()); |
1068 | return true; |
1069 | } |
1070 | |
1071 | bool (Attr *A) { |
1072 | switch (A->getKind()) { |
1073 | case attr::Override: |
1074 | case attr::Final: |
1075 | H.addToken(Loc: A->getLocation(), Kind: HighlightingKind::Modifier); |
1076 | break; |
1077 | default: |
1078 | break; |
1079 | } |
1080 | return true; |
1081 | } |
1082 | |
1083 | bool (DependentNameTypeLoc L) { |
1084 | H.addToken(Loc: L.getNameLoc(), Kind: HighlightingKind::Type) |
1085 | .addModifier(M: HighlightingModifier::DependentName) |
1086 | .addModifier(M: HighlightingModifier::ClassScope); |
1087 | return true; |
1088 | } |
1089 | |
1090 | bool ( |
1091 | DependentTemplateSpecializationTypeLoc L) { |
1092 | H.addToken(Loc: L.getTemplateNameLoc(), Kind: HighlightingKind::Type) |
1093 | .addModifier(M: HighlightingModifier::DependentName) |
1094 | .addModifier(M: HighlightingModifier::ClassScope); |
1095 | H.addAngleBracketTokens(LLoc: L.getLAngleLoc(), RLoc: L.getRAngleLoc()); |
1096 | return true; |
1097 | } |
1098 | |
1099 | bool (TemplateArgumentLoc L) { |
1100 | // Handle template template arguments only (other arguments are handled by |
1101 | // their Expr, TypeLoc etc values). |
1102 | if (L.getArgument().getKind() != TemplateArgument::Template && |
1103 | L.getArgument().getKind() != TemplateArgument::TemplateExpansion) |
1104 | return RecursiveASTVisitor::TraverseTemplateArgumentLoc(ArgLoc: L); |
1105 | |
1106 | TemplateName N = L.getArgument().getAsTemplateOrTemplatePattern(); |
1107 | switch (N.getKind()) { |
1108 | case TemplateName::OverloadedTemplate: |
1109 | // Template template params must always be class templates. |
1110 | // Don't bother to try to work out the scope here. |
1111 | H.addToken(Loc: L.getTemplateNameLoc(), Kind: HighlightingKind::Class); |
1112 | break; |
1113 | case TemplateName::DependentTemplate: |
1114 | case TemplateName::AssumedTemplate: |
1115 | H.addToken(Loc: L.getTemplateNameLoc(), Kind: HighlightingKind::Class) |
1116 | .addModifier(M: HighlightingModifier::DependentName); |
1117 | break; |
1118 | case TemplateName::Template: |
1119 | case TemplateName::QualifiedTemplate: |
1120 | case TemplateName::SubstTemplateTemplateParm: |
1121 | case TemplateName::SubstTemplateTemplateParmPack: |
1122 | case TemplateName::UsingTemplate: |
1123 | case TemplateName::DeducedTemplate: |
1124 | // Names that could be resolved to a TemplateDecl are handled elsewhere. |
1125 | break; |
1126 | } |
1127 | return RecursiveASTVisitor::TraverseTemplateArgumentLoc(ArgLoc: L); |
1128 | } |
1129 | |
1130 | // findExplicitReferences will walk nested-name-specifiers and |
1131 | // find anything that can be resolved to a Decl. However, non-leaf |
1132 | // components of nested-name-specifiers which are dependent names |
1133 | // (kind "Identifier") cannot be resolved to a decl, so we visit |
1134 | // them here. |
1135 | bool (NestedNameSpecifierLoc Q) { |
1136 | if (NestedNameSpecifier *NNS = Q.getNestedNameSpecifier()) { |
1137 | if (NNS->getKind() == NestedNameSpecifier::Identifier) |
1138 | H.addToken(Loc: Q.getLocalBeginLoc(), Kind: HighlightingKind::Type) |
1139 | .addModifier(M: HighlightingModifier::DependentName) |
1140 | .addModifier(M: HighlightingModifier::ClassScope); |
1141 | } |
1142 | return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(NNS: Q); |
1143 | } |
1144 | |
1145 | private: |
1146 | HighlightingsBuilder &; |
1147 | }; |
1148 | } // namespace |
1149 | |
1150 | std::vector<HighlightingToken> |
1151 | getSemanticHighlightings(ParsedAST &AST, bool IncludeInactiveRegionTokens) { |
1152 | auto &C = AST.getASTContext(); |
1153 | HighlightingFilter Filter = HighlightingFilter::fromCurrentConfig(); |
1154 | if (!IncludeInactiveRegionTokens) |
1155 | Filter.disableKind(Kind: HighlightingKind::InactiveCode); |
1156 | // Add highlightings for AST nodes. |
1157 | HighlightingsBuilder Builder(AST, Filter); |
1158 | // Highlight 'decltype' and 'auto' as their underlying types. |
1159 | CollectExtraHighlightings(Builder).TraverseAST(AST&: C); |
1160 | // Highlight all decls and references coming from the AST. |
1161 | findExplicitReferences( |
1162 | AST: C, |
1163 | Out: [&](ReferenceLoc R) { |
1164 | for (const NamedDecl *Decl : R.Targets) { |
1165 | if (!canHighlightName(Name: Decl->getDeclName())) |
1166 | continue; |
1167 | auto Kind = kindForDecl(D: Decl, Resolver: AST.getHeuristicResolver()); |
1168 | if (!Kind) |
1169 | continue; |
1170 | auto &Tok = Builder.addToken(Loc: R.NameLoc, Kind: *Kind); |
1171 | |
1172 | // The attribute tests don't want to look at the template. |
1173 | if (auto *TD = dyn_cast<TemplateDecl>(Val: Decl)) { |
1174 | if (auto *Templated = TD->getTemplatedDecl()) |
1175 | Decl = Templated; |
1176 | } |
1177 | if (auto Mod = scopeModifier(D: Decl)) |
1178 | Tok.addModifier(M: *Mod); |
1179 | if (isConst(Decl)) |
1180 | Tok.addModifier(M: HighlightingModifier::Readonly); |
1181 | if (isStatic(Decl)) |
1182 | Tok.addModifier(M: HighlightingModifier::Static); |
1183 | if (isAbstract(Decl)) |
1184 | Tok.addModifier(M: HighlightingModifier::Abstract); |
1185 | if (isVirtual(Decl)) |
1186 | Tok.addModifier(M: HighlightingModifier::Virtual); |
1187 | if (isDependent(Decl)) |
1188 | Tok.addModifier(M: HighlightingModifier::DependentName); |
1189 | if (isDefaultLibrary(Decl)) |
1190 | Tok.addModifier(M: HighlightingModifier::DefaultLibrary); |
1191 | if (Decl->isDeprecated()) |
1192 | Tok.addModifier(M: HighlightingModifier::Deprecated); |
1193 | if (isa<CXXConstructorDecl>(Val: Decl)) |
1194 | Tok.addModifier(M: HighlightingModifier::ConstructorOrDestructor); |
1195 | if (R.IsDecl) { |
1196 | // Do not treat an UnresolvedUsingValueDecl as a declaration. |
1197 | // It's more common to think of it as a reference to the |
1198 | // underlying declaration. |
1199 | if (!isa<UnresolvedUsingValueDecl>(Val: Decl)) |
1200 | Tok.addModifier(M: HighlightingModifier::Declaration); |
1201 | if (isUniqueDefinition(Decl)) |
1202 | Tok.addModifier(M: HighlightingModifier::Definition); |
1203 | } |
1204 | } |
1205 | }, |
1206 | Resolver: AST.getHeuristicResolver()); |
1207 | // Add highlightings for macro references. |
1208 | auto AddMacro = [&](const MacroOccurrence &M) { |
1209 | auto &T = Builder.addToken(R: M.toRange(SM: C.getSourceManager()), |
1210 | Kind: HighlightingKind::Macro); |
1211 | T.addModifier(M: HighlightingModifier::GlobalScope); |
1212 | if (M.IsDefinition) |
1213 | T.addModifier(M: HighlightingModifier::Declaration); |
1214 | }; |
1215 | for (const auto &SIDToRefs : AST.getMacros().MacroRefs) |
1216 | for (const auto &M : SIDToRefs.second) |
1217 | AddMacro(M); |
1218 | for (const auto &M : AST.getMacros().UnknownMacros) |
1219 | AddMacro(M); |
1220 | |
1221 | return std::move(Builder).collect(AST); |
1222 | } |
1223 | |
1224 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingKind K) { |
1225 | switch (K) { |
1226 | case HighlightingKind::Variable: |
1227 | return OS << "Variable" ; |
1228 | case HighlightingKind::LocalVariable: |
1229 | return OS << "LocalVariable" ; |
1230 | case HighlightingKind::Parameter: |
1231 | return OS << "Parameter" ; |
1232 | case HighlightingKind::Function: |
1233 | return OS << "Function" ; |
1234 | case HighlightingKind::Method: |
1235 | return OS << "Method" ; |
1236 | case HighlightingKind::StaticMethod: |
1237 | return OS << "StaticMethod" ; |
1238 | case HighlightingKind::Field: |
1239 | return OS << "Field" ; |
1240 | case HighlightingKind::StaticField: |
1241 | return OS << "StaticField" ; |
1242 | case HighlightingKind::Class: |
1243 | return OS << "Class" ; |
1244 | case HighlightingKind::Interface: |
1245 | return OS << "Interface" ; |
1246 | case HighlightingKind::Enum: |
1247 | return OS << "Enum" ; |
1248 | case HighlightingKind::EnumConstant: |
1249 | return OS << "EnumConstant" ; |
1250 | case HighlightingKind::Typedef: |
1251 | return OS << "Typedef" ; |
1252 | case HighlightingKind::Type: |
1253 | return OS << "Type" ; |
1254 | case HighlightingKind::Unknown: |
1255 | return OS << "Unknown" ; |
1256 | case HighlightingKind::Namespace: |
1257 | return OS << "Namespace" ; |
1258 | case HighlightingKind::TemplateParameter: |
1259 | return OS << "TemplateParameter" ; |
1260 | case HighlightingKind::Concept: |
1261 | return OS << "Concept" ; |
1262 | case HighlightingKind::Primitive: |
1263 | return OS << "Primitive" ; |
1264 | case HighlightingKind::Macro: |
1265 | return OS << "Macro" ; |
1266 | case HighlightingKind::Modifier: |
1267 | return OS << "Modifier" ; |
1268 | case HighlightingKind::Operator: |
1269 | return OS << "Operator" ; |
1270 | case HighlightingKind::Bracket: |
1271 | return OS << "Bracket" ; |
1272 | case HighlightingKind::Label: |
1273 | return OS << "Label" ; |
1274 | case HighlightingKind::InactiveCode: |
1275 | return OS << "InactiveCode" ; |
1276 | } |
1277 | llvm_unreachable("invalid HighlightingKind" ); |
1278 | } |
1279 | std::optional<HighlightingKind> |
1280 | highlightingKindFromString(llvm::StringRef Name) { |
1281 | static llvm::StringMap<HighlightingKind> Lookup = { |
1282 | {"Variable" , HighlightingKind::Variable}, |
1283 | {"LocalVariable" , HighlightingKind::LocalVariable}, |
1284 | {"Parameter" , HighlightingKind::Parameter}, |
1285 | {"Function" , HighlightingKind::Function}, |
1286 | {"Method" , HighlightingKind::Method}, |
1287 | {"StaticMethod" , HighlightingKind::StaticMethod}, |
1288 | {"Field" , HighlightingKind::Field}, |
1289 | {"StaticField" , HighlightingKind::StaticField}, |
1290 | {"Class" , HighlightingKind::Class}, |
1291 | {"Interface" , HighlightingKind::Interface}, |
1292 | {"Enum" , HighlightingKind::Enum}, |
1293 | {"EnumConstant" , HighlightingKind::EnumConstant}, |
1294 | {"Typedef" , HighlightingKind::Typedef}, |
1295 | {"Type" , HighlightingKind::Type}, |
1296 | {"Unknown" , HighlightingKind::Unknown}, |
1297 | {"Namespace" , HighlightingKind::Namespace}, |
1298 | {"TemplateParameter" , HighlightingKind::TemplateParameter}, |
1299 | {"Concept" , HighlightingKind::Concept}, |
1300 | {"Primitive" , HighlightingKind::Primitive}, |
1301 | {"Macro" , HighlightingKind::Macro}, |
1302 | {"Modifier" , HighlightingKind::Modifier}, |
1303 | {"Operator" , HighlightingKind::Operator}, |
1304 | {"Bracket" , HighlightingKind::Bracket}, |
1305 | {"InactiveCode" , HighlightingKind::InactiveCode}, |
1306 | }; |
1307 | |
1308 | auto It = Lookup.find(Key: Name); |
1309 | return It != Lookup.end() ? std::make_optional(t&: It->getValue()) : std::nullopt; |
1310 | } |
1311 | llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingModifier K) { |
1312 | switch (K) { |
1313 | case HighlightingModifier::Declaration: |
1314 | return OS << "decl" ; // abbreviation for common case |
1315 | case HighlightingModifier::Definition: |
1316 | return OS << "def" ; // abbrevation for common case |
1317 | case HighlightingModifier::ConstructorOrDestructor: |
1318 | return OS << "constrDestr" ; |
1319 | default: |
1320 | return OS << toSemanticTokenModifier(Modifier: K); |
1321 | } |
1322 | } |
1323 | std::optional<HighlightingModifier> |
1324 | highlightingModifierFromString(llvm::StringRef Name) { |
1325 | static llvm::StringMap<HighlightingModifier> Lookup = { |
1326 | {"Declaration" , HighlightingModifier::Declaration}, |
1327 | {"Definition" , HighlightingModifier::Definition}, |
1328 | {"Deprecated" , HighlightingModifier::Deprecated}, |
1329 | {"Deduced" , HighlightingModifier::Deduced}, |
1330 | {"Readonly" , HighlightingModifier::Readonly}, |
1331 | {"Static" , HighlightingModifier::Static}, |
1332 | {"Abstract" , HighlightingModifier::Abstract}, |
1333 | {"Virtual" , HighlightingModifier::Virtual}, |
1334 | {"DependentName" , HighlightingModifier::DependentName}, |
1335 | {"DefaultLibrary" , HighlightingModifier::DefaultLibrary}, |
1336 | {"UsedAsMutableReference" , HighlightingModifier::UsedAsMutableReference}, |
1337 | {"UsedAsMutablePointer" , HighlightingModifier::UsedAsMutablePointer}, |
1338 | {"ConstructorOrDestructor" , |
1339 | HighlightingModifier::ConstructorOrDestructor}, |
1340 | {"UserDefined" , HighlightingModifier::UserDefined}, |
1341 | {"FunctionScope" , HighlightingModifier::FunctionScope}, |
1342 | {"ClassScope" , HighlightingModifier::ClassScope}, |
1343 | {"FileScope" , HighlightingModifier::FileScope}, |
1344 | {"GlobalScope" , HighlightingModifier::GlobalScope}, |
1345 | }; |
1346 | |
1347 | auto It = Lookup.find(Key: Name); |
1348 | return It != Lookup.end() ? std::make_optional(t&: It->getValue()) : std::nullopt; |
1349 | } |
1350 | |
1351 | bool operator==(const HighlightingToken &L, const HighlightingToken &R) { |
1352 | return std::tie(args: L.R, args: L.Kind, args: L.Modifiers) == |
1353 | std::tie(args: R.R, args: R.Kind, args: R.Modifiers); |
1354 | } |
1355 | bool operator<(const HighlightingToken &L, const HighlightingToken &R) { |
1356 | return std::tie(args: L.R, args: L.Kind, args: L.Modifiers) < |
1357 | std::tie(args: R.R, args: R.Kind, args: R.Modifiers); |
1358 | } |
1359 | |
1360 | std::vector<SemanticToken> |
1361 | toSemanticTokens(llvm::ArrayRef<HighlightingToken> Tokens, |
1362 | llvm::StringRef Code) { |
1363 | assert(llvm::is_sorted(Tokens)); |
1364 | std::vector<SemanticToken> Result; |
1365 | // In case we split a HighlightingToken into multiple tokens (e.g. because it |
1366 | // was spanning multiple lines), this tracks the last one. This prevents |
1367 | // having a copy all the time. |
1368 | HighlightingToken Scratch; |
1369 | const HighlightingToken *Last = nullptr; |
1370 | for (const HighlightingToken &Tok : Tokens) { |
1371 | Result.emplace_back(); |
1372 | SemanticToken *Out = &Result.back(); |
1373 | // deltaStart/deltaLine are relative if possible. |
1374 | if (Last) { |
1375 | assert(Tok.R.start.line >= Last->R.end.line); |
1376 | Out->deltaLine = Tok.R.start.line - Last->R.end.line; |
1377 | if (Out->deltaLine == 0) { |
1378 | assert(Tok.R.start.character >= Last->R.start.character); |
1379 | Out->deltaStart = Tok.R.start.character - Last->R.start.character; |
1380 | } else { |
1381 | Out->deltaStart = Tok.R.start.character; |
1382 | } |
1383 | } else { |
1384 | Out->deltaLine = Tok.R.start.line; |
1385 | Out->deltaStart = Tok.R.start.character; |
1386 | } |
1387 | Out->tokenType = static_cast<unsigned>(Tok.Kind); |
1388 | Out->tokenModifiers = Tok.Modifiers; |
1389 | Last = &Tok; |
1390 | |
1391 | if (Tok.R.end.line == Tok.R.start.line) { |
1392 | Out->length = Tok.R.end.character - Tok.R.start.character; |
1393 | } else { |
1394 | // If the token spans a line break, split it into multiple pieces for each |
1395 | // line. |
1396 | // This is slow, but multiline tokens are rare. |
1397 | // FIXME: There's a client capability for supporting multiline tokens, |
1398 | // respect that. |
1399 | auto TokStartOffset = llvm::cantFail(ValOrErr: positionToOffset(Code, P: Tok.R.start)); |
1400 | // Note that the loop doesn't cover the last line, which has a special |
1401 | // length. |
1402 | for (int I = Tok.R.start.line; I < Tok.R.end.line; ++I) { |
1403 | auto LineEnd = Code.find(C: '\n', From: TokStartOffset); |
1404 | assert(LineEnd != Code.npos); |
1405 | Out->length = LineEnd - TokStartOffset; |
1406 | // Token continues on next line, right after the line break. |
1407 | TokStartOffset = LineEnd + 1; |
1408 | Result.emplace_back(); |
1409 | Out = &Result.back(); |
1410 | *Out = Result[Result.size() - 2]; |
1411 | // New token starts at the first column of the next line. |
1412 | Out->deltaLine = 1; |
1413 | Out->deltaStart = 0; |
1414 | } |
1415 | // This is the token on last line. |
1416 | Out->length = Tok.R.end.character; |
1417 | // Update the start location for last token, as that's used in the |
1418 | // relative delta calculation for following tokens. |
1419 | Scratch = *Last; |
1420 | Scratch.R.start.line = Tok.R.end.line; |
1421 | Scratch.R.start.character = 0; |
1422 | Last = &Scratch; |
1423 | } |
1424 | } |
1425 | return Result; |
1426 | } |
1427 | llvm::StringRef toSemanticTokenType(HighlightingKind Kind) { |
1428 | switch (Kind) { |
1429 | case HighlightingKind::Variable: |
1430 | case HighlightingKind::LocalVariable: |
1431 | case HighlightingKind::StaticField: |
1432 | return "variable" ; |
1433 | case HighlightingKind::Parameter: |
1434 | return "parameter" ; |
1435 | case HighlightingKind::Function: |
1436 | return "function" ; |
1437 | case HighlightingKind::Method: |
1438 | return "method" ; |
1439 | case HighlightingKind::StaticMethod: |
1440 | // FIXME: better method with static modifier? |
1441 | return "function" ; |
1442 | case HighlightingKind::Field: |
1443 | return "property" ; |
1444 | case HighlightingKind::Class: |
1445 | return "class" ; |
1446 | case HighlightingKind::Interface: |
1447 | return "interface" ; |
1448 | case HighlightingKind::Enum: |
1449 | return "enum" ; |
1450 | case HighlightingKind::EnumConstant: |
1451 | return "enumMember" ; |
1452 | case HighlightingKind::Typedef: |
1453 | case HighlightingKind::Type: |
1454 | return "type" ; |
1455 | case HighlightingKind::Unknown: |
1456 | return "unknown" ; // nonstandard |
1457 | case HighlightingKind::Namespace: |
1458 | return "namespace" ; |
1459 | case HighlightingKind::TemplateParameter: |
1460 | return "typeParameter" ; |
1461 | case HighlightingKind::Concept: |
1462 | return "concept" ; // nonstandard |
1463 | case HighlightingKind::Primitive: |
1464 | return "type" ; |
1465 | case HighlightingKind::Macro: |
1466 | return "macro" ; |
1467 | case HighlightingKind::Modifier: |
1468 | return "modifier" ; |
1469 | case HighlightingKind::Operator: |
1470 | return "operator" ; |
1471 | case HighlightingKind::Bracket: |
1472 | return "bracket" ; |
1473 | case HighlightingKind::Label: |
1474 | return "label" ; |
1475 | case HighlightingKind::InactiveCode: |
1476 | return "comment" ; |
1477 | } |
1478 | llvm_unreachable("unhandled HighlightingKind" ); |
1479 | } |
1480 | |
1481 | llvm::StringRef toSemanticTokenModifier(HighlightingModifier Modifier) { |
1482 | switch (Modifier) { |
1483 | case HighlightingModifier::Declaration: |
1484 | return "declaration" ; |
1485 | case HighlightingModifier::Definition: |
1486 | return "definition" ; |
1487 | case HighlightingModifier::Deprecated: |
1488 | return "deprecated" ; |
1489 | case HighlightingModifier::Readonly: |
1490 | return "readonly" ; |
1491 | case HighlightingModifier::Static: |
1492 | return "static" ; |
1493 | case HighlightingModifier::Deduced: |
1494 | return "deduced" ; // nonstandard |
1495 | case HighlightingModifier::Abstract: |
1496 | return "abstract" ; |
1497 | case HighlightingModifier::Virtual: |
1498 | return "virtual" ; |
1499 | case HighlightingModifier::DependentName: |
1500 | return "dependentName" ; // nonstandard |
1501 | case HighlightingModifier::DefaultLibrary: |
1502 | return "defaultLibrary" ; |
1503 | case HighlightingModifier::UsedAsMutableReference: |
1504 | return "usedAsMutableReference" ; // nonstandard |
1505 | case HighlightingModifier::UsedAsMutablePointer: |
1506 | return "usedAsMutablePointer" ; // nonstandard |
1507 | case HighlightingModifier::ConstructorOrDestructor: |
1508 | return "constructorOrDestructor" ; // nonstandard |
1509 | case HighlightingModifier::UserDefined: |
1510 | return "userDefined" ; // nonstandard |
1511 | case HighlightingModifier::FunctionScope: |
1512 | return "functionScope" ; // nonstandard |
1513 | case HighlightingModifier::ClassScope: |
1514 | return "classScope" ; // nonstandard |
1515 | case HighlightingModifier::FileScope: |
1516 | return "fileScope" ; // nonstandard |
1517 | case HighlightingModifier::GlobalScope: |
1518 | return "globalScope" ; // nonstandard |
1519 | } |
1520 | llvm_unreachable("unhandled HighlightingModifier" ); |
1521 | } |
1522 | |
1523 | std::vector<SemanticTokensEdit> |
1524 | diffTokens(llvm::ArrayRef<SemanticToken> Old, |
1525 | llvm::ArrayRef<SemanticToken> New) { |
1526 | // For now, just replace everything from the first-last modification. |
1527 | // FIXME: use a real diff instead, this is bad with include-insertion. |
1528 | |
1529 | unsigned Offset = 0; |
1530 | while (!Old.empty() && !New.empty() && Old.front() == New.front()) { |
1531 | ++Offset; |
1532 | Old = Old.drop_front(); |
1533 | New = New.drop_front(); |
1534 | } |
1535 | while (!Old.empty() && !New.empty() && Old.back() == New.back()) { |
1536 | Old = Old.drop_back(); |
1537 | New = New.drop_back(); |
1538 | } |
1539 | |
1540 | if (Old.empty() && New.empty()) |
1541 | return {}; |
1542 | SemanticTokensEdit Edit; |
1543 | Edit.startToken = Offset; |
1544 | Edit.deleteTokens = Old.size(); |
1545 | Edit.tokens = New; |
1546 | return {std::move(Edit)}; |
1547 | } |
1548 | |
1549 | std::vector<Range> getInactiveRegions(ParsedAST &AST) { |
1550 | std::vector<Range> SkippedRanges(std::move(AST.getMacros().SkippedRanges)); |
1551 | const auto &SM = AST.getSourceManager(); |
1552 | StringRef MainCode = SM.getBufferOrFake(FID: SM.getMainFileID()).getBuffer(); |
1553 | std::vector<Range> InactiveRegions; |
1554 | for (const Range &Skipped : SkippedRanges) { |
1555 | Range Inactive = Skipped; |
1556 | // Sometimes, SkippedRanges contains a range ending at position 0 |
1557 | // of a line. Clients that apply whole-line styles will treat that |
1558 | // line as inactive which is not desirable, so adjust the ending |
1559 | // position to be the end of the previous line. |
1560 | if (Inactive.end.character == 0 && Inactive.end.line > 0) { |
1561 | --Inactive.end.line; |
1562 | } |
1563 | // Exclude the directive lines themselves from the range. |
1564 | if (Inactive.end.line >= Inactive.start.line + 2) { |
1565 | ++Inactive.start.line; |
1566 | --Inactive.end.line; |
1567 | } else { |
1568 | // range would be empty, e.g. #endif on next line after #ifdef |
1569 | continue; |
1570 | } |
1571 | // Since we've adjusted the ending line, we need to recompute the |
1572 | // column to reflect the end of that line. |
1573 | if (auto EndOfLine = endOfLine(Code: MainCode, Line: Inactive.end.line)) { |
1574 | Inactive.end = *EndOfLine; |
1575 | } else { |
1576 | elog(Fmt: "Failed to determine end of line: {0}" , Vals: EndOfLine.takeError()); |
1577 | continue; |
1578 | } |
1579 | InactiveRegions.push_back(x: Inactive); |
1580 | } |
1581 | return InactiveRegions; |
1582 | } |
1583 | |
1584 | } // namespace clangd |
1585 | } // namespace clang |
1586 | |