https://github.com/chouzz updated https://github.com/llvm/llvm-project/pull/113669
>From 02124e4cfd7dbc395d4974c7561d5f110980aaa5 Mon Sep 17 00:00:00 2001 From: chouzz <zhouhua...@outlook.com> Date: Fri, 25 Oct 2024 17:42:04 +0800 Subject: [PATCH] [clangd] Support symbolTags for document symbol --- clang-tools-extra/clangd/AST.cpp | 63 ++++++++++++++ clang-tools-extra/clangd/AST.h | 31 +++++++ clang-tools-extra/clangd/FindSymbols.cpp | 30 +++++++ clang-tools-extra/clangd/Protocol.h | 29 ++++++- .../clangd/SemanticHighlighting.cpp | 85 ------------------- 5 files changed, 151 insertions(+), 87 deletions(-) diff --git a/clang-tools-extra/clangd/AST.cpp b/clang-tools-extra/clangd/AST.cpp index f3eee1c6335f98..52578699b159b8 100644 --- a/clang-tools-extra/clangd/AST.cpp +++ b/clang-tools-extra/clangd/AST.cpp @@ -169,6 +169,69 @@ bool isImplementationDetail(const Decl *D) { D->getASTContext().getSourceManager()); } +// Whether T is const in a loose sense - is a variable with this type readonly? +bool isConst(QualType T) { + if (T.isNull()) + return false; + T = T.getNonReferenceType(); + if (T.isConstQualified()) + return true; + if (const auto *AT = T->getAsArrayTypeUnsafe()) + return isConst(AT->getElementType()); + if (isConst(T->getPointeeType())) + return true; + return false; +} + +bool isConst(const Decl *D) { + if (llvm::isa<EnumConstantDecl>(D) || llvm::isa<NonTypeTemplateParmDecl>(D)) + return true; + if (llvm::isa<FieldDecl>(D) || llvm::isa<VarDecl>(D) || + llvm::isa<MSPropertyDecl>(D) || llvm::isa<BindingDecl>(D)) { + if (isConst(llvm::cast<ValueDecl>(D)->getType())) + return true; + } + if (const auto *OCPD = llvm::dyn_cast<ObjCPropertyDecl>(D)) { + if (OCPD->isReadOnly()) + return true; + } + if (const auto *MPD = llvm::dyn_cast<MSPropertyDecl>(D)) { + if (!MPD->hasSetter()) + return true; + } + if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) { + if (CMD->isConst()) + return true; + } + return false; +} + +bool isStatic(const Decl *D) { + if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) + return CMD->isStatic(); + if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(D)) + return VD->isStaticDataMember() || VD->isStaticLocal(); + if (const auto *OPD = llvm::dyn_cast<ObjCPropertyDecl>(D)) + return OPD->isClassProperty(); + if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(D)) + return OMD->isClassMethod(); + return false; +} + +bool isAbstract(const Decl *D) { + if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) + return CMD->isPureVirtual(); + if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(D)) + return CRD->hasDefinition() && CRD->isAbstract(); + return false; +} + +bool isVirtual(const Decl *D) { + if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) + return CMD->isVirtual(); + return false; +} + SourceLocation nameLocation(const clang::Decl &D, const SourceManager &SM) { auto L = D.getLocation(); // For `- (void)foo` we want `foo` not the `-`. diff --git a/clang-tools-extra/clangd/AST.h b/clang-tools-extra/clangd/AST.h index fb0722d697cd06..b7eb7ddedc1666 100644 --- a/clang-tools-extra/clangd/AST.h +++ b/clang-tools-extra/clangd/AST.h @@ -152,6 +152,37 @@ bool isImplicitTemplateInstantiation(const NamedDecl *D); /// explicit specialization. bool isExplicitTemplateSpecialization(const NamedDecl *D); +// 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); + /// Returns a nested name specifier loc of \p ND if it was present in the /// source, e.g. /// void ns::something::foo() -> returns 'ns::something' diff --git a/clang-tools-extra/clangd/FindSymbols.cpp b/clang-tools-extra/clangd/FindSymbols.cpp index 84bcbc1f2ddd3f..adb9b63fdca7e3 100644 --- a/clang-tools-extra/clangd/FindSymbols.cpp +++ b/clang-tools-extra/clangd/FindSymbols.cpp @@ -187,6 +187,35 @@ std::string getSymbolName(ASTContext &Ctx, const NamedDecl &ND) { return printName(Ctx, ND); } +std::vector<SymbolTag> getSymbolTags(const NamedDecl &ND) +{ + std::vector<SymbolTag> Tags; + if (ND.isDeprecated()) + Tags.push_back(SymbolTag::Deprecated); + if (isConst(&ND)) + Tags.push_back(SymbolTag::Constant); + if (isStatic(&ND)) + Tags.push_back(SymbolTag::Static); + if (const FieldDecl *FD = dyn_cast<FieldDecl>(&ND)) { + switch (FD->getAccess()) { + case AS_public: + Tags.push_back(SymbolTag::Public); + break; + case AS_protected: + Tags.push_back(SymbolTag::Protected); + break; + case AS_private: + Tags.push_back(SymbolTag::Private); + break; + default: + break; + } + } + if (isVirtual(&ND)) + Tags.push_back(SymbolTag::Virtual); + return Tags; +} + std::string getSymbolDetail(ASTContext &Ctx, const NamedDecl &ND) { PrintingPolicy P(Ctx.getPrintingPolicy()); P.SuppressScope = true; @@ -241,6 +270,7 @@ std::optional<DocumentSymbol> declToSym(ASTContext &Ctx, const NamedDecl &ND) { SI.range = Range{sourceLocToPosition(SM, SymbolRange->getBegin()), sourceLocToPosition(SM, SymbolRange->getEnd())}; SI.detail = getSymbolDetail(Ctx, ND); + SI.tags = getSymbolTags(ND); SourceLocation NameLoc = ND.getLocation(); SourceLocation FallbackNameLoc; diff --git a/clang-tools-extra/clangd/Protocol.h b/clang-tools-extra/clangd/Protocol.h index 5b28095758198d..48d8c81d4ad0ce 100644 --- a/clang-tools-extra/clangd/Protocol.h +++ b/clang-tools-extra/clangd/Protocol.h @@ -1090,6 +1090,30 @@ struct CodeAction { }; llvm::json::Value toJSON(const CodeAction &); +enum class SymbolTag { + Deprecated = 1 , + Private = 2, + Package = 3, + Protected = 4, + Public = 5, + Internal= 6, + File = 7, + Static = 8, + Abstract = 9, + Final = 10, + Sealed = 11, + Constant = 12, + Transient = 13, + Volatile = 14, + Synchronized = 15, + Virtual = 16, + Nullable = 17, + NonNull = 18, + Declaration = 19, + Definition = 20, + ReadOnly = 21, +}; +llvm::json::Value toJSON(SymbolTag); /// Represents programming constructs like variables, classes, interfaces etc. /// that appear in a document. Document symbols can be hierarchical and they /// have two ranges: one that encloses its definition and one that points to its @@ -1107,6 +1131,9 @@ struct DocumentSymbol { /// Indicates if this symbol is deprecated. bool deprecated = false; + /// Tags for this symbol, e.g public, private, static, const etc. + std::vector<SymbolTag> tags; + /// The range enclosing this symbol not including leading/trailing whitespace /// but everything else like comments. This information is typically used to /// determine if the clients cursor is inside the symbol to reveal in the @@ -1558,8 +1585,6 @@ struct ResolveTypeHierarchyItemParams { bool fromJSON(const llvm::json::Value &, ResolveTypeHierarchyItemParams &, llvm::json::Path); -enum class SymbolTag { Deprecated = 1 }; -llvm::json::Value toJSON(SymbolTag); /// The parameter of a `textDocument/prepareCallHierarchy` request. struct CallHierarchyPrepareParams : public TextDocumentPositionParams {}; diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp index e6d16af2495fec..035bd75c59bb59 100644 --- a/clang-tools-extra/clangd/SemanticHighlighting.cpp +++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp @@ -192,91 +192,6 @@ std::optional<HighlightingKind> kindForType(const Type *TP, return std::nullopt; } -// Whether T is const in a loose sense - is a variable with this type readonly? -bool isConst(QualType T) { - if (T.isNull()) - return false; - T = T.getNonReferenceType(); - if (T.isConstQualified()) - return true; - if (const auto *AT = T->getAsArrayTypeUnsafe()) - return isConst(AT->getElementType()); - if (isConst(T->getPointeeType())) - return true; - return false; -} - -// 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) { - if (llvm::isa<EnumConstantDecl>(D) || llvm::isa<NonTypeTemplateParmDecl>(D)) - return true; - if (llvm::isa<FieldDecl>(D) || llvm::isa<VarDecl>(D) || - llvm::isa<MSPropertyDecl>(D) || llvm::isa<BindingDecl>(D)) { - if (isConst(llvm::cast<ValueDecl>(D)->getType())) - return true; - } - if (const auto *OCPD = llvm::dyn_cast<ObjCPropertyDecl>(D)) { - if (OCPD->isReadOnly()) - return true; - } - if (const auto *MPD = llvm::dyn_cast<MSPropertyDecl>(D)) { - if (!MPD->hasSetter()) - return true; - } - if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) { - if (CMD->isConst()) - return true; - } - return false; -} - -// "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) { - if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) - return CMD->isStatic(); - if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(D)) - return VD->isStaticDataMember() || VD->isStaticLocal(); - if (const auto *OPD = llvm::dyn_cast<ObjCPropertyDecl>(D)) - return OPD->isClassProperty(); - if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(D)) - return OMD->isClassMethod(); - return false; -} - -bool isAbstract(const Decl *D) { - if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) - return CMD->isPureVirtual(); - if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(D)) - return CRD->hasDefinition() && CRD->isAbstract(); - return false; -} - -bool isVirtual(const Decl *D) { - if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) - return CMD->isVirtual(); - return false; -} - bool isDependent(const Decl *D) { if (isa<UnresolvedUsingValueDecl>(D)) return true; _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits