1 //===--- SemanticHighlighting.cpp - ------------------------- ---*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8
9 #include "SemanticHighlighting.h"
10 #include "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/None.h"
31 #include "llvm/ADT/Optional.h"
32 #include "llvm/ADT/STLExtras.h"
33 #include "llvm/ADT/StringRef.h"
34 #include "llvm/Support/Casting.h"
35 #include "llvm/Support/Error.h"
36 #include <algorithm>
37
38 namespace clang {
39 namespace clangd {
40 namespace {
41
42 /// Some names are not written in the source code and cannot be highlighted,
43 /// e.g. anonymous classes. This function detects those cases.
canHighlightName(DeclarationName Name)44 bool canHighlightName(DeclarationName Name) {
45 switch (Name.getNameKind()) {
46 case DeclarationName::Identifier: {
47 auto *II = Name.getAsIdentifierInfo();
48 return II && !II->getName().empty();
49 }
50 case DeclarationName::CXXConstructorName:
51 case DeclarationName::CXXDestructorName:
52 return true;
53 case DeclarationName::ObjCZeroArgSelector:
54 case DeclarationName::ObjCOneArgSelector:
55 case DeclarationName::ObjCMultiArgSelector:
56 // Multi-arg selectors need special handling, and we handle 0/1 arg
57 // selectors there too.
58 return false;
59 case DeclarationName::CXXConversionFunctionName:
60 case DeclarationName::CXXOperatorName:
61 case DeclarationName::CXXDeductionGuideName:
62 case DeclarationName::CXXLiteralOperatorName:
63 case DeclarationName::CXXUsingDirective:
64 return false;
65 }
66 llvm_unreachable("invalid name kind");
67 }
68
69 llvm::Optional<HighlightingKind> kindForType(const Type *TP,
70 const HeuristicResolver *Resolver);
71 llvm::Optional<HighlightingKind>
kindForDecl(const NamedDecl * D,const HeuristicResolver * Resolver)72 kindForDecl(const NamedDecl *D, const HeuristicResolver *Resolver) {
73 if (auto *USD = dyn_cast<UsingShadowDecl>(D)) {
74 if (auto *Target = USD->getTargetDecl())
75 D = Target;
76 }
77 if (auto *TD = dyn_cast<TemplateDecl>(D)) {
78 if (auto *Templated = TD->getTemplatedDecl())
79 D = Templated;
80 }
81 if (auto *TD = dyn_cast<TypedefNameDecl>(D)) {
82 // We try to highlight typedefs as their underlying type.
83 if (auto K =
84 kindForType(TD->getUnderlyingType().getTypePtrOrNull(), Resolver))
85 return K;
86 // And fallback to a generic kind if this fails.
87 return HighlightingKind::Typedef;
88 }
89 // We highlight class decls, constructor decls and destructor decls as
90 // `Class` type. The destructor decls are handled in `VisitTagTypeLoc` (we
91 // will visit a TypeLoc where the underlying Type is a CXXRecordDecl).
92 if (auto *RD = llvm::dyn_cast<RecordDecl>(D)) {
93 // We don't want to highlight lambdas like classes.
94 if (RD->isLambda())
95 return llvm::None;
96 return HighlightingKind::Class;
97 }
98 if (isa<ClassTemplateDecl, RecordDecl, CXXConstructorDecl, ObjCInterfaceDecl,
99 ObjCImplementationDecl>(D))
100 return HighlightingKind::Class;
101 if (isa<ObjCProtocolDecl>(D))
102 return HighlightingKind::Interface;
103 if (isa<ObjCCategoryDecl>(D))
104 return HighlightingKind::Namespace;
105 if (auto *MD = dyn_cast<CXXMethodDecl>(D))
106 return MD->isStatic() ? HighlightingKind::StaticMethod
107 : HighlightingKind::Method;
108 if (auto *OMD = dyn_cast<ObjCMethodDecl>(D))
109 return OMD->isClassMethod() ? HighlightingKind::StaticMethod
110 : HighlightingKind::Method;
111 if (isa<FieldDecl, ObjCPropertyDecl>(D))
112 return HighlightingKind::Field;
113 if (isa<EnumDecl>(D))
114 return HighlightingKind::Enum;
115 if (isa<EnumConstantDecl>(D))
116 return HighlightingKind::EnumConstant;
117 if (isa<ParmVarDecl>(D))
118 return HighlightingKind::Parameter;
119 if (auto *VD = dyn_cast<VarDecl>(D)) {
120 if (isa<ImplicitParamDecl>(VD)) // e.g. ObjC Self
121 return llvm::None;
122 return VD->isStaticDataMember()
123 ? HighlightingKind::StaticField
124 : VD->isLocalVarDecl() ? HighlightingKind::LocalVariable
125 : HighlightingKind::Variable;
126 }
127 if (const auto *BD = dyn_cast<BindingDecl>(D))
128 return BD->getDeclContext()->isFunctionOrMethod()
129 ? HighlightingKind::LocalVariable
130 : HighlightingKind::Variable;
131 if (isa<FunctionDecl>(D))
132 return HighlightingKind::Function;
133 if (isa<NamespaceDecl>(D) || isa<NamespaceAliasDecl>(D) ||
134 isa<UsingDirectiveDecl>(D))
135 return HighlightingKind::Namespace;
136 if (isa<TemplateTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(D) ||
137 isa<NonTypeTemplateParmDecl>(D))
138 return HighlightingKind::TemplateParameter;
139 if (isa<ConceptDecl>(D))
140 return HighlightingKind::Concept;
141 if (const auto *UUVD = dyn_cast<UnresolvedUsingValueDecl>(D)) {
142 auto Targets = Resolver->resolveUsingValueDecl(UUVD);
143 if (!Targets.empty()) {
144 return kindForDecl(Targets[0], Resolver);
145 }
146 return HighlightingKind::Unknown;
147 }
148 return llvm::None;
149 }
150 llvm::Optional<HighlightingKind>
kindForType(const Type * TP,const HeuristicResolver * Resolver)151 kindForType(const Type *TP, const HeuristicResolver *Resolver) {
152 if (!TP)
153 return llvm::None;
154 if (TP->isBuiltinType()) // Builtins are special, they do not have decls.
155 return HighlightingKind::Primitive;
156 if (auto *TD = dyn_cast<TemplateTypeParmType>(TP))
157 return kindForDecl(TD->getDecl(), Resolver);
158 if (isa<ObjCObjectPointerType>(TP))
159 return HighlightingKind::Class;
160 if (auto *TD = TP->getAsTagDecl())
161 return kindForDecl(TD, Resolver);
162 return llvm::None;
163 }
164
165 // Whether T is const in a loose sense - is a variable with this type readonly?
isConst(QualType T)166 bool isConst(QualType T) {
167 if (T.isNull() || T->isDependentType())
168 return false;
169 T = T.getNonReferenceType();
170 if (T.isConstQualified())
171 return true;
172 if (const auto *AT = T->getAsArrayTypeUnsafe())
173 return isConst(AT->getElementType());
174 if (isConst(T->getPointeeType()))
175 return true;
176 return false;
177 }
178
179 // Whether D is const in a loose sense (should it be highlighted as such?)
180 // FIXME: This is separate from whether *a particular usage* can mutate D.
181 // We may want V in V.size() to be readonly even if V is mutable.
isConst(const Decl * D)182 bool isConst(const Decl *D) {
183 if (llvm::isa<EnumConstantDecl>(D) || llvm::isa<NonTypeTemplateParmDecl>(D))
184 return true;
185 if (llvm::isa<FieldDecl>(D) || llvm::isa<VarDecl>(D) ||
186 llvm::isa<MSPropertyDecl>(D) || llvm::isa<BindingDecl>(D)) {
187 if (isConst(llvm::cast<ValueDecl>(D)->getType()))
188 return true;
189 }
190 if (const auto *OCPD = llvm::dyn_cast<ObjCPropertyDecl>(D)) {
191 if (OCPD->isReadOnly())
192 return true;
193 }
194 if (const auto *MPD = llvm::dyn_cast<MSPropertyDecl>(D)) {
195 if (!MPD->hasSetter())
196 return true;
197 }
198 if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) {
199 if (CMD->isConst())
200 return true;
201 }
202 return false;
203 }
204
205 // "Static" means many things in C++, only some get the "static" modifier.
206 //
207 // Meanings that do:
208 // - Members associated with the class rather than the instance.
209 // This is what 'static' most often means across languages.
210 // - static local variables
211 // These are similarly "detached from their context" by the static keyword.
212 // In practice, these are rarely used inside classes, reducing confusion.
213 //
214 // Meanings that don't:
215 // - Namespace-scoped variables, which have static storage class.
216 // This is implicit, so the keyword "static" isn't so strongly associated.
217 // If we want a modifier for these, "global scope" is probably the concept.
218 // - Namespace-scoped variables/functions explicitly marked "static".
219 // There the keyword changes *linkage* , which is a totally different concept.
220 // If we want to model this, "file scope" would be a nice modifier.
221 //
222 // This is confusing, and maybe we should use another name, but because "static"
223 // is a standard LSP modifier, having one with that name has advantages.
isStatic(const Decl * D)224 bool isStatic(const Decl *D) {
225 if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
226 return CMD->isStatic();
227 if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(D))
228 return VD->isStaticDataMember() || VD->isStaticLocal();
229 if (const auto *OPD = llvm::dyn_cast<ObjCPropertyDecl>(D))
230 return OPD->isClassProperty();
231 if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(D))
232 return OMD->isClassMethod();
233 return false;
234 }
235
isAbstract(const Decl * D)236 bool isAbstract(const Decl *D) {
237 if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
238 return CMD->isPure();
239 if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(D))
240 return CRD->hasDefinition() && CRD->isAbstract();
241 return false;
242 }
243
isVirtual(const Decl * D)244 bool isVirtual(const Decl *D) {
245 if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
246 return CMD->isVirtual();
247 return false;
248 }
249
isDependent(const Decl * D)250 bool isDependent(const Decl *D) {
251 if (isa<UnresolvedUsingValueDecl>(D))
252 return true;
253 return false;
254 }
255
256 /// Returns true if `Decl` is considered to be from a default/system library.
257 /// This currently checks the systemness of the file by include type, although
258 /// different heuristics may be used in the future (e.g. sysroot paths).
isDefaultLibrary(const Decl * D)259 bool isDefaultLibrary(const Decl *D) {
260 SourceLocation Loc = D->getLocation();
261 if (!Loc.isValid())
262 return false;
263 return D->getASTContext().getSourceManager().isInSystemHeader(Loc);
264 }
265
isDefaultLibrary(const Type * T)266 bool isDefaultLibrary(const Type *T) {
267 if (!T)
268 return false;
269 const Type *Underlying = T->getPointeeOrArrayElementType();
270 if (Underlying->isBuiltinType())
271 return true;
272 if (auto *TD = dyn_cast<TemplateTypeParmType>(Underlying))
273 return isDefaultLibrary(TD->getDecl());
274 if (auto *TD = Underlying->getAsTagDecl())
275 return isDefaultLibrary(TD);
276 return false;
277 }
278
279 // For a macro usage `DUMP(foo)`, we want:
280 // - DUMP --> "macro"
281 // - foo --> "variable".
getHighlightableSpellingToken(SourceLocation L,const SourceManager & SM)282 SourceLocation getHighlightableSpellingToken(SourceLocation L,
283 const SourceManager &SM) {
284 if (L.isFileID())
285 return SM.isWrittenInMainFile(L) ? L : SourceLocation{};
286 // Tokens expanded from the macro body contribute no highlightings.
287 if (!SM.isMacroArgExpansion(L))
288 return {};
289 // Tokens expanded from macro args are potentially highlightable.
290 return getHighlightableSpellingToken(SM.getImmediateSpellingLoc(L), SM);
291 }
292
evaluateHighlightPriority(const HighlightingToken & Tok)293 unsigned evaluateHighlightPriority(const HighlightingToken &Tok) {
294 enum HighlightPriority { Dependent = 0, Resolved = 1 };
295 return (Tok.Modifiers & (1 << uint32_t(HighlightingModifier::DependentName)))
296 ? Dependent
297 : Resolved;
298 }
299
300 // Sometimes we get multiple tokens at the same location:
301 //
302 // - findExplicitReferences() returns a heuristic result for a dependent name
303 // (e.g. Method) and CollectExtraHighlighting returning a fallback dependent
304 // highlighting (e.g. Unknown+Dependent).
305 // - macro arguments are expanded multiple times and have different roles
306 // - broken code recovery produces several AST nodes at the same location
307 //
308 // We should either resolve these to a single token, or drop them all.
309 // Our heuristics are:
310 //
311 // - token kinds that come with "dependent-name" modifiers are less reliable
312 // (these tend to be vague, like Type or Unknown)
313 // - if we have multiple equally reliable kinds, drop token rather than guess
314 // - take the union of modifiers from all tokens
315 //
316 // In particular, heuristically resolved dependent names get their heuristic
317 // kind, plus the dependent modifier.
resolveConflict(const HighlightingToken & A,const HighlightingToken & B)318 llvm::Optional<HighlightingToken> resolveConflict(const HighlightingToken &A,
319 const HighlightingToken &B) {
320 unsigned Priority1 = evaluateHighlightPriority(A);
321 unsigned Priority2 = evaluateHighlightPriority(B);
322 if (Priority1 == Priority2 && A.Kind != B.Kind)
323 return llvm::None;
324 auto Result = Priority1 > Priority2 ? A : B;
325 Result.Modifiers = A.Modifiers | B.Modifiers;
326 return Result;
327 }
328 llvm::Optional<HighlightingToken>
resolveConflict(ArrayRef<HighlightingToken> Tokens)329 resolveConflict(ArrayRef<HighlightingToken> Tokens) {
330 if (Tokens.size() == 1)
331 return Tokens[0];
332
333 assert(Tokens.size() >= 2);
334 Optional<HighlightingToken> Winner = resolveConflict(Tokens[0], Tokens[1]);
335 for (size_t I = 2; Winner && I < Tokens.size(); ++I)
336 Winner = resolveConflict(*Winner, Tokens[I]);
337 return Winner;
338 }
339
340 /// Consumes source locations and maps them to text ranges for highlightings.
341 class HighlightingsBuilder {
342 public:
HighlightingsBuilder(const ParsedAST & AST)343 HighlightingsBuilder(const ParsedAST &AST)
344 : TB(AST.getTokens()), SourceMgr(AST.getSourceManager()),
345 LangOpts(AST.getLangOpts()) {}
346
addToken(SourceLocation Loc,HighlightingKind Kind)347 HighlightingToken &addToken(SourceLocation Loc, HighlightingKind Kind) {
348 auto Range = getRangeForSourceLocation(Loc);
349 if (!Range)
350 return InvalidHighlightingToken;
351
352 return addToken(*Range, Kind);
353 }
354
addToken(Range R,HighlightingKind Kind)355 HighlightingToken &addToken(Range R, HighlightingKind Kind) {
356 HighlightingToken HT;
357 HT.R = std::move(R);
358 HT.Kind = Kind;
359 Tokens.push_back(std::move(HT));
360 return Tokens.back();
361 }
362
addExtraModifier(SourceLocation Loc,HighlightingModifier Modifier)363 void addExtraModifier(SourceLocation Loc, HighlightingModifier Modifier) {
364 if (auto Range = getRangeForSourceLocation(Loc))
365 ExtraModifiers[*Range].push_back(Modifier);
366 }
367
collect(ParsedAST & AST)368 std::vector<HighlightingToken> collect(ParsedAST &AST) && {
369 // Initializer lists can give duplicates of tokens, therefore all tokens
370 // must be deduplicated.
371 llvm::sort(Tokens);
372 auto Last = std::unique(Tokens.begin(), Tokens.end());
373 Tokens.erase(Last, Tokens.end());
374
375 // Macros can give tokens that have the same source range but conflicting
376 // kinds. In this case all tokens sharing this source range should be
377 // removed.
378 std::vector<HighlightingToken> NonConflicting;
379 NonConflicting.reserve(Tokens.size());
380 for (ArrayRef<HighlightingToken> TokRef = Tokens; !TokRef.empty();) {
381 ArrayRef<HighlightingToken> Conflicting =
382 TokRef.take_while([&](const HighlightingToken &T) {
383 // TokRef is guaranteed at least one element here because otherwise
384 // this predicate would never fire.
385 return T.R == TokRef.front().R;
386 });
387 if (auto Resolved = resolveConflict(Conflicting)) {
388 // Apply extra collected highlighting modifiers
389 auto Modifiers = ExtraModifiers.find(Resolved->R);
390 if (Modifiers != ExtraModifiers.end()) {
391 for (HighlightingModifier Mod : Modifiers->second) {
392 Resolved->addModifier(Mod);
393 }
394 }
395
396 NonConflicting.push_back(*Resolved);
397 }
398 // TokRef[Conflicting.size()] is the next token with a different range (or
399 // the end of the Tokens).
400 TokRef = TokRef.drop_front(Conflicting.size());
401 }
402
403 const auto &SM = AST.getSourceManager();
404 StringRef MainCode = SM.getBufferOrFake(SM.getMainFileID()).getBuffer();
405
406 // Merge token stream with "inactive line" markers.
407 std::vector<HighlightingToken> WithInactiveLines;
408 auto SortedSkippedRanges = AST.getMacros().SkippedRanges;
409 llvm::sort(SortedSkippedRanges);
410 auto It = NonConflicting.begin();
411 for (const Range &R : SortedSkippedRanges) {
412 // Create one token for each line in the skipped range, so it works
413 // with line-based diffing.
414 assert(R.start.line <= R.end.line);
415 for (int Line = R.start.line; Line <= R.end.line; ++Line) {
416 // If the end of the inactive range is at the beginning
417 // of a line, that line is not inactive.
418 if (Line == R.end.line && R.end.character == 0)
419 continue;
420 // Copy tokens before the inactive line
421 for (; It != NonConflicting.end() && It->R.start.line < Line; ++It)
422 WithInactiveLines.push_back(std::move(*It));
423 // Add a token for the inactive line itself.
424 auto StartOfLine = positionToOffset(MainCode, Position{Line, 0});
425 if (StartOfLine) {
426 StringRef LineText =
427 MainCode.drop_front(*StartOfLine).take_until([](char C) {
428 return C == '\n';
429 });
430 HighlightingToken HT;
431 WithInactiveLines.emplace_back();
432 WithInactiveLines.back().Kind = HighlightingKind::InactiveCode;
433 WithInactiveLines.back().R.start.line = Line;
434 WithInactiveLines.back().R.end.line = Line;
435 WithInactiveLines.back().R.end.character =
436 static_cast<int>(lspLength(LineText));
437 } else {
438 elog("Failed to convert position to offset: {0}",
439 StartOfLine.takeError());
440 }
441
442 // Skip any other tokens on the inactive line. e.g.
443 // `#ifndef Foo` is considered as part of an inactive region when Foo is
444 // defined, and there is a Foo macro token.
445 // FIXME: we should reduce the scope of the inactive region to not
446 // include the directive itself.
447 while (It != NonConflicting.end() && It->R.start.line == Line)
448 ++It;
449 }
450 }
451 // Copy tokens after the last inactive line
452 for (; It != NonConflicting.end(); ++It)
453 WithInactiveLines.push_back(std::move(*It));
454 return WithInactiveLines;
455 }
456
getResolver() const457 const HeuristicResolver *getResolver() const { return Resolver; }
458
459 private:
getRangeForSourceLocation(SourceLocation Loc)460 llvm::Optional<Range> getRangeForSourceLocation(SourceLocation Loc) {
461 Loc = getHighlightableSpellingToken(Loc, SourceMgr);
462 if (Loc.isInvalid())
463 return llvm::None;
464
465 const auto *Tok = TB.spelledTokenAt(Loc);
466 assert(Tok);
467
468 return halfOpenToRange(SourceMgr,
469 Tok->range(SourceMgr).toCharRange(SourceMgr));
470 }
471
472 const syntax::TokenBuffer &TB;
473 const SourceManager &SourceMgr;
474 const LangOptions &LangOpts;
475 std::vector<HighlightingToken> Tokens;
476 std::map<Range, llvm::SmallVector<HighlightingModifier, 1>> ExtraModifiers;
477 const HeuristicResolver *Resolver = nullptr;
478 // returned from addToken(InvalidLoc)
479 HighlightingToken InvalidHighlightingToken;
480 };
481
scopeModifier(const NamedDecl * D)482 llvm::Optional<HighlightingModifier> scopeModifier(const NamedDecl *D) {
483 const DeclContext *DC = D->getDeclContext();
484 // Injected "Foo" within the class "Foo" has file scope, not class scope.
485 if (auto *R = dyn_cast_or_null<RecordDecl>(D))
486 if (R->isInjectedClassName())
487 DC = DC->getParent();
488 // Lambda captures are considered function scope, not class scope.
489 if (llvm::isa<FieldDecl>(D))
490 if (const auto *RD = llvm::dyn_cast<RecordDecl>(DC))
491 if (RD->isLambda())
492 return HighlightingModifier::FunctionScope;
493 // Walk up the DeclContext hierarchy until we find something interesting.
494 for (; !DC->isFileContext(); DC = DC->getParent()) {
495 if (DC->isFunctionOrMethod())
496 return HighlightingModifier::FunctionScope;
497 if (DC->isRecord())
498 return HighlightingModifier::ClassScope;
499 }
500 // Some template parameters (e.g. those for variable templates) don't have
501 // meaningful DeclContexts. That doesn't mean they're global!
502 if (DC->isTranslationUnit() && D->isTemplateParameter())
503 return llvm::None;
504 // ExternalLinkage threshold could be tweaked, e.g. module-visible as global.
505 if (D->getLinkageInternal() < ExternalLinkage)
506 return HighlightingModifier::FileScope;
507 return HighlightingModifier::GlobalScope;
508 }
509
scopeModifier(const Type * T)510 llvm::Optional<HighlightingModifier> scopeModifier(const Type *T) {
511 if (!T)
512 return llvm::None;
513 if (T->isBuiltinType())
514 return HighlightingModifier::GlobalScope;
515 if (auto *TD = dyn_cast<TemplateTypeParmType>(T))
516 return scopeModifier(TD->getDecl());
517 if (auto *TD = T->getAsTagDecl())
518 return scopeModifier(TD);
519 return llvm::None;
520 }
521
522 /// Produces highlightings, which are not captured by findExplicitReferences,
523 /// e.g. highlights dependent names and 'auto' as the underlying type.
524 class CollectExtraHighlightings
525 : public RecursiveASTVisitor<CollectExtraHighlightings> {
526 using Base = RecursiveASTVisitor<CollectExtraHighlightings>;
527
528 public:
CollectExtraHighlightings(HighlightingsBuilder & H)529 CollectExtraHighlightings(HighlightingsBuilder &H) : H(H) {}
530
VisitCXXConstructExpr(CXXConstructExpr * E)531 bool VisitCXXConstructExpr(CXXConstructExpr *E) {
532 highlightMutableReferenceArguments(E->getConstructor(),
533 {E->getArgs(), E->getNumArgs()});
534
535 return true;
536 }
537
TraverseConstructorInitializer(CXXCtorInitializer * Init)538 bool TraverseConstructorInitializer(CXXCtorInitializer *Init) {
539 if (Init->isMemberInitializer())
540 if (auto *Member = Init->getMember())
541 highlightMutableReferenceArgument(Member->getType(), Init->getInit());
542 return Base::TraverseConstructorInitializer(Init);
543 }
544
VisitCallExpr(CallExpr * E)545 bool VisitCallExpr(CallExpr *E) {
546 // Highlighting parameters passed by non-const reference does not really
547 // make sense for literals...
548 if (isa<UserDefinedLiteral>(E))
549 return true;
550
551 // FIXME: consider highlighting parameters of some other overloaded
552 // operators as well
553 llvm::ArrayRef<const Expr *> Args = {E->getArgs(), E->getNumArgs()};
554 if (auto *CallOp = dyn_cast<CXXOperatorCallExpr>(E)) {
555 switch (CallOp->getOperator()) {
556 case OO_Call:
557 case OO_Subscript:
558 Args = Args.drop_front(); // Drop object parameter
559 break;
560 default:
561 return true;
562 }
563 }
564
565 highlightMutableReferenceArguments(
566 dyn_cast_or_null<FunctionDecl>(E->getCalleeDecl()), Args);
567
568 return true;
569 }
570
highlightMutableReferenceArgument(QualType T,const Expr * Arg)571 void highlightMutableReferenceArgument(QualType T, const Expr *Arg) {
572 if (!Arg)
573 return;
574
575 // Is this parameter passed by non-const reference?
576 // FIXME The condition T->idDependentType() could be relaxed a bit,
577 // e.g. std::vector<T>& is dependent but we would want to highlight it
578 if (!T->isLValueReferenceType() ||
579 T.getNonReferenceType().isConstQualified() || T->isDependentType()) {
580 return;
581 }
582
583 llvm::Optional<SourceLocation> Location;
584
585 // FIXME Add "unwrapping" for ArraySubscriptExpr and UnaryOperator,
586 // e.g. highlight `a` in `a[i]`
587 // FIXME Handle dependent expression types
588 if (auto *DR = dyn_cast<DeclRefExpr>(Arg))
589 Location = DR->getLocation();
590 else if (auto *M = dyn_cast<MemberExpr>(Arg))
591 Location = M->getMemberLoc();
592
593 if (Location)
594 H.addExtraModifier(*Location,
595 HighlightingModifier::UsedAsMutableReference);
596 }
597
598 void
highlightMutableReferenceArguments(const FunctionDecl * FD,llvm::ArrayRef<const Expr * const> Args)599 highlightMutableReferenceArguments(const FunctionDecl *FD,
600 llvm::ArrayRef<const Expr *const> Args) {
601 if (!FD)
602 return;
603
604 if (auto *ProtoType = FD->getType()->getAs<FunctionProtoType>()) {
605 // Iterate over the types of the function parameters.
606 // If any of them are non-const reference paramteres, add it as a
607 // highlighting modifier to the corresponding expression
608 for (size_t I = 0;
609 I < std::min(size_t(ProtoType->getNumParams()), Args.size()); ++I) {
610 highlightMutableReferenceArgument(ProtoType->getParamType(I), Args[I]);
611 }
612 }
613 }
614
VisitDecltypeTypeLoc(DecltypeTypeLoc L)615 bool VisitDecltypeTypeLoc(DecltypeTypeLoc L) {
616 if (auto K = kindForType(L.getTypePtr(), H.getResolver())) {
617 auto &Tok = H.addToken(L.getBeginLoc(), *K)
618 .addModifier(HighlightingModifier::Deduced);
619 if (auto Mod = scopeModifier(L.getTypePtr()))
620 Tok.addModifier(*Mod);
621 if (isDefaultLibrary(L.getTypePtr()))
622 Tok.addModifier(HighlightingModifier::DefaultLibrary);
623 }
624 return true;
625 }
626
VisitDeclaratorDecl(DeclaratorDecl * D)627 bool VisitDeclaratorDecl(DeclaratorDecl *D) {
628 auto *AT = D->getType()->getContainedAutoType();
629 if (!AT)
630 return true;
631 auto K =
632 kindForType(AT->getDeducedType().getTypePtrOrNull(), H.getResolver());
633 if (!K)
634 return true;
635 SourceLocation StartLoc = D->getTypeSpecStartLoc();
636 // The AutoType may not have a corresponding token, e.g. in the case of
637 // init-captures. In this case, StartLoc overlaps with the location
638 // of the decl itself, and producing a token for the type here would result
639 // in both it and the token for the decl being dropped due to conflict.
640 if (StartLoc == D->getLocation())
641 return true;
642 auto &Tok =
643 H.addToken(StartLoc, *K).addModifier(HighlightingModifier::Deduced);
644 const Type *Deduced = AT->getDeducedType().getTypePtrOrNull();
645 if (auto Mod = scopeModifier(Deduced))
646 Tok.addModifier(*Mod);
647 if (isDefaultLibrary(Deduced))
648 Tok.addModifier(HighlightingModifier::DefaultLibrary);
649 return true;
650 }
651
652 // We handle objective-C selectors specially, because one reference can
653 // cover several non-contiguous tokens.
highlightObjCSelector(const ArrayRef<SourceLocation> & Locs,bool Decl,bool Class,bool DefaultLibrary)654 void highlightObjCSelector(const ArrayRef<SourceLocation> &Locs, bool Decl,
655 bool Class, bool DefaultLibrary) {
656 HighlightingKind Kind =
657 Class ? HighlightingKind::StaticMethod : HighlightingKind::Method;
658 for (SourceLocation Part : Locs) {
659 auto &Tok =
660 H.addToken(Part, Kind).addModifier(HighlightingModifier::ClassScope);
661 if (Decl)
662 Tok.addModifier(HighlightingModifier::Declaration);
663 if (Class)
664 Tok.addModifier(HighlightingModifier::Static);
665 if (DefaultLibrary)
666 Tok.addModifier(HighlightingModifier::DefaultLibrary);
667 }
668 }
669
VisitObjCMethodDecl(ObjCMethodDecl * OMD)670 bool VisitObjCMethodDecl(ObjCMethodDecl *OMD) {
671 llvm::SmallVector<SourceLocation> Locs;
672 OMD->getSelectorLocs(Locs);
673 highlightObjCSelector(Locs, /*Decl=*/true, OMD->isClassMethod(),
674 isDefaultLibrary(OMD));
675 return true;
676 }
677
VisitObjCMessageExpr(ObjCMessageExpr * OME)678 bool VisitObjCMessageExpr(ObjCMessageExpr *OME) {
679 llvm::SmallVector<SourceLocation> Locs;
680 OME->getSelectorLocs(Locs);
681 bool DefaultLibrary = false;
682 if (ObjCMethodDecl *OMD = OME->getMethodDecl())
683 DefaultLibrary = isDefaultLibrary(OMD);
684 highlightObjCSelector(Locs, /*Decl=*/false, OME->isClassMessage(),
685 DefaultLibrary);
686 return true;
687 }
688
689 // Objective-C allows you to use property syntax `self.prop` as sugar for
690 // `[self prop]` and `[self setProp:]` when there's no explicit `@property`
691 // for `prop` as well as for class properties. We treat this like a property
692 // even though semantically it's equivalent to a method expression.
highlightObjCImplicitPropertyRef(const ObjCMethodDecl * OMD,SourceLocation Loc)693 void highlightObjCImplicitPropertyRef(const ObjCMethodDecl *OMD,
694 SourceLocation Loc) {
695 auto &Tok = H.addToken(Loc, HighlightingKind::Field)
696 .addModifier(HighlightingModifier::ClassScope);
697 if (OMD->isClassMethod())
698 Tok.addModifier(HighlightingModifier::Static);
699 if (isDefaultLibrary(OMD))
700 Tok.addModifier(HighlightingModifier::DefaultLibrary);
701 }
702
VisitObjCPropertyRefExpr(ObjCPropertyRefExpr * OPRE)703 bool VisitObjCPropertyRefExpr(ObjCPropertyRefExpr *OPRE) {
704 // We need to handle implicit properties here since they will appear to
705 // reference `ObjCMethodDecl` via an implicit `ObjCMessageExpr`, so normal
706 // highlighting will not work.
707 if (!OPRE->isImplicitProperty())
708 return true;
709 // A single property expr can reference both a getter and setter, but we can
710 // only provide a single semantic token, so prefer the getter. In most cases
711 // the end result should be the same, although it's technically possible
712 // that the user defines a setter for a system SDK.
713 if (OPRE->isMessagingGetter()) {
714 highlightObjCImplicitPropertyRef(OPRE->getImplicitPropertyGetter(),
715 OPRE->getLocation());
716 return true;
717 }
718 if (OPRE->isMessagingSetter()) {
719 highlightObjCImplicitPropertyRef(OPRE->getImplicitPropertySetter(),
720 OPRE->getLocation());
721 }
722 return true;
723 }
724
VisitOverloadExpr(OverloadExpr * E)725 bool VisitOverloadExpr(OverloadExpr *E) {
726 if (!E->decls().empty())
727 return true; // handled by findExplicitReferences.
728 auto &Tok = H.addToken(E->getNameLoc(), HighlightingKind::Unknown)
729 .addModifier(HighlightingModifier::DependentName);
730 if (llvm::isa<UnresolvedMemberExpr>(E))
731 Tok.addModifier(HighlightingModifier::ClassScope);
732 // other case is UnresolvedLookupExpr, scope is unknown.
733 return true;
734 }
735
VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr * E)736 bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) {
737 H.addToken(E->getMemberNameInfo().getLoc(), HighlightingKind::Unknown)
738 .addModifier(HighlightingModifier::DependentName)
739 .addModifier(HighlightingModifier::ClassScope);
740 return true;
741 }
742
VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr * E)743 bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) {
744 H.addToken(E->getNameInfo().getLoc(), HighlightingKind::Unknown)
745 .addModifier(HighlightingModifier::DependentName)
746 .addModifier(HighlightingModifier::ClassScope);
747 return true;
748 }
749
VisitDependentNameTypeLoc(DependentNameTypeLoc L)750 bool VisitDependentNameTypeLoc(DependentNameTypeLoc L) {
751 H.addToken(L.getNameLoc(), HighlightingKind::Type)
752 .addModifier(HighlightingModifier::DependentName)
753 .addModifier(HighlightingModifier::ClassScope);
754 return true;
755 }
756
VisitDependentTemplateSpecializationTypeLoc(DependentTemplateSpecializationTypeLoc L)757 bool VisitDependentTemplateSpecializationTypeLoc(
758 DependentTemplateSpecializationTypeLoc L) {
759 H.addToken(L.getTemplateNameLoc(), HighlightingKind::Type)
760 .addModifier(HighlightingModifier::DependentName)
761 .addModifier(HighlightingModifier::ClassScope);
762 return true;
763 }
764
TraverseTemplateArgumentLoc(TemplateArgumentLoc L)765 bool TraverseTemplateArgumentLoc(TemplateArgumentLoc L) {
766 // Handle template template arguments only (other arguments are handled by
767 // their Expr, TypeLoc etc values).
768 if (L.getArgument().getKind() != TemplateArgument::Template &&
769 L.getArgument().getKind() != TemplateArgument::TemplateExpansion)
770 return RecursiveASTVisitor::TraverseTemplateArgumentLoc(L);
771
772 TemplateName N = L.getArgument().getAsTemplateOrTemplatePattern();
773 switch (N.getKind()) {
774 case TemplateName::OverloadedTemplate:
775 // Template template params must always be class templates.
776 // Don't bother to try to work out the scope here.
777 H.addToken(L.getTemplateNameLoc(), HighlightingKind::Class);
778 break;
779 case TemplateName::DependentTemplate:
780 case TemplateName::AssumedTemplate:
781 H.addToken(L.getTemplateNameLoc(), HighlightingKind::Class)
782 .addModifier(HighlightingModifier::DependentName);
783 break;
784 case TemplateName::Template:
785 case TemplateName::QualifiedTemplate:
786 case TemplateName::SubstTemplateTemplateParm:
787 case TemplateName::SubstTemplateTemplateParmPack:
788 case TemplateName::UsingTemplate:
789 // Names that could be resolved to a TemplateDecl are handled elsewhere.
790 break;
791 }
792 return RecursiveASTVisitor::TraverseTemplateArgumentLoc(L);
793 }
794
795 // findExplicitReferences will walk nested-name-specifiers and
796 // find anything that can be resolved to a Decl. However, non-leaf
797 // components of nested-name-specifiers which are dependent names
798 // (kind "Identifier") cannot be resolved to a decl, so we visit
799 // them here.
TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc Q)800 bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc Q) {
801 if (NestedNameSpecifier *NNS = Q.getNestedNameSpecifier()) {
802 if (NNS->getKind() == NestedNameSpecifier::Identifier)
803 H.addToken(Q.getLocalBeginLoc(), HighlightingKind::Type)
804 .addModifier(HighlightingModifier::DependentName)
805 .addModifier(HighlightingModifier::ClassScope);
806 }
807 return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(Q);
808 }
809
810 private:
811 HighlightingsBuilder &H;
812 };
813 } // namespace
814
getSemanticHighlightings(ParsedAST & AST)815 std::vector<HighlightingToken> getSemanticHighlightings(ParsedAST &AST) {
816 auto &C = AST.getASTContext();
817 // Add highlightings for AST nodes.
818 HighlightingsBuilder Builder(AST);
819 // Highlight 'decltype' and 'auto' as their underlying types.
820 CollectExtraHighlightings(Builder).TraverseAST(C);
821 // Highlight all decls and references coming from the AST.
822 findExplicitReferences(
823 C,
824 [&](ReferenceLoc R) {
825 for (const NamedDecl *Decl : R.Targets) {
826 if (!canHighlightName(Decl->getDeclName()))
827 continue;
828 auto Kind = kindForDecl(Decl, AST.getHeuristicResolver());
829 if (!Kind)
830 continue;
831 auto &Tok = Builder.addToken(R.NameLoc, *Kind);
832
833 // The attribute tests don't want to look at the template.
834 if (auto *TD = dyn_cast<TemplateDecl>(Decl)) {
835 if (auto *Templated = TD->getTemplatedDecl())
836 Decl = Templated;
837 }
838 if (auto Mod = scopeModifier(Decl))
839 Tok.addModifier(*Mod);
840 if (isConst(Decl))
841 Tok.addModifier(HighlightingModifier::Readonly);
842 if (isStatic(Decl))
843 Tok.addModifier(HighlightingModifier::Static);
844 if (isAbstract(Decl))
845 Tok.addModifier(HighlightingModifier::Abstract);
846 if (isVirtual(Decl))
847 Tok.addModifier(HighlightingModifier::Virtual);
848 if (isDependent(Decl))
849 Tok.addModifier(HighlightingModifier::DependentName);
850 if (isDefaultLibrary(Decl))
851 Tok.addModifier(HighlightingModifier::DefaultLibrary);
852 if (Decl->isDeprecated())
853 Tok.addModifier(HighlightingModifier::Deprecated);
854 // Do not treat an UnresolvedUsingValueDecl as a declaration.
855 // It's more common to think of it as a reference to the
856 // underlying declaration.
857 if (R.IsDecl && !isa<UnresolvedUsingValueDecl>(Decl))
858 Tok.addModifier(HighlightingModifier::Declaration);
859 }
860 },
861 AST.getHeuristicResolver());
862 // Add highlightings for macro references.
863 auto AddMacro = [&](const MacroOccurrence &M) {
864 auto &T = Builder.addToken(M.Rng, HighlightingKind::Macro);
865 T.addModifier(HighlightingModifier::GlobalScope);
866 if (M.IsDefinition)
867 T.addModifier(HighlightingModifier::Declaration);
868 };
869 for (const auto &SIDToRefs : AST.getMacros().MacroRefs)
870 for (const auto &M : SIDToRefs.second)
871 AddMacro(M);
872 for (const auto &M : AST.getMacros().UnknownMacros)
873 AddMacro(M);
874
875 return std::move(Builder).collect(AST);
876 }
877
operator <<(llvm::raw_ostream & OS,HighlightingKind K)878 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingKind K) {
879 switch (K) {
880 case HighlightingKind::Variable:
881 return OS << "Variable";
882 case HighlightingKind::LocalVariable:
883 return OS << "LocalVariable";
884 case HighlightingKind::Parameter:
885 return OS << "Parameter";
886 case HighlightingKind::Function:
887 return OS << "Function";
888 case HighlightingKind::Method:
889 return OS << "Method";
890 case HighlightingKind::StaticMethod:
891 return OS << "StaticMethod";
892 case HighlightingKind::Field:
893 return OS << "Field";
894 case HighlightingKind::StaticField:
895 return OS << "StaticField";
896 case HighlightingKind::Class:
897 return OS << "Class";
898 case HighlightingKind::Interface:
899 return OS << "Interface";
900 case HighlightingKind::Enum:
901 return OS << "Enum";
902 case HighlightingKind::EnumConstant:
903 return OS << "EnumConstant";
904 case HighlightingKind::Typedef:
905 return OS << "Typedef";
906 case HighlightingKind::Type:
907 return OS << "Type";
908 case HighlightingKind::Unknown:
909 return OS << "Unknown";
910 case HighlightingKind::Namespace:
911 return OS << "Namespace";
912 case HighlightingKind::TemplateParameter:
913 return OS << "TemplateParameter";
914 case HighlightingKind::Concept:
915 return OS << "Concept";
916 case HighlightingKind::Primitive:
917 return OS << "Primitive";
918 case HighlightingKind::Macro:
919 return OS << "Macro";
920 case HighlightingKind::InactiveCode:
921 return OS << "InactiveCode";
922 }
923 llvm_unreachable("invalid HighlightingKind");
924 }
operator <<(llvm::raw_ostream & OS,HighlightingModifier K)925 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingModifier K) {
926 switch (K) {
927 case HighlightingModifier::Declaration:
928 return OS << "decl"; // abbrevation for common case
929 default:
930 return OS << toSemanticTokenModifier(K);
931 }
932 }
933
operator ==(const HighlightingToken & L,const HighlightingToken & R)934 bool operator==(const HighlightingToken &L, const HighlightingToken &R) {
935 return std::tie(L.R, L.Kind, L.Modifiers) ==
936 std::tie(R.R, R.Kind, R.Modifiers);
937 }
operator <(const HighlightingToken & L,const HighlightingToken & R)938 bool operator<(const HighlightingToken &L, const HighlightingToken &R) {
939 return std::tie(L.R, L.Kind, L.Modifiers) <
940 std::tie(R.R, R.Kind, R.Modifiers);
941 }
942
943 std::vector<SemanticToken>
toSemanticTokens(llvm::ArrayRef<HighlightingToken> Tokens,llvm::StringRef Code)944 toSemanticTokens(llvm::ArrayRef<HighlightingToken> Tokens,
945 llvm::StringRef Code) {
946 assert(std::is_sorted(Tokens.begin(), Tokens.end()));
947 std::vector<SemanticToken> Result;
948 // In case we split a HighlightingToken into multiple tokens (e.g. because it
949 // was spanning multiple lines), this tracks the last one. This prevents
950 // having a copy all the time.
951 HighlightingToken Scratch;
952 const HighlightingToken *Last = nullptr;
953 for (const HighlightingToken &Tok : Tokens) {
954 Result.emplace_back();
955 SemanticToken *Out = &Result.back();
956 // deltaStart/deltaLine are relative if possible.
957 if (Last) {
958 assert(Tok.R.start.line >= Last->R.end.line);
959 Out->deltaLine = Tok.R.start.line - Last->R.end.line;
960 if (Out->deltaLine == 0) {
961 assert(Tok.R.start.character >= Last->R.start.character);
962 Out->deltaStart = Tok.R.start.character - Last->R.start.character;
963 } else {
964 Out->deltaStart = Tok.R.start.character;
965 }
966 } else {
967 Out->deltaLine = Tok.R.start.line;
968 Out->deltaStart = Tok.R.start.character;
969 }
970 Out->tokenType = static_cast<unsigned>(Tok.Kind);
971 Out->tokenModifiers = Tok.Modifiers;
972 Last = &Tok;
973
974 if (Tok.R.end.line == Tok.R.start.line) {
975 Out->length = Tok.R.end.character - Tok.R.start.character;
976 } else {
977 // If the token spans a line break, split it into multiple pieces for each
978 // line.
979 // This is slow, but multiline tokens are rare.
980 // FIXME: There's a client capability for supporting multiline tokens,
981 // respect that.
982 auto TokStartOffset = llvm::cantFail(positionToOffset(Code, Tok.R.start));
983 // Note that the loop doesn't cover the last line, which has a special
984 // length.
985 for (int I = Tok.R.start.line; I < Tok.R.end.line; ++I) {
986 auto LineEnd = Code.find('\n', TokStartOffset);
987 assert(LineEnd != Code.npos);
988 Out->length = LineEnd - TokStartOffset;
989 // Token continues on next line, right after the line break.
990 TokStartOffset = LineEnd + 1;
991 Result.emplace_back();
992 Out = &Result.back();
993 *Out = Result[Result.size() - 2];
994 // New token starts at the first column of the next line.
995 Out->deltaLine = 1;
996 Out->deltaStart = 0;
997 }
998 // This is the token on last line.
999 Out->length = Tok.R.end.character;
1000 // Update the start location for last token, as that's used in the
1001 // relative delta calculation for following tokens.
1002 Scratch = *Last;
1003 Scratch.R.start.line = Tok.R.end.line;
1004 Scratch.R.start.character = 0;
1005 Last = &Scratch;
1006 }
1007 }
1008 return Result;
1009 }
toSemanticTokenType(HighlightingKind Kind)1010 llvm::StringRef toSemanticTokenType(HighlightingKind Kind) {
1011 switch (Kind) {
1012 case HighlightingKind::Variable:
1013 case HighlightingKind::LocalVariable:
1014 case HighlightingKind::StaticField:
1015 return "variable";
1016 case HighlightingKind::Parameter:
1017 return "parameter";
1018 case HighlightingKind::Function:
1019 return "function";
1020 case HighlightingKind::Method:
1021 return "method";
1022 case HighlightingKind::StaticMethod:
1023 // FIXME: better method with static modifier?
1024 return "function";
1025 case HighlightingKind::Field:
1026 return "property";
1027 case HighlightingKind::Class:
1028 return "class";
1029 case HighlightingKind::Interface:
1030 return "interface";
1031 case HighlightingKind::Enum:
1032 return "enum";
1033 case HighlightingKind::EnumConstant:
1034 return "enumMember";
1035 case HighlightingKind::Typedef:
1036 case HighlightingKind::Type:
1037 return "type";
1038 case HighlightingKind::Unknown:
1039 return "unknown"; // nonstandard
1040 case HighlightingKind::Namespace:
1041 return "namespace";
1042 case HighlightingKind::TemplateParameter:
1043 return "typeParameter";
1044 case HighlightingKind::Concept:
1045 return "concept"; // nonstandard
1046 case HighlightingKind::Primitive:
1047 return "type";
1048 case HighlightingKind::Macro:
1049 return "macro";
1050 case HighlightingKind::InactiveCode:
1051 return "comment";
1052 }
1053 llvm_unreachable("unhandled HighlightingKind");
1054 }
1055
toSemanticTokenModifier(HighlightingModifier Modifier)1056 llvm::StringRef toSemanticTokenModifier(HighlightingModifier Modifier) {
1057 switch (Modifier) {
1058 case HighlightingModifier::Declaration:
1059 return "declaration";
1060 case HighlightingModifier::Deprecated:
1061 return "deprecated";
1062 case HighlightingModifier::Readonly:
1063 return "readonly";
1064 case HighlightingModifier::Static:
1065 return "static";
1066 case HighlightingModifier::Deduced:
1067 return "deduced"; // nonstandard
1068 case HighlightingModifier::Abstract:
1069 return "abstract";
1070 case HighlightingModifier::Virtual:
1071 return "virtual";
1072 case HighlightingModifier::DependentName:
1073 return "dependentName"; // nonstandard
1074 case HighlightingModifier::DefaultLibrary:
1075 return "defaultLibrary";
1076 case HighlightingModifier::UsedAsMutableReference:
1077 return "usedAsMutableReference"; // nonstandard
1078 case HighlightingModifier::FunctionScope:
1079 return "functionScope"; // nonstandard
1080 case HighlightingModifier::ClassScope:
1081 return "classScope"; // nonstandard
1082 case HighlightingModifier::FileScope:
1083 return "fileScope"; // nonstandard
1084 case HighlightingModifier::GlobalScope:
1085 return "globalScope"; // nonstandard
1086 }
1087 llvm_unreachable("unhandled HighlightingModifier");
1088 }
1089
1090 std::vector<SemanticTokensEdit>
diffTokens(llvm::ArrayRef<SemanticToken> Old,llvm::ArrayRef<SemanticToken> New)1091 diffTokens(llvm::ArrayRef<SemanticToken> Old,
1092 llvm::ArrayRef<SemanticToken> New) {
1093 // For now, just replace everything from the first-last modification.
1094 // FIXME: use a real diff instead, this is bad with include-insertion.
1095
1096 unsigned Offset = 0;
1097 while (!Old.empty() && !New.empty() && Old.front() == New.front()) {
1098 ++Offset;
1099 Old = Old.drop_front();
1100 New = New.drop_front();
1101 }
1102 while (!Old.empty() && !New.empty() && Old.back() == New.back()) {
1103 Old = Old.drop_back();
1104 New = New.drop_back();
1105 }
1106
1107 if (Old.empty() && New.empty())
1108 return {};
1109 SemanticTokensEdit Edit;
1110 Edit.startToken = Offset;
1111 Edit.deleteTokens = Old.size();
1112 Edit.tokens = New;
1113 return {std::move(Edit)};
1114 }
1115
1116 } // namespace clangd
1117 } // namespace clang
1118