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