Skip to content

Commit 02124e4

Browse files
committed
[clangd] Support symbolTags for document symbol
1 parent 2e43a30 commit 02124e4

File tree

5 files changed

+151
-87
lines changed

5 files changed

+151
-87
lines changed

clang-tools-extra/clangd/AST.cpp

+63
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,69 @@ bool isImplementationDetail(const Decl *D) {
169169
D->getASTContext().getSourceManager());
170170
}
171171

172+
// Whether T is const in a loose sense - is a variable with this type readonly?
173+
bool isConst(QualType T) {
174+
if (T.isNull())
175+
return false;
176+
T = T.getNonReferenceType();
177+
if (T.isConstQualified())
178+
return true;
179+
if (const auto *AT = T->getAsArrayTypeUnsafe())
180+
return isConst(AT->getElementType());
181+
if (isConst(T->getPointeeType()))
182+
return true;
183+
return false;
184+
}
185+
186+
bool isConst(const Decl *D) {
187+
if (llvm::isa<EnumConstantDecl>(D) || llvm::isa<NonTypeTemplateParmDecl>(D))
188+
return true;
189+
if (llvm::isa<FieldDecl>(D) || llvm::isa<VarDecl>(D) ||
190+
llvm::isa<MSPropertyDecl>(D) || llvm::isa<BindingDecl>(D)) {
191+
if (isConst(llvm::cast<ValueDecl>(D)->getType()))
192+
return true;
193+
}
194+
if (const auto *OCPD = llvm::dyn_cast<ObjCPropertyDecl>(D)) {
195+
if (OCPD->isReadOnly())
196+
return true;
197+
}
198+
if (const auto *MPD = llvm::dyn_cast<MSPropertyDecl>(D)) {
199+
if (!MPD->hasSetter())
200+
return true;
201+
}
202+
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) {
203+
if (CMD->isConst())
204+
return true;
205+
}
206+
return false;
207+
}
208+
209+
bool isStatic(const Decl *D) {
210+
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
211+
return CMD->isStatic();
212+
if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(D))
213+
return VD->isStaticDataMember() || VD->isStaticLocal();
214+
if (const auto *OPD = llvm::dyn_cast<ObjCPropertyDecl>(D))
215+
return OPD->isClassProperty();
216+
if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(D))
217+
return OMD->isClassMethod();
218+
return false;
219+
}
220+
221+
bool isAbstract(const Decl *D) {
222+
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
223+
return CMD->isPureVirtual();
224+
if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(D))
225+
return CRD->hasDefinition() && CRD->isAbstract();
226+
return false;
227+
}
228+
229+
bool isVirtual(const Decl *D) {
230+
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
231+
return CMD->isVirtual();
232+
return false;
233+
}
234+
172235
SourceLocation nameLocation(const clang::Decl &D, const SourceManager &SM) {
173236
auto L = D.getLocation();
174237
// For `- (void)foo` we want `foo` not the `-`.

clang-tools-extra/clangd/AST.h

+31
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,37 @@ bool isImplicitTemplateInstantiation(const NamedDecl *D);
152152
/// explicit specialization.
153153
bool isExplicitTemplateSpecialization(const NamedDecl *D);
154154

155+
// Whether T is const in a loose sense - is a variable with this type readonly?
156+
bool isConst(QualType T);
157+
158+
// Whether D is const in a loose sense (should it be highlighted as such?)
159+
// FIXME: This is separate from whether *a particular usage* can mutate D.
160+
// We may want V in V.size() to be readonly even if V is mutable.
161+
bool isConst(const Decl *D);
162+
163+
// "Static" means many things in C++, only some get the "static" modifier.
164+
//
165+
// Meanings that do:
166+
// - Members associated with the class rather than the instance.
167+
// This is what 'static' most often means across languages.
168+
// - static local variables
169+
// These are similarly "detached from their context" by the static keyword.
170+
// In practice, these are rarely used inside classes, reducing confusion.
171+
//
172+
// Meanings that don't:
173+
// - Namespace-scoped variables, which have static storage class.
174+
// This is implicit, so the keyword "static" isn't so strongly associated.
175+
// If we want a modifier for these, "global scope" is probably the concept.
176+
// - Namespace-scoped variables/functions explicitly marked "static".
177+
// There the keyword changes *linkage* , which is a totally different concept.
178+
// If we want to model this, "file scope" would be a nice modifier.
179+
//
180+
// This is confusing, and maybe we should use another name, but because "static"
181+
// is a standard LSP modifier, having one with that name has advantages.
182+
bool isStatic(const Decl *D);
183+
bool isAbstract(const Decl *D);
184+
bool isVirtual(const Decl *D);
185+
155186
/// Returns a nested name specifier loc of \p ND if it was present in the
156187
/// source, e.g.
157188
/// void ns::something::foo() -> returns 'ns::something'

clang-tools-extra/clangd/FindSymbols.cpp

+30
Original file line numberDiff line numberDiff line change
@@ -187,6 +187,35 @@ std::string getSymbolName(ASTContext &Ctx, const NamedDecl &ND) {
187187
return printName(Ctx, ND);
188188
}
189189

190+
std::vector<SymbolTag> getSymbolTags(const NamedDecl &ND)
191+
{
192+
std::vector<SymbolTag> Tags;
193+
if (ND.isDeprecated())
194+
Tags.push_back(SymbolTag::Deprecated);
195+
if (isConst(&ND))
196+
Tags.push_back(SymbolTag::Constant);
197+
if (isStatic(&ND))
198+
Tags.push_back(SymbolTag::Static);
199+
if (const FieldDecl *FD = dyn_cast<FieldDecl>(&ND)) {
200+
switch (FD->getAccess()) {
201+
case AS_public:
202+
Tags.push_back(SymbolTag::Public);
203+
break;
204+
case AS_protected:
205+
Tags.push_back(SymbolTag::Protected);
206+
break;
207+
case AS_private:
208+
Tags.push_back(SymbolTag::Private);
209+
break;
210+
default:
211+
break;
212+
}
213+
}
214+
if (isVirtual(&ND))
215+
Tags.push_back(SymbolTag::Virtual);
216+
return Tags;
217+
}
218+
190219
std::string getSymbolDetail(ASTContext &Ctx, const NamedDecl &ND) {
191220
PrintingPolicy P(Ctx.getPrintingPolicy());
192221
P.SuppressScope = true;
@@ -241,6 +270,7 @@ std::optional<DocumentSymbol> declToSym(ASTContext &Ctx, const NamedDecl &ND) {
241270
SI.range = Range{sourceLocToPosition(SM, SymbolRange->getBegin()),
242271
sourceLocToPosition(SM, SymbolRange->getEnd())};
243272
SI.detail = getSymbolDetail(Ctx, ND);
273+
SI.tags = getSymbolTags(ND);
244274

245275
SourceLocation NameLoc = ND.getLocation();
246276
SourceLocation FallbackNameLoc;

clang-tools-extra/clangd/Protocol.h

+27-2
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,30 @@ struct CodeAction {
10901090
};
10911091
llvm::json::Value toJSON(const CodeAction &);
10921092

1093+
enum class SymbolTag {
1094+
Deprecated = 1 ,
1095+
Private = 2,
1096+
Package = 3,
1097+
Protected = 4,
1098+
Public = 5,
1099+
Internal= 6,
1100+
File = 7,
1101+
Static = 8,
1102+
Abstract = 9,
1103+
Final = 10,
1104+
Sealed = 11,
1105+
Constant = 12,
1106+
Transient = 13,
1107+
Volatile = 14,
1108+
Synchronized = 15,
1109+
Virtual = 16,
1110+
Nullable = 17,
1111+
NonNull = 18,
1112+
Declaration = 19,
1113+
Definition = 20,
1114+
ReadOnly = 21,
1115+
};
1116+
llvm::json::Value toJSON(SymbolTag);
10931117
/// Represents programming constructs like variables, classes, interfaces etc.
10941118
/// that appear in a document. Document symbols can be hierarchical and they
10951119
/// have two ranges: one that encloses its definition and one that points to its
@@ -1107,6 +1131,9 @@ struct DocumentSymbol {
11071131
/// Indicates if this symbol is deprecated.
11081132
bool deprecated = false;
11091133

1134+
/// Tags for this symbol, e.g public, private, static, const etc.
1135+
std::vector<SymbolTag> tags;
1136+
11101137
/// The range enclosing this symbol not including leading/trailing whitespace
11111138
/// but everything else like comments. This information is typically used to
11121139
/// determine if the clients cursor is inside the symbol to reveal in the
@@ -1558,8 +1585,6 @@ struct ResolveTypeHierarchyItemParams {
15581585
bool fromJSON(const llvm::json::Value &, ResolveTypeHierarchyItemParams &,
15591586
llvm::json::Path);
15601587

1561-
enum class SymbolTag { Deprecated = 1 };
1562-
llvm::json::Value toJSON(SymbolTag);
15631588

15641589
/// The parameter of a `textDocument/prepareCallHierarchy` request.
15651590
struct CallHierarchyPrepareParams : public TextDocumentPositionParams {};

clang-tools-extra/clangd/SemanticHighlighting.cpp

-85
Original file line numberDiff line numberDiff line change
@@ -192,91 +192,6 @@ std::optional<HighlightingKind> kindForType(const Type *TP,
192192
return std::nullopt;
193193
}
194194

195-
// Whether T is const in a loose sense - is a variable with this type readonly?
196-
bool isConst(QualType T) {
197-
if (T.isNull())
198-
return false;
199-
T = T.getNonReferenceType();
200-
if (T.isConstQualified())
201-
return true;
202-
if (const auto *AT = T->getAsArrayTypeUnsafe())
203-
return isConst(AT->getElementType());
204-
if (isConst(T->getPointeeType()))
205-
return true;
206-
return false;
207-
}
208-
209-
// Whether D is const in a loose sense (should it be highlighted as such?)
210-
// FIXME: This is separate from whether *a particular usage* can mutate D.
211-
// We may want V in V.size() to be readonly even if V is mutable.
212-
bool isConst(const Decl *D) {
213-
if (llvm::isa<EnumConstantDecl>(D) || llvm::isa<NonTypeTemplateParmDecl>(D))
214-
return true;
215-
if (llvm::isa<FieldDecl>(D) || llvm::isa<VarDecl>(D) ||
216-
llvm::isa<MSPropertyDecl>(D) || llvm::isa<BindingDecl>(D)) {
217-
if (isConst(llvm::cast<ValueDecl>(D)->getType()))
218-
return true;
219-
}
220-
if (const auto *OCPD = llvm::dyn_cast<ObjCPropertyDecl>(D)) {
221-
if (OCPD->isReadOnly())
222-
return true;
223-
}
224-
if (const auto *MPD = llvm::dyn_cast<MSPropertyDecl>(D)) {
225-
if (!MPD->hasSetter())
226-
return true;
227-
}
228-
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) {
229-
if (CMD->isConst())
230-
return true;
231-
}
232-
return false;
233-
}
234-
235-
// "Static" means many things in C++, only some get the "static" modifier.
236-
//
237-
// Meanings that do:
238-
// - Members associated with the class rather than the instance.
239-
// This is what 'static' most often means across languages.
240-
// - static local variables
241-
// These are similarly "detached from their context" by the static keyword.
242-
// In practice, these are rarely used inside classes, reducing confusion.
243-
//
244-
// Meanings that don't:
245-
// - Namespace-scoped variables, which have static storage class.
246-
// This is implicit, so the keyword "static" isn't so strongly associated.
247-
// If we want a modifier for these, "global scope" is probably the concept.
248-
// - Namespace-scoped variables/functions explicitly marked "static".
249-
// There the keyword changes *linkage* , which is a totally different concept.
250-
// If we want to model this, "file scope" would be a nice modifier.
251-
//
252-
// This is confusing, and maybe we should use another name, but because "static"
253-
// is a standard LSP modifier, having one with that name has advantages.
254-
bool isStatic(const Decl *D) {
255-
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
256-
return CMD->isStatic();
257-
if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(D))
258-
return VD->isStaticDataMember() || VD->isStaticLocal();
259-
if (const auto *OPD = llvm::dyn_cast<ObjCPropertyDecl>(D))
260-
return OPD->isClassProperty();
261-
if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(D))
262-
return OMD->isClassMethod();
263-
return false;
264-
}
265-
266-
bool isAbstract(const Decl *D) {
267-
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
268-
return CMD->isPureVirtual();
269-
if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(D))
270-
return CRD->hasDefinition() && CRD->isAbstract();
271-
return false;
272-
}
273-
274-
bool isVirtual(const Decl *D) {
275-
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
276-
return CMD->isVirtual();
277-
return false;
278-
}
279-
280195
bool isDependent(const Decl *D) {
281196
if (isa<UnresolvedUsingValueDecl>(D))
282197
return true;

0 commit comments

Comments
 (0)