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