Skip to content

Commit 103b18f

Browse files
chouzzDimitri Ratz
authored andcommitted
[clangd] Support symbolTags for document symbol
1 parent 9100c92 commit 103b18f

File tree

5 files changed

+151
-87
lines changed

5 files changed

+151
-87
lines changed

clang-tools-extra/clangd/AST.cpp

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -194,6 +194,69 @@ bool isImplementationDetail(const Decl *D) {
194194
D->getASTContext().getSourceManager());
195195
}
196196

197+
// Whether T is const in a loose sense - is a variable with this type readonly?
198+
bool isConst(QualType T) {
199+
if (T.isNull())
200+
return false;
201+
T = T.getNonReferenceType();
202+
if (T.isConstQualified())
203+
return true;
204+
if (const auto *AT = T->getAsArrayTypeUnsafe())
205+
return isConst(AT->getElementType());
206+
if (isConst(T->getPointeeType()))
207+
return true;
208+
return false;
209+
}
210+
211+
bool isConst(const Decl *D) {
212+
if (llvm::isa<EnumConstantDecl>(D) || llvm::isa<NonTypeTemplateParmDecl>(D))
213+
return true;
214+
if (llvm::isa<FieldDecl>(D) || llvm::isa<VarDecl>(D) ||
215+
llvm::isa<MSPropertyDecl>(D) || llvm::isa<BindingDecl>(D)) {
216+
if (isConst(llvm::cast<ValueDecl>(D)->getType()))
217+
return true;
218+
}
219+
if (const auto *OCPD = llvm::dyn_cast<ObjCPropertyDecl>(D)) {
220+
if (OCPD->isReadOnly())
221+
return true;
222+
}
223+
if (const auto *MPD = llvm::dyn_cast<MSPropertyDecl>(D)) {
224+
if (!MPD->hasSetter())
225+
return true;
226+
}
227+
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) {
228+
if (CMD->isConst())
229+
return true;
230+
}
231+
return false;
232+
}
233+
234+
bool isStatic(const Decl *D) {
235+
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
236+
return CMD->isStatic();
237+
if (const VarDecl *VD = llvm::dyn_cast<VarDecl>(D))
238+
return VD->isStaticDataMember() || VD->isStaticLocal();
239+
if (const auto *OPD = llvm::dyn_cast<ObjCPropertyDecl>(D))
240+
return OPD->isClassProperty();
241+
if (const auto *OMD = llvm::dyn_cast<ObjCMethodDecl>(D))
242+
return OMD->isClassMethod();
243+
return false;
244+
}
245+
246+
bool isAbstract(const Decl *D) {
247+
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
248+
return CMD->isPureVirtual();
249+
if (const auto *CRD = llvm::dyn_cast<CXXRecordDecl>(D))
250+
return CRD->hasDefinition() && CRD->isAbstract();
251+
return false;
252+
}
253+
254+
bool isVirtual(const Decl *D) {
255+
if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D))
256+
return CMD->isVirtual();
257+
return false;
258+
}
259+
197260
SourceLocation nameLocation(const clang::Decl &D, const SourceManager &SM) {
198261
auto L = D.getLocation();
199262
// For `- (void)foo` we want `foo` not the `-`.

clang-tools-extra/clangd/AST.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,37 @@ bool isImplicitTemplateInstantiation(const NamedDecl *D);
154154
/// explicit specialization.
155155
bool isExplicitTemplateSpecialization(const NamedDecl *D);
156156

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

clang-tools-extra/clangd/FindSymbols.cpp

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,35 @@ std::string getSymbolName(ASTContext &Ctx, const NamedDecl &ND) {
188188
return printName(Ctx, ND);
189189
}
190190

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

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

clang-tools-extra/clangd/Protocol.h

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,6 +1104,30 @@ struct CodeAction {
11041104
};
11051105
llvm::json::Value toJSON(const CodeAction &);
11061106

1107+
enum class SymbolTag {
1108+
Deprecated = 1 ,
1109+
Private = 2,
1110+
Package = 3,
1111+
Protected = 4,
1112+
Public = 5,
1113+
Internal= 6,
1114+
File = 7,
1115+
Static = 8,
1116+
Abstract = 9,
1117+
Final = 10,
1118+
Sealed = 11,
1119+
Constant = 12,
1120+
Transient = 13,
1121+
Volatile = 14,
1122+
Synchronized = 15,
1123+
Virtual = 16,
1124+
Nullable = 17,
1125+
NonNull = 18,
1126+
Declaration = 19,
1127+
Definition = 20,
1128+
ReadOnly = 21,
1129+
};
1130+
llvm::json::Value toJSON(SymbolTag);
11071131
/// Represents programming constructs like variables, classes, interfaces etc.
11081132
/// that appear in a document. Document symbols can be hierarchical and they
11091133
/// have two ranges: one that encloses its definition and one that points to its
@@ -1121,6 +1145,9 @@ struct DocumentSymbol {
11211145
/// Indicates if this symbol is deprecated.
11221146
bool deprecated = false;
11231147

1148+
/// Tags for this symbol, e.g public, private, static, const etc.
1149+
std::vector<SymbolTag> tags;
1150+
11241151
/// The range enclosing this symbol not including leading/trailing whitespace
11251152
/// but everything else like comments. This information is typically used to
11261153
/// determine if the clients cursor is inside the symbol to reveal in the
@@ -1572,8 +1599,6 @@ struct ResolveTypeHierarchyItemParams {
15721599
bool fromJSON(const llvm::json::Value &, ResolveTypeHierarchyItemParams &,
15731600
llvm::json::Path);
15741601

1575-
enum class SymbolTag { Deprecated = 1 };
1576-
llvm::json::Value toJSON(SymbolTag);
15771602

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

clang-tools-extra/clangd/SemanticHighlighting.cpp

Lines changed: 0 additions & 85 deletions
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)