//===--- SemanticHighlighting.cpp - ------------------------- ---*- C++ -*-===// // // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. // See https://llvm.org/LICENSE.txt for license information. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // //===----------------------------------------------------------------------===// #include "SemanticHighlighting.h" #include "Config.h" #include "FindTarget.h" #include "HeuristicResolver.h" #include "ParsedAST.h" #include "Protocol.h" #include "SourceCode.h" #include "support/Logger.h" #include "clang/AST/ASTContext.h" #include "clang/AST/Decl.h" #include "clang/AST/DeclCXX.h" #include "clang/AST/DeclObjC.h" #include "clang/AST/DeclTemplate.h" #include "clang/AST/DeclarationName.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/RecursiveASTVisitor.h" #include "clang/AST/Type.h" #include "clang/AST/TypeLoc.h" #include "clang/Basic/LangOptions.h" #include "clang/Basic/SourceLocation.h" #include "clang/Basic/SourceManager.h" #include "clang/Tooling/Syntax/Tokens.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/StringRef.h" #include "llvm/Support/Casting.h" #include "llvm/Support/Error.h" #include <algorithm> #include <optional> namespace clang { namespace clangd { namespace { /// Get the last Position on a given line. llvm::Expected<Position> endOfLine(llvm::StringRef Code, int Line) { … } /// Some names are not written in the source code and cannot be highlighted, /// e.g. anonymous classes. This function detects those cases. bool canHighlightName(DeclarationName Name) { … } bool isUniqueDefinition(const NamedDecl *Decl) { … } std::optional<HighlightingKind> kindForType(const Type *TP, const HeuristicResolver *Resolver); std::optional<HighlightingKind> kindForDecl(const NamedDecl *D, const HeuristicResolver *Resolver) { … } std::optional<HighlightingKind> kindForType(const Type *TP, const HeuristicResolver *Resolver) { … } // Whether T is const in a loose sense - is a variable with this type readonly? bool isConst(QualType T) { … } // Whether D is const in a loose sense (should it be highlighted as such?) // FIXME: This is separate from whether *a particular usage* can mutate D. // We may want V in V.size() to be readonly even if V is mutable. bool isConst(const Decl *D) { … } // "Static" means many things in C++, only some get the "static" modifier. // // Meanings that do: // - Members associated with the class rather than the instance. // This is what 'static' most often means across languages. // - static local variables // These are similarly "detached from their context" by the static keyword. // In practice, these are rarely used inside classes, reducing confusion. // // Meanings that don't: // - Namespace-scoped variables, which have static storage class. // This is implicit, so the keyword "static" isn't so strongly associated. // If we want a modifier for these, "global scope" is probably the concept. // - Namespace-scoped variables/functions explicitly marked "static". // There the keyword changes *linkage* , which is a totally different concept. // If we want to model this, "file scope" would be a nice modifier. // // This is confusing, and maybe we should use another name, but because "static" // is a standard LSP modifier, having one with that name has advantages. bool isStatic(const Decl *D) { … } bool isAbstract(const Decl *D) { … } bool isVirtual(const Decl *D) { … } bool isDependent(const Decl *D) { … } /// Returns true if `Decl` is considered to be from a default/system library. /// This currently checks the systemness of the file by include type, although /// different heuristics may be used in the future (e.g. sysroot paths). bool isDefaultLibrary(const Decl *D) { … } bool isDefaultLibrary(const Type *T) { … } // For a macro usage `DUMP(foo)`, we want: // - DUMP --> "macro" // - foo --> "variable". SourceLocation getHighlightableSpellingToken(SourceLocation L, const SourceManager &SM) { … } unsigned evaluateHighlightPriority(const HighlightingToken &Tok) { … } // Sometimes we get multiple tokens at the same location: // // - findExplicitReferences() returns a heuristic result for a dependent name // (e.g. Method) and CollectExtraHighlighting returning a fallback dependent // highlighting (e.g. Unknown+Dependent). // - macro arguments are expanded multiple times and have different roles // - broken code recovery produces several AST nodes at the same location // // We should either resolve these to a single token, or drop them all. // Our heuristics are: // // - token kinds that come with "dependent-name" modifiers are less reliable // (these tend to be vague, like Type or Unknown) // - if we have multiple equally reliable kinds, drop token rather than guess // - take the union of modifiers from all tokens // // In particular, heuristically resolved dependent names get their heuristic // kind, plus the dependent modifier. std::optional<HighlightingToken> resolveConflict(const HighlightingToken &A, const HighlightingToken &B) { … } std::optional<HighlightingToken> resolveConflict(ArrayRef<HighlightingToken> Tokens) { … } /// Filter to remove particular kinds of highlighting tokens and modifiers from /// the output. class HighlightingFilter { … }; /// Consumes source locations and maps them to text ranges for highlightings. class HighlightingsBuilder { … }; std::optional<HighlightingModifier> scopeModifier(const NamedDecl *D) { … } std::optional<HighlightingModifier> scopeModifier(const Type *T) { … } /// Produces highlightings, which are not captured by findExplicitReferences, /// e.g. highlights dependent names and 'auto' as the underlying type. class CollectExtraHighlightings : public RecursiveASTVisitor<CollectExtraHighlightings> { … }; } // namespace std::vector<HighlightingToken> getSemanticHighlightings(ParsedAST &AST, bool IncludeInactiveRegionTokens) { … } llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingKind K) { … } std::optional<HighlightingKind> highlightingKindFromString(llvm::StringRef Name) { … } llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingModifier K) { … } std::optional<HighlightingModifier> highlightingModifierFromString(llvm::StringRef Name) { … } bool operator==(const HighlightingToken &L, const HighlightingToken &R) { … } bool operator<(const HighlightingToken &L, const HighlightingToken &R) { … } std::vector<SemanticToken> toSemanticTokens(llvm::ArrayRef<HighlightingToken> Tokens, llvm::StringRef Code) { … } llvm::StringRef toSemanticTokenType(HighlightingKind Kind) { … } llvm::StringRef toSemanticTokenModifier(HighlightingModifier Modifier) { … } std::vector<SemanticTokensEdit> diffTokens(llvm::ArrayRef<SemanticToken> Old, llvm::ArrayRef<SemanticToken> New) { … } std::vector<Range> getInactiveRegions(ParsedAST &AST) { … } } // namespace clangd } // namespace clang