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