Mercurial > hg > CbC > CbC_llvm
comparison clang-tools-extra/clangd/SemanticHighlighting.cpp @ 221:79ff65ed7e25
LLVM12 Original
author | Shinji KONO <kono@ie.u-ryukyu.ac.jp> |
---|---|
date | Tue, 15 Jun 2021 19:15:29 +0900 |
parents | 0572611fdcc8 |
children | 5f17cb93ff66 |
comparison
equal
deleted
inserted
replaced
220:42394fc6a535 | 221:79ff65ed7e25 |
---|---|
6 // | 6 // |
7 //===----------------------------------------------------------------------===// | 7 //===----------------------------------------------------------------------===// |
8 | 8 |
9 #include "SemanticHighlighting.h" | 9 #include "SemanticHighlighting.h" |
10 #include "FindTarget.h" | 10 #include "FindTarget.h" |
11 #include "HeuristicResolver.h" | |
11 #include "ParsedAST.h" | 12 #include "ParsedAST.h" |
12 #include "Protocol.h" | 13 #include "Protocol.h" |
13 #include "SourceCode.h" | 14 #include "SourceCode.h" |
14 #include "support/Logger.h" | 15 #include "support/Logger.h" |
15 #include "clang/AST/ASTContext.h" | 16 #include "clang/AST/ASTContext.h" |
16 #include "clang/AST/Decl.h" | 17 #include "clang/AST/Decl.h" |
17 #include "clang/AST/DeclCXX.h" | 18 #include "clang/AST/DeclCXX.h" |
19 #include "clang/AST/DeclObjC.h" | |
20 #include "clang/AST/DeclTemplate.h" | |
18 #include "clang/AST/DeclarationName.h" | 21 #include "clang/AST/DeclarationName.h" |
19 #include "clang/AST/ExprCXX.h" | 22 #include "clang/AST/ExprCXX.h" |
20 #include "clang/AST/RecursiveASTVisitor.h" | 23 #include "clang/AST/RecursiveASTVisitor.h" |
21 #include "clang/AST/Type.h" | 24 #include "clang/AST/Type.h" |
22 #include "clang/AST/TypeLoc.h" | 25 #include "clang/AST/TypeLoc.h" |
36 namespace { | 39 namespace { |
37 | 40 |
38 /// Some names are not written in the source code and cannot be highlighted, | 41 /// Some names are not written in the source code and cannot be highlighted, |
39 /// e.g. anonymous classes. This function detects those cases. | 42 /// e.g. anonymous classes. This function detects those cases. |
40 bool canHighlightName(DeclarationName Name) { | 43 bool canHighlightName(DeclarationName Name) { |
41 if (Name.getNameKind() == DeclarationName::CXXConstructorName || | 44 switch (Name.getNameKind()) { |
42 Name.getNameKind() == DeclarationName::CXXUsingDirective) | 45 case DeclarationName::Identifier: { |
43 return true; | 46 auto *II = Name.getAsIdentifierInfo(); |
44 auto *II = Name.getAsIdentifierInfo(); | 47 return II && !II->getName().empty(); |
45 return II && !II->getName().empty(); | 48 } |
46 } | 49 case DeclarationName::CXXConstructorName: |
47 | 50 case DeclarationName::CXXDestructorName: |
48 llvm::Optional<HighlightingKind> kindForType(const Type *TP); | 51 return true; |
49 llvm::Optional<HighlightingKind> kindForDecl(const NamedDecl *D) { | 52 case DeclarationName::ObjCZeroArgSelector: |
53 case DeclarationName::ObjCOneArgSelector: | |
54 case DeclarationName::ObjCMultiArgSelector: | |
55 // Multi-arg selectors need special handling, and we handle 0/1 arg | |
56 // selectors there too. | |
57 return false; | |
58 case DeclarationName::CXXConversionFunctionName: | |
59 case DeclarationName::CXXOperatorName: | |
60 case DeclarationName::CXXDeductionGuideName: | |
61 case DeclarationName::CXXLiteralOperatorName: | |
62 case DeclarationName::CXXUsingDirective: | |
63 return false; | |
64 } | |
65 llvm_unreachable("invalid name kind"); | |
66 } | |
67 | |
68 llvm::Optional<HighlightingKind> kindForType(const Type *TP, | |
69 const HeuristicResolver *Resolver); | |
70 llvm::Optional<HighlightingKind> | |
71 kindForDecl(const NamedDecl *D, const HeuristicResolver *Resolver) { | |
50 if (auto *USD = dyn_cast<UsingShadowDecl>(D)) { | 72 if (auto *USD = dyn_cast<UsingShadowDecl>(D)) { |
51 if (auto *Target = USD->getTargetDecl()) | 73 if (auto *Target = USD->getTargetDecl()) |
52 D = Target; | 74 D = Target; |
53 } | 75 } |
54 if (auto *TD = dyn_cast<TemplateDecl>(D)) { | 76 if (auto *TD = dyn_cast<TemplateDecl>(D)) { |
55 if (auto *Templated = TD->getTemplatedDecl()) | 77 if (auto *Templated = TD->getTemplatedDecl()) |
56 D = Templated; | 78 D = Templated; |
57 } | 79 } |
58 if (auto *TD = dyn_cast<TypedefNameDecl>(D)) { | 80 if (auto *TD = dyn_cast<TypedefNameDecl>(D)) { |
59 // We try to highlight typedefs as their underlying type. | 81 // We try to highlight typedefs as their underlying type. |
60 if (auto K = kindForType(TD->getUnderlyingType().getTypePtrOrNull())) | 82 if (auto K = |
83 kindForType(TD->getUnderlyingType().getTypePtrOrNull(), Resolver)) | |
61 return K; | 84 return K; |
62 // And fallback to a generic kind if this fails. | 85 // And fallback to a generic kind if this fails. |
63 return HighlightingKind::Typedef; | 86 return HighlightingKind::Typedef; |
64 } | 87 } |
65 // We highlight class decls, constructor decls and destructor decls as | 88 // We highlight class decls, constructor decls and destructor decls as |
69 // We don't want to highlight lambdas like classes. | 92 // We don't want to highlight lambdas like classes. |
70 if (RD->isLambda()) | 93 if (RD->isLambda()) |
71 return llvm::None; | 94 return llvm::None; |
72 return HighlightingKind::Class; | 95 return HighlightingKind::Class; |
73 } | 96 } |
74 if (isa<ClassTemplateDecl>(D) || isa<RecordDecl>(D) || | 97 if (isa<ClassTemplateDecl, RecordDecl, CXXConstructorDecl, ObjCInterfaceDecl, |
75 isa<CXXConstructorDecl>(D)) | 98 ObjCImplementationDecl>(D)) |
76 return HighlightingKind::Class; | 99 return HighlightingKind::Class; |
100 if (isa<ObjCProtocolDecl>(D)) | |
101 return HighlightingKind::Interface; | |
102 if (isa<ObjCCategoryDecl>(D)) | |
103 return HighlightingKind::Namespace; | |
77 if (auto *MD = dyn_cast<CXXMethodDecl>(D)) | 104 if (auto *MD = dyn_cast<CXXMethodDecl>(D)) |
78 return MD->isStatic() ? HighlightingKind::StaticMethod | 105 return MD->isStatic() ? HighlightingKind::StaticMethod |
79 : HighlightingKind::Method; | 106 : HighlightingKind::Method; |
80 if (isa<FieldDecl>(D)) | 107 if (auto *OMD = dyn_cast<ObjCMethodDecl>(D)) |
108 return OMD->isClassMethod() ? HighlightingKind::StaticMethod | |
109 : HighlightingKind::Method; | |
110 if (isa<FieldDecl, ObjCPropertyDecl>(D)) | |
81 return HighlightingKind::Field; | 111 return HighlightingKind::Field; |
82 if (isa<EnumDecl>(D)) | 112 if (isa<EnumDecl>(D)) |
83 return HighlightingKind::Enum; | 113 return HighlightingKind::Enum; |
84 if (isa<EnumConstantDecl>(D)) | 114 if (isa<EnumConstantDecl>(D)) |
85 return HighlightingKind::EnumConstant; | 115 return HighlightingKind::EnumConstant; |
86 if (isa<ParmVarDecl>(D)) | 116 if (isa<ParmVarDecl>(D)) |
87 return HighlightingKind::Parameter; | 117 return HighlightingKind::Parameter; |
88 if (auto *VD = dyn_cast<VarDecl>(D)) | 118 if (auto *VD = dyn_cast<VarDecl>(D)) { |
119 if (isa<ImplicitParamDecl>(VD)) // e.g. ObjC Self | |
120 return llvm::None; | |
89 return VD->isStaticDataMember() | 121 return VD->isStaticDataMember() |
90 ? HighlightingKind::StaticField | 122 ? HighlightingKind::StaticField |
91 : VD->isLocalVarDecl() ? HighlightingKind::LocalVariable | 123 : VD->isLocalVarDecl() ? HighlightingKind::LocalVariable |
92 : HighlightingKind::Variable; | 124 : HighlightingKind::Variable; |
93 if (isa<BindingDecl>(D)) | 125 } |
94 return HighlightingKind::Variable; | 126 if (const auto *BD = dyn_cast<BindingDecl>(D)) |
127 return BD->getDeclContext()->isFunctionOrMethod() | |
128 ? HighlightingKind::LocalVariable | |
129 : HighlightingKind::Variable; | |
95 if (isa<FunctionDecl>(D)) | 130 if (isa<FunctionDecl>(D)) |
96 return HighlightingKind::Function; | 131 return HighlightingKind::Function; |
97 if (isa<NamespaceDecl>(D) || isa<NamespaceAliasDecl>(D) || | 132 if (isa<NamespaceDecl>(D) || isa<NamespaceAliasDecl>(D) || |
98 isa<UsingDirectiveDecl>(D)) | 133 isa<UsingDirectiveDecl>(D)) |
99 return HighlightingKind::Namespace; | 134 return HighlightingKind::Namespace; |
100 if (isa<TemplateTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(D) || | 135 if (isa<TemplateTemplateParmDecl>(D) || isa<TemplateTypeParmDecl>(D) || |
101 isa<NonTypeTemplateParmDecl>(D)) | 136 isa<NonTypeTemplateParmDecl>(D)) |
102 return HighlightingKind::TemplateParameter; | 137 return HighlightingKind::TemplateParameter; |
103 if (isa<ConceptDecl>(D)) | 138 if (isa<ConceptDecl>(D)) |
104 return HighlightingKind::Concept; | 139 return HighlightingKind::Concept; |
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 } | |
105 return llvm::None; | 147 return llvm::None; |
106 } | 148 } |
107 llvm::Optional<HighlightingKind> kindForType(const Type *TP) { | 149 llvm::Optional<HighlightingKind> |
150 kindForType(const Type *TP, const HeuristicResolver *Resolver) { | |
108 if (!TP) | 151 if (!TP) |
109 return llvm::None; | 152 return llvm::None; |
110 if (TP->isBuiltinType()) // Builtins are special, they do not have decls. | 153 if (TP->isBuiltinType()) // Builtins are special, they do not have decls. |
111 return HighlightingKind::Primitive; | 154 return HighlightingKind::Primitive; |
112 if (auto *TD = dyn_cast<TemplateTypeParmType>(TP)) | 155 if (auto *TD = dyn_cast<TemplateTypeParmType>(TP)) |
113 return kindForDecl(TD->getDecl()); | 156 return kindForDecl(TD->getDecl(), Resolver); |
157 if (isa<ObjCObjectPointerType>(TP)) | |
158 return HighlightingKind::Class; | |
114 if (auto *TD = TP->getAsTagDecl()) | 159 if (auto *TD = TP->getAsTagDecl()) |
115 return kindForDecl(TD); | 160 return kindForDecl(TD, Resolver); |
116 return llvm::None; | 161 return llvm::None; |
117 } | 162 } |
118 | 163 |
119 llvm::Optional<HighlightingKind> kindForReference(const ReferenceLoc &R) { | 164 // Whether T is const in a loose sense - is a variable with this type readonly? |
120 llvm::Optional<HighlightingKind> Result; | 165 bool isConst(QualType T) { |
121 for (const NamedDecl *Decl : R.Targets) { | 166 if (T.isNull() || T->isDependentType()) |
122 if (!canHighlightName(Decl->getDeclName())) | 167 return false; |
123 return llvm::None; | 168 T = T.getNonReferenceType(); |
124 auto Kind = kindForDecl(Decl); | 169 if (T.isConstQualified()) |
125 if (!Kind || (Result && Kind != Result)) | 170 return true; |
126 return llvm::None; | 171 if (const auto *AT = T->getAsArrayTypeUnsafe()) |
127 Result = Kind; | 172 return isConst(AT->getElementType()); |
128 } | 173 if (isConst(T->getPointeeType())) |
129 return Result; | 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; | |
200 } | |
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; | |
130 } | 270 } |
131 | 271 |
132 // For a macro usage `DUMP(foo)`, we want: | 272 // For a macro usage `DUMP(foo)`, we want: |
133 // - DUMP --> "macro" | 273 // - DUMP --> "macro" |
134 // - foo --> "variable". | 274 // - foo --> "variable". |
141 return {}; | 281 return {}; |
142 // Tokens expanded from macro args are potentially highlightable. | 282 // Tokens expanded from macro args are potentially highlightable. |
143 return getHighlightableSpellingToken(SM.getImmediateSpellingLoc(L), SM); | 283 return getHighlightableSpellingToken(SM.getImmediateSpellingLoc(L), SM); |
144 } | 284 } |
145 | 285 |
146 unsigned evaluateHighlightPriority(HighlightingKind Kind) { | 286 unsigned evaluateHighlightPriority(const HighlightingToken &Tok) { |
147 enum HighlightPriority { Dependent = 0, Resolved = 1 }; | 287 enum HighlightPriority { Dependent = 0, Resolved = 1 }; |
148 return Kind == HighlightingKind::DependentType || | 288 return (Tok.Modifiers & (1 << uint32_t(HighlightingModifier::DependentName))) |
149 Kind == HighlightingKind::DependentName | |
150 ? Dependent | 289 ? Dependent |
151 : Resolved; | 290 : Resolved; |
152 } | 291 } |
153 | 292 |
154 // Sometimes we get conflicts between findExplicitReferences() returning | 293 // Sometimes we get multiple tokens at the same location: |
155 // a heuristic result for a dependent name (e.g. Method) and | 294 // |
156 // CollectExtraHighlighting returning a fallback dependent highlighting (e.g. | 295 // - findExplicitReferences() returns a heuristic result for a dependent name |
157 // DependentName). In such cases, resolve the conflict in favour of the | 296 // (e.g. Method) and CollectExtraHighlighting returning a fallback dependent |
158 // resolved (non-dependent) highlighting. | 297 // highlighting (e.g. Unknown+Dependent). |
159 // With macros we can get other conflicts (if a spelled token has multiple | 298 // - macro arguments are expanded multiple times and have different roles |
160 // expansions with different token types) which we can't usefully resolve. | 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. | |
161 llvm::Optional<HighlightingToken> | 311 llvm::Optional<HighlightingToken> |
162 resolveConflict(ArrayRef<HighlightingToken> Tokens) { | 312 resolveConflict(ArrayRef<HighlightingToken> Tokens) { |
163 if (Tokens.size() == 1) | 313 if (Tokens.size() == 1) |
164 return Tokens[0]; | 314 return Tokens[0]; |
165 | 315 |
166 if (Tokens.size() != 2) | 316 if (Tokens.size() != 2) |
167 return llvm::None; | 317 return llvm::None; |
168 | 318 |
169 unsigned Priority1 = evaluateHighlightPriority(Tokens[0].Kind); | 319 unsigned Priority1 = evaluateHighlightPriority(Tokens[0]); |
170 unsigned Priority2 = evaluateHighlightPriority(Tokens[1].Kind); | 320 unsigned Priority2 = evaluateHighlightPriority(Tokens[1]); |
171 if (Priority1 == Priority2) | 321 if (Priority1 == Priority2 && Tokens[0].Kind != Tokens[1].Kind) |
172 return llvm::None; | 322 return llvm::None; |
173 return Priority1 > Priority2 ? Tokens[0] : Tokens[1]; | 323 auto Result = Priority1 > Priority2 ? Tokens[0] : Tokens[1]; |
324 Result.Modifiers = Tokens[0].Modifiers | Tokens[1].Modifiers; | |
325 return Result; | |
174 } | 326 } |
175 | 327 |
176 /// Consumes source locations and maps them to text ranges for highlightings. | 328 /// Consumes source locations and maps them to text ranges for highlightings. |
177 class HighlightingsBuilder { | 329 class HighlightingsBuilder { |
178 public: | 330 public: |
179 HighlightingsBuilder(const ParsedAST &AST) | 331 HighlightingsBuilder(const ParsedAST &AST) |
180 : TB(AST.getTokens()), SourceMgr(AST.getSourceManager()), | 332 : TB(AST.getTokens()), SourceMgr(AST.getSourceManager()), |
181 LangOpts(AST.getLangOpts()) {} | 333 LangOpts(AST.getLangOpts()) {} |
182 | 334 |
183 void addToken(HighlightingToken T) { Tokens.push_back(T); } | 335 HighlightingToken &addToken(SourceLocation Loc, HighlightingKind Kind) { |
184 | |
185 void addToken(SourceLocation Loc, HighlightingKind Kind) { | |
186 Loc = getHighlightableSpellingToken(Loc, SourceMgr); | 336 Loc = getHighlightableSpellingToken(Loc, SourceMgr); |
187 if (Loc.isInvalid()) | 337 if (Loc.isInvalid()) |
188 return; | 338 return InvalidHighlightingToken; |
189 const auto *Tok = TB.spelledTokenAt(Loc); | 339 const auto *Tok = TB.spelledTokenAt(Loc); |
190 assert(Tok); | 340 assert(Tok); |
191 | 341 return addToken( |
192 auto Range = halfOpenToRange(SourceMgr, | 342 halfOpenToRange(SourceMgr, |
193 Tok->range(SourceMgr).toCharRange(SourceMgr)); | 343 Tok->range(SourceMgr).toCharRange(SourceMgr)), |
194 Tokens.push_back(HighlightingToken{Kind, std::move(Range)}); | 344 Kind); |
345 } | |
346 | |
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(); | |
195 } | 353 } |
196 | 354 |
197 std::vector<HighlightingToken> collect(ParsedAST &AST) && { | 355 std::vector<HighlightingToken> collect(ParsedAST &AST) && { |
198 // Initializer lists can give duplicates of tokens, therefore all tokens | 356 // Initializer lists can give duplicates of tokens, therefore all tokens |
199 // must be deduplicated. | 357 // must be deduplicated. |
217 NonConflicting.push_back(*Resolved); | 375 NonConflicting.push_back(*Resolved); |
218 // TokRef[Conflicting.size()] is the next token with a different range (or | 376 // TokRef[Conflicting.size()] is the next token with a different range (or |
219 // the end of the Tokens). | 377 // the end of the Tokens). |
220 TokRef = TokRef.drop_front(Conflicting.size()); | 378 TokRef = TokRef.drop_front(Conflicting.size()); |
221 } | 379 } |
222 // Add tokens indicating lines skipped by the preprocessor. | 380 const auto &SM = AST.getSourceManager(); |
223 for (const Range &R : AST.getMacros().SkippedRanges) { | 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) { | |
224 // Create one token for each line in the skipped range, so it works | 389 // Create one token for each line in the skipped range, so it works |
225 // with line-based diffing. | 390 // with line-based diffing. |
226 assert(R.start.line <= R.end.line); | 391 assert(R.start.line <= R.end.line); |
227 for (int Line = R.start.line; Line < R.end.line; ++Line) { | 392 for (int Line = R.start.line; Line <= R.end.line; ++Line) { |
228 // Don't bother computing the offset for the end of the line, just use | 393 // If the end of the inactive range is at the beginning |
229 // zero. The client will treat this highlighting kind specially, and | 394 // of a line, that line is not inactive. |
230 // highlight the entire line visually (i.e. not just to where the text | 395 if (Line == R.end.line && R.end.character == 0) |
231 // on the line ends, but to the end of the screen). | 396 continue; |
232 NonConflicting.push_back({HighlightingKind::InactiveCode, | 397 // Copy tokens before the inactive line |
233 {Position{Line, 0}, Position{Line, 0}}}); | 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; | |
234 } | 426 } |
235 } | 427 } |
236 // Re-sort the tokens because that's what the diffing expects. | 428 // Copy tokens after the last inactive line |
237 llvm::sort(NonConflicting); | 429 for (; It != NonConflicting.end(); ++It) |
238 return NonConflicting; | 430 WithInactiveLines.push_back(std::move(*It)); |
239 } | 431 return WithInactiveLines; |
432 } | |
433 | |
434 const HeuristicResolver *getResolver() const { return Resolver; } | |
240 | 435 |
241 private: | 436 private: |
242 const syntax::TokenBuffer &TB; | 437 const syntax::TokenBuffer &TB; |
243 const SourceManager &SourceMgr; | 438 const SourceManager &SourceMgr; |
244 const LangOptions &LangOpts; | 439 const LangOptions &LangOpts; |
245 std::vector<HighlightingToken> Tokens; | 440 std::vector<HighlightingToken> Tokens; |
441 const HeuristicResolver *Resolver; | |
442 // returned from addToken(InvalidLoc) | |
443 HighlightingToken InvalidHighlightingToken; | |
246 }; | 444 }; |
445 | |
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 } | |
247 | 485 |
248 /// Produces highlightings, which are not captured by findExplicitReferences, | 486 /// Produces highlightings, which are not captured by findExplicitReferences, |
249 /// e.g. highlights dependent names and 'auto' as the underlying type. | 487 /// e.g. highlights dependent names and 'auto' as the underlying type. |
250 class CollectExtraHighlightings | 488 class CollectExtraHighlightings |
251 : public RecursiveASTVisitor<CollectExtraHighlightings> { | 489 : public RecursiveASTVisitor<CollectExtraHighlightings> { |
252 public: | 490 public: |
253 CollectExtraHighlightings(HighlightingsBuilder &H) : H(H) {} | 491 CollectExtraHighlightings(HighlightingsBuilder &H) : H(H) {} |
254 | 492 |
255 bool VisitDecltypeTypeLoc(DecltypeTypeLoc L) { | 493 bool VisitDecltypeTypeLoc(DecltypeTypeLoc L) { |
256 if (auto K = kindForType(L.getTypePtr())) | 494 if (auto K = kindForType(L.getTypePtr(), H.getResolver())) { |
257 H.addToken(L.getBeginLoc(), *K); | 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 } | |
258 return true; | 502 return true; |
259 } | 503 } |
260 | 504 |
261 bool VisitDeclaratorDecl(DeclaratorDecl *D) { | 505 bool VisitDeclaratorDecl(DeclaratorDecl *D) { |
262 auto *AT = D->getType()->getContainedAutoType(); | 506 auto *AT = D->getType()->getContainedAutoType(); |
263 if (!AT) | 507 if (!AT) |
264 return true; | 508 return true; |
265 if (auto K = kindForType(AT->getDeducedType().getTypePtrOrNull())) | 509 if (auto K = kindForType(AT->getDeducedType().getTypePtrOrNull(), |
266 H.addToken(D->getTypeSpecStartLoc(), *K); | 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); | |
267 return true; | 556 return true; |
268 } | 557 } |
269 | 558 |
270 bool VisitOverloadExpr(OverloadExpr *E) { | 559 bool VisitOverloadExpr(OverloadExpr *E) { |
271 if (!E->decls().empty()) | 560 if (!E->decls().empty()) |
272 return true; // handled by findExplicitReferences. | 561 return true; // handled by findExplicitReferences. |
273 H.addToken(E->getNameLoc(), HighlightingKind::DependentName); | 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. | |
274 return true; | 567 return true; |
275 } | 568 } |
276 | 569 |
277 bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) { | 570 bool VisitCXXDependentScopeMemberExpr(CXXDependentScopeMemberExpr *E) { |
278 H.addToken(E->getMemberNameInfo().getLoc(), | 571 H.addToken(E->getMemberNameInfo().getLoc(), HighlightingKind::Unknown) |
279 HighlightingKind::DependentName); | 572 .addModifier(HighlightingModifier::DependentName) |
573 .addModifier(HighlightingModifier::ClassScope); | |
280 return true; | 574 return true; |
281 } | 575 } |
282 | 576 |
283 bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) { | 577 bool VisitDependentScopeDeclRefExpr(DependentScopeDeclRefExpr *E) { |
284 H.addToken(E->getNameInfo().getLoc(), HighlightingKind::DependentName); | 578 H.addToken(E->getNameInfo().getLoc(), HighlightingKind::Unknown) |
579 .addModifier(HighlightingModifier::DependentName) | |
580 .addModifier(HighlightingModifier::ClassScope); | |
285 return true; | 581 return true; |
286 } | 582 } |
287 | 583 |
288 bool VisitDependentNameTypeLoc(DependentNameTypeLoc L) { | 584 bool VisitDependentNameTypeLoc(DependentNameTypeLoc L) { |
289 H.addToken(L.getNameLoc(), HighlightingKind::DependentType); | 585 H.addToken(L.getNameLoc(), HighlightingKind::Type) |
586 .addModifier(HighlightingModifier::DependentName) | |
587 .addModifier(HighlightingModifier::ClassScope); | |
290 return true; | 588 return true; |
291 } | 589 } |
292 | 590 |
293 bool VisitDependentTemplateSpecializationTypeLoc( | 591 bool VisitDependentTemplateSpecializationTypeLoc( |
294 DependentTemplateSpecializationTypeLoc L) { | 592 DependentTemplateSpecializationTypeLoc L) { |
295 H.addToken(L.getTemplateNameLoc(), HighlightingKind::DependentType); | 593 H.addToken(L.getTemplateNameLoc(), HighlightingKind::Type) |
296 return true; | 594 .addModifier(HighlightingModifier::DependentName) |
595 .addModifier(HighlightingModifier::ClassScope); | |
596 return true; | |
597 } | |
598 | |
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); | |
297 } | 626 } |
298 | 627 |
299 // findExplicitReferences will walk nested-name-specifiers and | 628 // findExplicitReferences will walk nested-name-specifiers and |
300 // find anything that can be resolved to a Decl. However, non-leaf | 629 // find anything that can be resolved to a Decl. However, non-leaf |
301 // components of nested-name-specifiers which are dependent names | 630 // components of nested-name-specifiers which are dependent names |
302 // (kind "Identifier") cannot be resolved to a decl, so we visit | 631 // (kind "Identifier") cannot be resolved to a decl, so we visit |
303 // them here. | 632 // them here. |
304 bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc Q) { | 633 bool TraverseNestedNameSpecifierLoc(NestedNameSpecifierLoc Q) { |
305 if (NestedNameSpecifier *NNS = Q.getNestedNameSpecifier()) { | 634 if (NestedNameSpecifier *NNS = Q.getNestedNameSpecifier()) { |
306 if (NNS->getKind() == NestedNameSpecifier::Identifier) | 635 if (NNS->getKind() == NestedNameSpecifier::Identifier) |
307 H.addToken(Q.getLocalBeginLoc(), HighlightingKind::DependentType); | 636 H.addToken(Q.getLocalBeginLoc(), HighlightingKind::Type) |
637 .addModifier(HighlightingModifier::DependentName) | |
638 .addModifier(HighlightingModifier::ClassScope); | |
308 } | 639 } |
309 return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(Q); | 640 return RecursiveASTVisitor::TraverseNestedNameSpecifierLoc(Q); |
310 } | 641 } |
311 | 642 |
312 private: | 643 private: |
313 HighlightingsBuilder &H; | 644 HighlightingsBuilder &H; |
314 }; | 645 }; |
315 | |
316 void write32be(uint32_t I, llvm::raw_ostream &OS) { | |
317 std::array<char, 4> Buf; | |
318 llvm::support::endian::write32be(Buf.data(), I); | |
319 OS.write(Buf.data(), Buf.size()); | |
320 } | |
321 | |
322 void write16be(uint16_t I, llvm::raw_ostream &OS) { | |
323 std::array<char, 2> Buf; | |
324 llvm::support::endian::write16be(Buf.data(), I); | |
325 OS.write(Buf.data(), Buf.size()); | |
326 } | |
327 | |
328 // Get the highlightings on \c Line where the first entry of line is at \c | |
329 // StartLineIt. If it is not at \c StartLineIt an empty vector is returned. | |
330 ArrayRef<HighlightingToken> | |
331 takeLine(ArrayRef<HighlightingToken> AllTokens, | |
332 ArrayRef<HighlightingToken>::iterator StartLineIt, int Line) { | |
333 return ArrayRef<HighlightingToken>(StartLineIt, AllTokens.end()) | |
334 .take_while([Line](const HighlightingToken &Token) { | |
335 return Token.R.start.line == Line; | |
336 }); | |
337 } | |
338 } // namespace | 646 } // namespace |
339 | 647 |
340 std::vector<HighlightingToken> getSemanticHighlightings(ParsedAST &AST) { | 648 std::vector<HighlightingToken> getSemanticHighlightings(ParsedAST &AST) { |
341 auto &C = AST.getASTContext(); | 649 auto &C = AST.getASTContext(); |
342 // Add highlightings for AST nodes. | 650 // Add highlightings for AST nodes. |
343 HighlightingsBuilder Builder(AST); | 651 HighlightingsBuilder Builder(AST); |
344 // Highlight 'decltype' and 'auto' as their underlying types. | 652 // Highlight 'decltype' and 'auto' as their underlying types. |
345 CollectExtraHighlightings(Builder).TraverseAST(C); | 653 CollectExtraHighlightings(Builder).TraverseAST(C); |
346 // Highlight all decls and references coming from the AST. | 654 // Highlight all decls and references coming from the AST. |
347 findExplicitReferences(C, [&](ReferenceLoc R) { | 655 findExplicitReferences( |
348 if (auto Kind = kindForReference(R)) | 656 C, |
349 Builder.addToken(R.NameLoc, *Kind); | 657 [&](ReferenceLoc R) { |
350 }); | 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()); | |
351 // Add highlightings for macro references. | 693 // Add highlightings for macro references. |
352 for (const auto &SIDToRefs : AST.getMacros().MacroRefs) { | 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) | |
353 for (const auto &M : SIDToRefs.second) | 701 for (const auto &M : SIDToRefs.second) |
354 Builder.addToken({HighlightingKind::Macro, M}); | 702 AddMacro(M); |
355 } | |
356 for (const auto &M : AST.getMacros().UnknownMacros) | 703 for (const auto &M : AST.getMacros().UnknownMacros) |
357 Builder.addToken({HighlightingKind::Macro, M}); | 704 AddMacro(M); |
358 | 705 |
359 return std::move(Builder).collect(AST); | 706 return std::move(Builder).collect(AST); |
360 } | 707 } |
361 | 708 |
362 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingKind K) { | 709 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingKind K) { |
377 return OS << "Field"; | 724 return OS << "Field"; |
378 case HighlightingKind::StaticField: | 725 case HighlightingKind::StaticField: |
379 return OS << "StaticField"; | 726 return OS << "StaticField"; |
380 case HighlightingKind::Class: | 727 case HighlightingKind::Class: |
381 return OS << "Class"; | 728 return OS << "Class"; |
729 case HighlightingKind::Interface: | |
730 return OS << "Interface"; | |
382 case HighlightingKind::Enum: | 731 case HighlightingKind::Enum: |
383 return OS << "Enum"; | 732 return OS << "Enum"; |
384 case HighlightingKind::EnumConstant: | 733 case HighlightingKind::EnumConstant: |
385 return OS << "EnumConstant"; | 734 return OS << "EnumConstant"; |
386 case HighlightingKind::Typedef: | 735 case HighlightingKind::Typedef: |
387 return OS << "Typedef"; | 736 return OS << "Typedef"; |
388 case HighlightingKind::DependentType: | 737 case HighlightingKind::Type: |
389 return OS << "DependentType"; | 738 return OS << "Type"; |
390 case HighlightingKind::DependentName: | 739 case HighlightingKind::Unknown: |
391 return OS << "DependentName"; | 740 return OS << "Unknown"; |
392 case HighlightingKind::Namespace: | 741 case HighlightingKind::Namespace: |
393 return OS << "Namespace"; | 742 return OS << "Namespace"; |
394 case HighlightingKind::TemplateParameter: | 743 case HighlightingKind::TemplateParameter: |
395 return OS << "TemplateParameter"; | 744 return OS << "TemplateParameter"; |
396 case HighlightingKind::Concept: | 745 case HighlightingKind::Concept: |
402 case HighlightingKind::InactiveCode: | 751 case HighlightingKind::InactiveCode: |
403 return OS << "InactiveCode"; | 752 return OS << "InactiveCode"; |
404 } | 753 } |
405 llvm_unreachable("invalid HighlightingKind"); | 754 llvm_unreachable("invalid HighlightingKind"); |
406 } | 755 } |
407 | 756 llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingModifier K) { |
408 std::vector<LineHighlightings> | 757 switch (K) { |
409 diffHighlightings(ArrayRef<HighlightingToken> New, | 758 case HighlightingModifier::Declaration: |
410 ArrayRef<HighlightingToken> Old) { | 759 return OS << "decl"; // abbrevation for common case |
411 assert(std::is_sorted(New.begin(), New.end()) && | 760 default: |
412 "New must be a sorted vector"); | 761 return OS << toSemanticTokenModifier(K); |
413 assert(std::is_sorted(Old.begin(), Old.end()) && | 762 } |
414 "Old must be a sorted vector"); | |
415 | |
416 // FIXME: There's an edge case when tokens span multiple lines. If the first | |
417 // token on the line started on a line above the current one and the rest of | |
418 // the line is the equal to the previous one than we will remove all | |
419 // highlights but the ones for the token spanning multiple lines. This means | |
420 // that when we get into the LSP layer the only highlights that will be | |
421 // visible are the ones for the token spanning multiple lines. | |
422 // Example: | |
423 // EndOfMultilineToken Token Token Token | |
424 // If "Token Token Token" don't differ from previously the line is | |
425 // incorrectly removed. Suggestion to fix is to separate any multiline tokens | |
426 // into one token for every line it covers. This requires reading from the | |
427 // file buffer to figure out the length of each line though. | |
428 std::vector<LineHighlightings> DiffedLines; | |
429 // ArrayRefs to the current line in the highlightings. | |
430 ArrayRef<HighlightingToken> NewLine(New.begin(), | |
431 /*length*/ static_cast<size_t>(0)); | |
432 ArrayRef<HighlightingToken> OldLine(Old.begin(), | |
433 /*length*/ static_cast<size_t>(0)); | |
434 auto NewEnd = New.end(); | |
435 auto OldEnd = Old.end(); | |
436 auto NextLineNumber = [&]() { | |
437 int NextNew = NewLine.end() != NewEnd ? NewLine.end()->R.start.line | |
438 : std::numeric_limits<int>::max(); | |
439 int NextOld = OldLine.end() != OldEnd ? OldLine.end()->R.start.line | |
440 : std::numeric_limits<int>::max(); | |
441 return std::min(NextNew, NextOld); | |
442 }; | |
443 | |
444 for (int LineNumber = 0; NewLine.end() < NewEnd || OldLine.end() < OldEnd; | |
445 LineNumber = NextLineNumber()) { | |
446 NewLine = takeLine(New, NewLine.end(), LineNumber); | |
447 OldLine = takeLine(Old, OldLine.end(), LineNumber); | |
448 if (NewLine != OldLine) { | |
449 DiffedLines.push_back({LineNumber, NewLine, /*IsInactive=*/false}); | |
450 | |
451 // Turn a HighlightingKind::InactiveCode token into the IsInactive flag. | |
452 auto &AddedLine = DiffedLines.back(); | |
453 llvm::erase_if(AddedLine.Tokens, [&](const HighlightingToken &T) { | |
454 if (T.Kind == HighlightingKind::InactiveCode) { | |
455 AddedLine.IsInactive = true; | |
456 return true; | |
457 } | |
458 return false; | |
459 }); | |
460 } | |
461 } | |
462 | |
463 return DiffedLines; | |
464 } | 763 } |
465 | 764 |
466 bool operator==(const HighlightingToken &L, const HighlightingToken &R) { | 765 bool operator==(const HighlightingToken &L, const HighlightingToken &R) { |
467 return std::tie(L.R, L.Kind) == std::tie(R.R, R.Kind); | 766 return std::tie(L.R, L.Kind, L.Modifiers) == |
767 std::tie(R.R, R.Kind, R.Modifiers); | |
468 } | 768 } |
469 bool operator<(const HighlightingToken &L, const HighlightingToken &R) { | 769 bool operator<(const HighlightingToken &L, const HighlightingToken &R) { |
470 return std::tie(L.R, L.Kind) < std::tie(R.R, R.Kind); | 770 return std::tie(L.R, L.Kind, R.Modifiers) < |
471 } | 771 std::tie(R.R, R.Kind, R.Modifiers); |
472 bool operator==(const LineHighlightings &L, const LineHighlightings &R) { | |
473 return std::tie(L.Line, L.Tokens) == std::tie(R.Line, R.Tokens); | |
474 } | 772 } |
475 | 773 |
476 std::vector<SemanticToken> | 774 std::vector<SemanticToken> |
477 toSemanticTokens(llvm::ArrayRef<HighlightingToken> Tokens) { | 775 toSemanticTokens(llvm::ArrayRef<HighlightingToken> Tokens) { |
478 assert(std::is_sorted(Tokens.begin(), Tokens.end())); | 776 assert(std::is_sorted(Tokens.begin(), Tokens.end())); |
479 std::vector<SemanticToken> Result; | 777 std::vector<SemanticToken> Result; |
480 const HighlightingToken *Last = nullptr; | 778 const HighlightingToken *Last = nullptr; |
481 for (const HighlightingToken &Tok : Tokens) { | 779 for (const HighlightingToken &Tok : Tokens) { |
482 // FIXME: support inactive code - we need to provide the actual bounds. | |
483 if (Tok.Kind == HighlightingKind::InactiveCode) | |
484 continue; | |
485 Result.emplace_back(); | 780 Result.emplace_back(); |
486 SemanticToken &Out = Result.back(); | 781 SemanticToken &Out = Result.back(); |
487 // deltaStart/deltaLine are relative if possible. | 782 // deltaStart/deltaLine are relative if possible. |
488 if (Last) { | 783 if (Last) { |
489 assert(Tok.R.start.line >= Last->R.start.line); | 784 assert(Tok.R.start.line >= Last->R.start.line); |
499 Out.deltaStart = Tok.R.start.character; | 794 Out.deltaStart = Tok.R.start.character; |
500 } | 795 } |
501 assert(Tok.R.end.line == Tok.R.start.line); | 796 assert(Tok.R.end.line == Tok.R.start.line); |
502 Out.length = Tok.R.end.character - Tok.R.start.character; | 797 Out.length = Tok.R.end.character - Tok.R.start.character; |
503 Out.tokenType = static_cast<unsigned>(Tok.Kind); | 798 Out.tokenType = static_cast<unsigned>(Tok.Kind); |
799 Out.tokenModifiers = Tok.Modifiers; | |
504 | 800 |
505 Last = &Tok; | 801 Last = &Tok; |
506 } | 802 } |
507 return Result; | 803 return Result; |
508 } | 804 } |
515 case HighlightingKind::Parameter: | 811 case HighlightingKind::Parameter: |
516 return "parameter"; | 812 return "parameter"; |
517 case HighlightingKind::Function: | 813 case HighlightingKind::Function: |
518 return "function"; | 814 return "function"; |
519 case HighlightingKind::Method: | 815 case HighlightingKind::Method: |
520 return "member"; | 816 return "method"; |
521 case HighlightingKind::StaticMethod: | 817 case HighlightingKind::StaticMethod: |
522 // FIXME: better function/member with static modifier? | 818 // FIXME: better method with static modifier? |
523 return "function"; | 819 return "function"; |
524 case HighlightingKind::Field: | 820 case HighlightingKind::Field: |
525 return "member"; | 821 return "property"; |
526 case HighlightingKind::Class: | 822 case HighlightingKind::Class: |
527 return "class"; | 823 return "class"; |
824 case HighlightingKind::Interface: | |
825 return "interface"; | |
528 case HighlightingKind::Enum: | 826 case HighlightingKind::Enum: |
529 return "enum"; | 827 return "enum"; |
530 case HighlightingKind::EnumConstant: | 828 case HighlightingKind::EnumConstant: |
531 return "enumConstant"; // nonstandard | 829 return "enumMember"; |
532 case HighlightingKind::Typedef: | 830 case HighlightingKind::Typedef: |
831 case HighlightingKind::Type: | |
533 return "type"; | 832 return "type"; |
534 case HighlightingKind::DependentType: | 833 case HighlightingKind::Unknown: |
535 return "dependent"; // nonstandard | 834 return "unknown"; // nonstandard |
536 case HighlightingKind::DependentName: | |
537 return "dependent"; // nonstandard | |
538 case HighlightingKind::Namespace: | 835 case HighlightingKind::Namespace: |
539 return "namespace"; | 836 return "namespace"; |
540 case HighlightingKind::TemplateParameter: | 837 case HighlightingKind::TemplateParameter: |
541 return "typeParameter"; | 838 return "typeParameter"; |
542 case HighlightingKind::Concept: | 839 case HighlightingKind::Concept: |
549 return "comment"; | 846 return "comment"; |
550 } | 847 } |
551 llvm_unreachable("unhandled HighlightingKind"); | 848 llvm_unreachable("unhandled HighlightingKind"); |
552 } | 849 } |
553 | 850 |
554 std::vector<TheiaSemanticHighlightingInformation> | 851 llvm::StringRef toSemanticTokenModifier(HighlightingModifier Modifier) { |
555 toTheiaSemanticHighlightingInformation( | 852 switch (Modifier) { |
556 llvm::ArrayRef<LineHighlightings> Tokens) { | 853 case HighlightingModifier::Declaration: |
557 if (Tokens.size() == 0) | 854 return "declaration"; |
558 return {}; | 855 case HighlightingModifier::Deprecated: |
559 | 856 return "deprecated"; |
560 // FIXME: Tokens might be multiple lines long (block comments) in this case | 857 case HighlightingModifier::Readonly: |
561 // this needs to add multiple lines for those tokens. | 858 return "readonly"; |
562 std::vector<TheiaSemanticHighlightingInformation> Lines; | 859 case HighlightingModifier::Static: |
563 Lines.reserve(Tokens.size()); | 860 return "static"; |
564 for (const auto &Line : Tokens) { | 861 case HighlightingModifier::Deduced: |
565 llvm::SmallVector<char, 128> LineByteTokens; | 862 return "deduced"; // nonstandard |
566 llvm::raw_svector_ostream OS(LineByteTokens); | 863 case HighlightingModifier::Abstract: |
567 for (const auto &Token : Line.Tokens) { | 864 return "abstract"; |
568 // Writes the token to LineByteTokens in the byte format specified by the | 865 case HighlightingModifier::DependentName: |
569 // LSP proposal. Described below. | 866 return "dependentName"; // nonstandard |
570 // |<---- 4 bytes ---->|<-- 2 bytes -->|<--- 2 bytes -->| | 867 case HighlightingModifier::DefaultLibrary: |
571 // | character | length | index | | 868 return "defaultLibrary"; |
572 | 869 case HighlightingModifier::FunctionScope: |
573 write32be(Token.R.start.character, OS); | 870 return "functionScope"; // nonstandard |
574 write16be(Token.R.end.character - Token.R.start.character, OS); | 871 case HighlightingModifier::ClassScope: |
575 write16be(static_cast<int>(Token.Kind), OS); | 872 return "classScope"; // nonstandard |
576 } | 873 case HighlightingModifier::FileScope: |
577 | 874 return "fileScope"; // nonstandard |
578 Lines.push_back({Line.Line, encodeBase64(LineByteTokens), Line.IsInactive}); | 875 case HighlightingModifier::GlobalScope: |
579 } | 876 return "globalScope"; // nonstandard |
580 | 877 } |
581 return Lines; | 878 llvm_unreachable("unhandled HighlightingModifier"); |
582 } | |
583 | |
584 llvm::StringRef toTextMateScope(HighlightingKind Kind) { | |
585 // FIXME: Add scopes for C and Objective C. | |
586 switch (Kind) { | |
587 case HighlightingKind::Function: | |
588 return "entity.name.function.cpp"; | |
589 case HighlightingKind::Method: | |
590 return "entity.name.function.method.cpp"; | |
591 case HighlightingKind::StaticMethod: | |
592 return "entity.name.function.method.static.cpp"; | |
593 case HighlightingKind::Variable: | |
594 return "variable.other.cpp"; | |
595 case HighlightingKind::LocalVariable: | |
596 return "variable.other.local.cpp"; | |
597 case HighlightingKind::Parameter: | |
598 return "variable.parameter.cpp"; | |
599 case HighlightingKind::Field: | |
600 return "variable.other.field.cpp"; | |
601 case HighlightingKind::StaticField: | |
602 return "variable.other.field.static.cpp"; | |
603 case HighlightingKind::Class: | |
604 return "entity.name.type.class.cpp"; | |
605 case HighlightingKind::Enum: | |
606 return "entity.name.type.enum.cpp"; | |
607 case HighlightingKind::EnumConstant: | |
608 return "variable.other.enummember.cpp"; | |
609 case HighlightingKind::Typedef: | |
610 return "entity.name.type.typedef.cpp"; | |
611 case HighlightingKind::DependentType: | |
612 return "entity.name.type.dependent.cpp"; | |
613 case HighlightingKind::DependentName: | |
614 return "entity.name.other.dependent.cpp"; | |
615 case HighlightingKind::Namespace: | |
616 return "entity.name.namespace.cpp"; | |
617 case HighlightingKind::TemplateParameter: | |
618 return "entity.name.type.template.cpp"; | |
619 case HighlightingKind::Concept: | |
620 return "entity.name.type.concept.cpp"; | |
621 case HighlightingKind::Primitive: | |
622 return "storage.type.primitive.cpp"; | |
623 case HighlightingKind::Macro: | |
624 return "entity.name.function.preprocessor.cpp"; | |
625 case HighlightingKind::InactiveCode: | |
626 return "meta.disabled"; | |
627 } | |
628 llvm_unreachable("unhandled HighlightingKind"); | |
629 } | 879 } |
630 | 880 |
631 std::vector<SemanticTokensEdit> | 881 std::vector<SemanticTokensEdit> |
632 diffTokens(llvm::ArrayRef<SemanticToken> Old, | 882 diffTokens(llvm::ArrayRef<SemanticToken> Old, |
633 llvm::ArrayRef<SemanticToken> New) { | 883 llvm::ArrayRef<SemanticToken> New) { |