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