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