[clang-tools-extra] [clangd] Support symbolTags for document symbol (PR #113669)
via cfe-commits
cfe-commits at lists.llvm.org
Mon Oct 28 05:14:35 PDT 2024
https://github.com/chouzz updated https://github.com/llvm/llvm-project/pull/113669
>From 02124e4cfd7dbc395d4974c7561d5f110980aaa5 Mon Sep 17 00:00:00 2001
From: chouzz <zhouhua258 at outlook.com>
Date: Fri, 25 Oct 2024 17:42:04 +0800
Subject: [PATCH 1/2] [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;
>From 5b77f732f6de70bc7b5eaee459ef600843cf5f63 Mon Sep 17 00:00:00 2001
From: chouzz <zhouhua258 at outlook.com>
Date: Mon, 28 Oct 2024 20:14:15 +0800
Subject: [PATCH 2/2] Fix access for class method
---
clang-tools-extra/clangd/FindSymbols.cpp | 6 ++++--
clang-tools-extra/clangd/Protocol.cpp | 2 ++
clang-tools-extra/clangd/SemanticHighlighting.cpp | 1 +
3 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/clang-tools-extra/clangd/FindSymbols.cpp b/clang-tools-extra/clangd/FindSymbols.cpp
index adb9b63fdca7e3..3a782506d3b54e 100644
--- a/clang-tools-extra/clangd/FindSymbols.cpp
+++ b/clang-tools-extra/clangd/FindSymbols.cpp
@@ -196,6 +196,9 @@ std::vector<SymbolTag> getSymbolTags(const NamedDecl &ND)
Tags.push_back(SymbolTag::Constant);
if (isStatic(&ND))
Tags.push_back(SymbolTag::Static);
+ if (isVirtual(&ND))
+ Tags.push_back(SymbolTag::Virtual);
+
if (const FieldDecl *FD = dyn_cast<FieldDecl>(&ND)) {
switch (FD->getAccess()) {
case AS_public:
@@ -211,8 +214,7 @@ std::vector<SymbolTag> getSymbolTags(const NamedDecl &ND)
break;
}
}
- if (isVirtual(&ND))
- Tags.push_back(SymbolTag::Virtual);
+
return Tags;
}
diff --git a/clang-tools-extra/clangd/Protocol.cpp b/clang-tools-extra/clangd/Protocol.cpp
index 295ccd26a40454..8805c05ae4d13d 100644
--- a/clang-tools-extra/clangd/Protocol.cpp
+++ b/clang-tools-extra/clangd/Protocol.cpp
@@ -901,6 +901,8 @@ llvm::json::Value toJSON(const DocumentSymbol &S) {
Result["children"] = S.children;
if (S.deprecated)
Result["deprecated"] = true;
+ if (!S.tags.empty())
+ Result["tags"] = S.tags;
// FIXME: workaround for older gcc/clang
return std::move(Result);
}
diff --git a/clang-tools-extra/clangd/SemanticHighlighting.cpp b/clang-tools-extra/clangd/SemanticHighlighting.cpp
index 035bd75c59bb59..10b96589c350c8 100644
--- a/clang-tools-extra/clangd/SemanticHighlighting.cpp
+++ b/clang-tools-extra/clangd/SemanticHighlighting.cpp
@@ -7,6 +7,7 @@
//===----------------------------------------------------------------------===//
#include "SemanticHighlighting.h"
+#include "AST.h"
#include "Config.h"
#include "FindTarget.h"
#include "HeuristicResolver.h"
More information about the cfe-commits
mailing list