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