diff --git a/clang-tools-extra/clangd/ClangdLSPServer.cpp b/clang-tools-extra/clangd/ClangdLSPServer.cpp --- a/clang-tools-extra/clangd/ClangdLSPServer.cpp +++ b/clang-tools-extra/clangd/ClangdLSPServer.cpp @@ -466,6 +466,15 @@ return Types; } +static std::vector semanticTokenModifiers() { + std::vector Modifiers; + for (unsigned I = 0; + I <= static_cast(HighlightingModifier::LastModifier); ++I) + Modifiers.push_back( + toSemanticTokenModifier(static_cast(I))); + return Modifiers; +} + void ClangdLSPServer::onInitialize(const InitializeParams &Params, Callback Reply) { // Determine character encoding first as it affects constructed ClangdServer. @@ -582,8 +591,9 @@ {"documentProvider", true}, {"rangeProvider", false}, {"legend", - llvm::json::Object{{"tokenTypes", semanticTokenTypes()}, - {"tokenModifiers", llvm::json::Array()}}}, + llvm::json::Object{ + {"tokenTypes", semanticTokenTypes()}, + {"tokenModifiers", semanticTokenModifiers()}}}, }}, {"signatureHelpProvider", llvm::json::Object{ diff --git a/clang-tools-extra/clangd/SemanticHighlighting.h b/clang-tools-extra/clangd/SemanticHighlighting.h --- a/clang-tools-extra/clangd/SemanticHighlighting.h +++ b/clang-tools-extra/clangd/SemanticHighlighting.h @@ -64,12 +64,33 @@ LastKind = InactiveCode }; + llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingKind K); +enum class HighlightingModifier { + Declaration, + Definition, + Deprecated, + Deduced, + Readonly, + StaticMember, + + LastModifier = Deprecated +}; +static_assert(static_cast(HighlightingModifier::LastModifier) < 32, + "Increase width of modifiers bitfield!"); +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingModifier K); + // Contains all information needed for the highlighting a token. struct HighlightingToken { HighlightingKind Kind; + uint32_t Modifiers; Range R; + + HighlightingToken &addModifier(HighlightingModifier M) { + Modifiers |= 1 << static_cast(M); + return *this; + } }; bool operator==(const HighlightingToken &L, const HighlightingToken &R); @@ -90,6 +111,7 @@ std::vector toSemanticTokens(llvm::ArrayRef); llvm::StringRef toSemanticTokenType(HighlightingKind Kind); +llvm::StringRef toSemanticTokenModifier(HighlightingModifier Modifier); /// Converts a HighlightingKind to a corresponding TextMate scope /// (https://manual.macromates.com/en/language_grammars). diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp --- a/clang-tools-extra/clangd/SemanticHighlighting.cpp +++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -15,6 +15,8 @@ #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" @@ -116,17 +118,53 @@ return llvm::None; } -llvm::Optional kindForReference(const ReferenceLoc &R) { - llvm::Optional Result; - for (const NamedDecl *Decl : R.Targets) { - if (!canHighlightName(Decl->getDeclName())) - return llvm::None; - auto Kind = kindForDecl(Decl); - if (!Kind || (Result && Kind != Result)) - return llvm::None; - Result = Kind; +// Whether T is const in a loose sense - is a variable with this type readonly? +bool isConst(QualType T) { + if (T.isNull() || T->isDependentType()) + return false; + T = T.getNonReferenceType(); + if (T.isConstQualified()) + return true; + if (const auto *AT = T->getAsArrayTypeUnsafe()) + return isConst(AT->getElementType()); + return isConst(T->getPointeeType()); +} + +// Whether D is const in a loose sense (should it be highlighted as such?) +bool isConst(const Decl* D) { + if (llvm::isa(D) || llvm::isa(D)) + return true; + if (llvm::isa(D) || llvm::isa(D) || + llvm::isa(D) || llvm::isa(D)) { + if (isConst(llvm::cast(D)->getType())) + return true; } - return Result; + if (const auto *OCPD = llvm::dyn_cast(D)) { + if (OCPD->isReadOnly()) + return true; + } + if (const auto *MPD = llvm::dyn_cast(D)) { + if (!MPD->hasSetter()) + return true; + } + if (const auto *CMD = llvm::dyn_cast(D)) { + if (CMD->isConst()) + return true; + } + return false; + +} + +bool isStaticMember(const Decl *D) { + if (const auto *CMD = llvm::dyn_cast(D)) + return CMD->isStatic(); + if (const VarDecl *VD = llvm::dyn_cast(D)) + return VD->isStaticDataMember(); + if (const auto *FD = llvm::dyn_cast(D)) + return FD->isCXXInstanceMember(); + if (const auto *OMD = llvm::dyn_cast(D)) + return OMD->isClassMethod(); + return false; } // For a macro usage `DUMP(foo)`, we want: @@ -150,18 +188,24 @@ : TB(AST.getTokens()), SourceMgr(AST.getSourceManager()), LangOpts(AST.getLangOpts()) {} - void addToken(HighlightingToken T) { Tokens.push_back(T); } - - void addToken(SourceLocation Loc, HighlightingKind Kind) { + HighlightingToken &addToken(SourceLocation Loc, HighlightingKind Kind) { Loc = getHighlightableSpellingToken(Loc, SourceMgr); if (Loc.isInvalid()) - return; + return Dummy; const auto *Tok = TB.spelledTokenAt(Loc); assert(Tok); + return addToken( + halfOpenToRange(SourceMgr, + Tok->range(SourceMgr).toCharRange(SourceMgr)), + Kind); + } - auto Range = halfOpenToRange(SourceMgr, - Tok->range(SourceMgr).toCharRange(SourceMgr)); - Tokens.push_back(HighlightingToken{Kind, std::move(Range)}); + HighlightingToken &addToken(Range R, HighlightingKind Kind) { + HighlightingToken HT; + HT.R = std::move(R); + HT.Kind = Kind; + Tokens.push_back(std::move(HT)); + return Tokens.back(); } std::vector collect(ParsedAST &AST) && { @@ -201,8 +245,11 @@ // zero. The client will treat this highlighting kind specially, and // highlight the entire line visually (i.e. not just to where the text // on the line ends, but to the end of the screen). - NonConflicting.push_back({HighlightingKind::InactiveCode, - {Position{Line, 0}, Position{Line, 0}}}); + HighlightingToken HT; + NonConflicting.emplace_back(); + NonConflicting.back().Kind = HighlightingKind::InactiveCode; + NonConflicting.back().R.start.line = Line; + NonConflicting.back().R.end = NonConflicting.back().R.start; } } // Re-sort the tokens because that's what the diffing expects. @@ -215,6 +262,7 @@ const SourceManager &SourceMgr; const LangOptions &LangOpts; std::vector Tokens; + HighlightingToken Dummy; // returned from addToken(InvalidLoc) }; /// Produces highlightings, which are not captured by findExplicitReferences, @@ -226,7 +274,8 @@ bool VisitDecltypeTypeLoc(DecltypeTypeLoc L) { if (auto K = kindForType(L.getTypePtr())) - H.addToken(L.getBeginLoc(), *K); + H.addToken(L.getBeginLoc(), *K) + .addModifier(HighlightingModifier::Deduced); return true; } @@ -235,7 +284,8 @@ if (!AT) return true; if (auto K = kindForType(AT->getDeducedType().getTypePtrOrNull())) - H.addToken(D->getTypeSpecStartLoc(), *K); + H.addToken(D->getTypeSpecStartLoc(), *K) + .addModifier(HighlightingModifier::Deduced); return true; } @@ -317,16 +367,34 @@ CollectExtraHighlightings(Builder).TraverseAST(C); // Highlight all decls and references coming from the AST. findExplicitReferences(C, [&](ReferenceLoc R) { - if (auto Kind = kindForReference(R)) - Builder.addToken(R.NameLoc, *Kind); + llvm::Optional Result; + for (const NamedDecl *Decl : R.Targets) { + if (!canHighlightName(Decl->getDeclName())) + continue; + auto Kind = kindForDecl(Decl); + if (!Kind || (Result && Kind != Result)) + continue; + Result = Kind; + + auto &Tok = Builder.addToken(R.NameLoc, *Kind); + if (isConst(Decl)) + Tok.addModifier(HighlightingModifier::Readonly); + if (isStaticMember(Decl)) + Tok.addModifier(HighlightingModifier::StaticMember); + if (Decl->isDeprecated()) + Tok.addModifier(HighlightingModifier::Deprecated); + if (R.IsDecl) // FIXME: also definition? + Tok.addModifier(HighlightingModifier::Declaration); + } }); // Add highlightings for macro references. for (const auto &SIDToRefs : AST.getMacros().MacroRefs) { for (const auto &M : SIDToRefs.second) - Builder.addToken({HighlightingKind::Macro, M}); + Builder.addToken(M, HighlightingKind::Macro); } for (const auto &M : AST.getMacros().UnknownMacros) - Builder.addToken({HighlightingKind::Macro, M}); + Builder.addToken(M, HighlightingKind::Macro) + .addModifier(HighlightingModifier::Definition); return std::move(Builder).collect(AST); } @@ -376,6 +444,9 @@ } llvm_unreachable("invalid HighlightingKind"); } +llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, HighlightingModifier K) { + return OS << toSemanticTokenModifier(K); +} std::vector diffHighlightings(ArrayRef New, @@ -436,10 +507,12 @@ } bool operator==(const HighlightingToken &L, const HighlightingToken &R) { - return std::tie(L.R, L.Kind) == std::tie(R.R, R.Kind); + return std::tie(L.R, L.Kind, L.Modifiers) == + std::tie(R.R, R.Kind, R.Modifiers); } bool operator<(const HighlightingToken &L, const HighlightingToken &R) { - return std::tie(L.R, L.Kind) < std::tie(R.R, R.Kind); + return std::tie(L.R, L.Kind, R.Modifiers) < + std::tie(R.R, R.Kind, R.Modifiers); } bool operator==(const LineHighlightings &L, const LineHighlightings &R) { return std::tie(L.Line, L.Tokens) == std::tie(R.Line, R.Tokens); @@ -473,6 +546,7 @@ assert(Tok.R.end.line == Tok.R.start.line); Out.length = Tok.R.end.character - Tok.R.start.character; Out.tokenType = static_cast(Tok.Kind); + Out.tokenModifiers = Tok.Modifiers; Last = &Tok; } @@ -482,19 +556,17 @@ switch (Kind) { case HighlightingKind::Variable: case HighlightingKind::LocalVariable: - case HighlightingKind::StaticField: return "variable"; + case HighlightingKind::Field: + case HighlightingKind::StaticField: + return "member"; + case HighlightingKind::Method: + case HighlightingKind::StaticMethod: + return "member"; case HighlightingKind::Parameter: return "parameter"; case HighlightingKind::Function: return "function"; - case HighlightingKind::Method: - return "member"; - case HighlightingKind::StaticMethod: - // FIXME: better function/member with static modifier? - return "function"; - case HighlightingKind::Field: - return "member"; case HighlightingKind::Class: return "class"; case HighlightingKind::Enum: @@ -504,7 +576,6 @@ case HighlightingKind::Typedef: return "type"; case HighlightingKind::DependentType: - return "dependent"; // nonstandard case HighlightingKind::DependentName: return "dependent"; // nonstandard case HighlightingKind::Namespace: @@ -523,6 +594,23 @@ llvm_unreachable("unhandled HighlightingKind"); } +llvm::StringRef toSemanticTokenModifier(HighlightingModifier Modifier) { + switch (Modifier) { + case HighlightingModifier::Declaration: + return "declaration"; + case HighlightingModifier::Definition: + return "definition"; + case HighlightingModifier::Deprecated: + return "deprecated"; + case HighlightingModifier::Readonly: + return "readonly"; + case HighlightingModifier::StaticMember: + return "static"; + case HighlightingModifier::Deduced: + return "deduced"; // nonstandard + } +} + std::vector toTheiaSemanticHighlightingInformation( llvm::ArrayRef Tokens) { diff --git a/clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp b/clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp --- a/clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp +++ b/clang-tools-extra/clangd/refactor/tweaks/AnnotateHighlightings.cpp @@ -8,6 +8,7 @@ #include "SemanticHighlighting.h" #include "refactor/Tweak.h" #include "llvm/ADT/StringRef.h" +#include "llvm/Support/ScopedPrinter.h" namespace clang { namespace clangd { @@ -65,9 +66,19 @@ if (!InsertOffset) return InsertOffset.takeError(); - auto InsertReplacement = tooling::Replacement( - FilePath, *InsertOffset, 0, - ("/* " + toTextMateScope(Token.Kind) + " */").str()); + std::string Comment = "/* "; + Comment.append(llvm::to_string(Token.Kind)); + for (unsigned I = 0; + I <= static_cast(HighlightingModifier::LastModifier); ++I) { + if (Token.Modifiers & (1 <(I))); + Comment.push_back(']'); + } + } + Comment.append(" */"); + auto InsertReplacement = + tooling::Replacement(FilePath, *InsertOffset, 0, Comment); if (auto Err = Result.add(InsertReplacement)) return std::move(Err); }