-
Notifications
You must be signed in to change notification settings - Fork 18k
Symbol tags in SymbolInformation, WorkspaceSymbol, CallHierarchyItem and TypeHierarchyItem #170103
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 17 commits
910a890
01a022e
c450d62
c297add
35ee021
89237d1
9eb72dc
0b1c3f6
7e11d69
007764f
f1fdea3
2c0475f
525a6ec
d6865cc
66529e5
fad6461
f99012b
317abd3
477dd75
765ef3e
949f510
88aa2a1
bdacf73
86a41ca
2aea848
c39d64b
d54d642
88b5800
0ffcf77
c1005bb
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,7 +13,10 @@ | |
| #include "Quality.h" | ||
| #include "SourceCode.h" | ||
| #include "index/Index.h" | ||
| #include "index/Symbol.h" | ||
| #include "index/SymbolLocation.h" | ||
| #include "support/Logger.h" | ||
| #include "clang/AST/Decl.h" | ||
| #include "clang/AST/DeclFriend.h" | ||
| #include "clang/AST/DeclTemplate.h" | ||
| #include "clang/Index/IndexSymbol.h" | ||
|
|
@@ -117,6 +120,16 @@ bool isAbstract(const Decl *D) { | |
| } | ||
|
|
||
| // Indicates whether declaration D is virtual in cases where D is a method. | ||
| // We want to treat a method as virtual if it is declared virtual, even if it | ||
| // is not implemented in this class, or if it overrides/implements a | ||
| // base-class method. This is because the "virtual" modifier is still relevant | ||
| // to the method's behavior and how it should be highlighted, even if it is | ||
| // not itself a virtual method in the strictest sense. For example, a method | ||
| // that overrides a virtual method from a base class is still considered | ||
| // virtual, even if it is not declared as such in the derived class. | ||
| // Similarly, a method that implements a pure virtual method from a base class | ||
| // is also considered virtual, even if it is not declared as such in the | ||
| // derived class. | ||
| bool isVirtual(const Decl *D) { | ||
| if (const auto *CMD = llvm::dyn_cast<CXXMethodDecl>(D)) | ||
| return CMD->isVirtual(); | ||
|
|
@@ -135,6 +148,42 @@ bool isFinal(const Decl *D) { | |
| return false; | ||
| } | ||
|
|
||
| // A method "overrides" if: | ||
| // 1. It overrides at least one method | ||
| // 2. At least one of the overridden methods is virtual (but NOT pure | ||
| // virtual) | ||
| bool isOverrides(const NamedDecl *ND) { | ||
|
timon-ul marked this conversation as resolved.
|
||
| if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(ND)) { | ||
| if (MD->size_overridden_methods() == 0) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we don't need to check |
||
| return false; | ||
|
|
||
| for (const auto *Overridden : MD->overridden_methods()) { | ||
| // Check if the overridden method is virtual but not pure virtual | ||
| if (Overridden->isVirtual() && !Overridden->isPureVirtual()) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of checking
|
||
| return true; | ||
| } | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| // A method "implements" pure virtual methods from base classes if: | ||
| // 1. It overrides at least one method | ||
| // 2. It is NOT itself pure virtual (i.e., it has a concrete implementation) | ||
| // 3. ALL overridden methods are pure virtual | ||
| bool isImplements(const NamedDecl *ND) { | ||
|
timon-ul marked this conversation as resolved.
|
||
| if (const auto *MD = llvm::dyn_cast<CXXMethodDecl>(ND)) { | ||
| if (MD->size_overridden_methods() == 0 || MD->isPureVirtual()) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here. I think we don't need to check |
||
| return false; | ||
|
|
||
| for (const auto *Overridden : MD->overridden_methods()) { | ||
| if (!Overridden->isPureVirtual()) | ||
| return false; | ||
| } | ||
| return true; | ||
| } | ||
| return false; | ||
| } | ||
|
|
||
| // Indicates whether declaration D is a unique definition (as opposed to a | ||
| // declaration). | ||
| bool isUniqueDefinition(const NamedDecl *Decl) { | ||
|
|
@@ -153,6 +202,63 @@ bool isUniqueDefinition(const NamedDecl *Decl) { | |
| isa<TemplateTemplateParmDecl>(Decl) || isa<ObjCCategoryDecl>(Decl) || | ||
| isa<ObjCImplDecl>(Decl); | ||
| } | ||
|
|
||
| // Filter symbol tags based on the presence of other tags and the kind of | ||
| // symbol. This is needed to avoid redundant tags, e.g. final implies override, | ||
| // override implies virtual, etc. | ||
| SymbolTags filterSymbolTags(const NamedDecl &ND, const SymbolTags ST) { | ||
|
timon-ul marked this conversation as resolved.
Outdated
timon-ul marked this conversation as resolved.
Outdated
|
||
| SymbolTags Result = ST; | ||
|
|
||
| if (not isa<CXXMethodDecl>(ND)) | ||
| return Result; | ||
|
|
||
| if (ST & toSymbolTagBitmask(SymbolTag::Overrides)) { | ||
| // Overrides means that ND overrides an existing implementation of a virtual | ||
| // method in a base class. If a symbol is marked as Overrides, the tags | ||
| // Virtual, Declaration and Definition should be removed, as the Overrides | ||
|
timon-ul marked this conversation as resolved.
Outdated
|
||
| // tag implies that the symbol has/is virtual/declaration/definition. | ||
| Result &= ~toSymbolTagBitmask(SymbolTag::Virtual); | ||
| Result &= ~toSymbolTagBitmask(SymbolTag::Declaration); | ||
| Result &= ~toSymbolTagBitmask(SymbolTag::Definition); | ||
| } | ||
| if (ST & toSymbolTagBitmask(SymbolTag::Implements)) { | ||
| // Implements means that ND implements an existing pure virtual method in a | ||
| // base class. If a symbol is marked as Implements, the tags Virtual, | ||
| // Declaration, Definition and Overrides should be removed, as the | ||
| // Implements tag implies that the symbol is virtual, is a declaration, is a | ||
| // definition, and overrides a method. | ||
| Result &= ~toSymbolTagBitmask(SymbolTag::Virtual); | ||
| Result &= ~toSymbolTagBitmask(SymbolTag::Declaration); | ||
| Result &= ~toSymbolTagBitmask(SymbolTag::Definition); | ||
| Result &= ~toSymbolTagBitmask(SymbolTag::Overrides); | ||
| } | ||
| if (ST & toSymbolTagBitmask(SymbolTag::Virtual)) { | ||
| // Virtual means that ND is a virtual method that does not override any | ||
| // method in a base class. If a symbol is marked as Virtual, the tags | ||
| // Declaration and Definition should be removed, as the Virtual tag implies | ||
| // that the symbol is a declaration/definition. | ||
| Result &= ~toSymbolTagBitmask(SymbolTag::Declaration); | ||
| Result &= ~toSymbolTagBitmask(SymbolTag::Definition); | ||
| } | ||
| if (ST & toSymbolTagBitmask(SymbolTag::Abstract)) { | ||
| // Abstract means that ND is a pure virtual method. If a symbol is marked as | ||
| // Abstract, the tags Virtual, Declaration and Definition should be removed, | ||
| // as the Abstract tag implies that the symbol is virtual and a | ||
| // declaration/definition. | ||
| Result &= ~toSymbolTagBitmask(SymbolTag::Virtual); | ||
| Result &= ~toSymbolTagBitmask(SymbolTag::Declaration); | ||
| Result &= ~toSymbolTagBitmask(SymbolTag::Definition); | ||
| } | ||
| if (ST & toSymbolTagBitmask(SymbolTag::Final)) { | ||
|
timon-ul marked this conversation as resolved.
Outdated
|
||
| // Final means that ND is a method that cannot be overridden by any method | ||
| // in a derived class. If a symbol is marked as Final, the tags Virtual and | ||
| // Overrides should be removed, as the Final tag implies that the symbol is | ||
| // virtual. | ||
| Result &= ~toSymbolTagBitmask(SymbolTag::Virtual); | ||
| Result &= ~toSymbolTagBitmask(SymbolTag::Overrides); | ||
| } | ||
| return Result; | ||
| } | ||
| } // namespace | ||
|
|
||
| SymbolTags toSymbolTagBitmask(const SymbolTag ST) { | ||
|
|
@@ -178,9 +284,15 @@ SymbolTags computeSymbolTags(const NamedDecl &ND) { | |
| if (isAbstract(&ND)) | ||
| Result |= toSymbolTagBitmask(SymbolTag::Abstract); | ||
|
|
||
| if (isOverrides(&ND)) | ||
| Result |= toSymbolTagBitmask(SymbolTag::Overrides); | ||
|
|
||
| if (isFinal(&ND)) | ||
| Result |= toSymbolTagBitmask(SymbolTag::Final); | ||
|
|
||
| if (isImplements(&ND)) | ||
| Result |= toSymbolTagBitmask(SymbolTag::Implements); | ||
|
|
||
| if (not isa<UnresolvedUsingValueDecl>(ND)) { | ||
| // Do not treat an UnresolvedUsingValueDecl as a declaration. | ||
| // It's more common to think of it as a reference to the | ||
|
|
@@ -208,21 +320,47 @@ SymbolTags computeSymbolTags(const NamedDecl &ND) { | |
| return Result; | ||
| } | ||
|
|
||
| std::vector<SymbolTag> expandTagBitmask(const SymbolTags STGS) { | ||
|
timon-ul marked this conversation as resolved.
|
||
| std::vector<SymbolTag> Tags; | ||
|
|
||
| if (STGS == 0) | ||
| return Tags; | ||
|
|
||
| // No filtering required since this function is only used for Symbols from the | ||
| // index, which have already been filtered in getSymbolTags(const NamedDecl | ||
| // &ND). | ||
|
|
||
|
ratzdi marked this conversation as resolved.
|
||
| // Iterate through SymbolTag enum values and collect any that are present in | ||
| // the bitmask. SymbolTag values are in the numeric range | ||
| // [FirstTag .. LastTag]. | ||
| constexpr unsigned MinTag = static_cast<unsigned>(SymbolTag::FirstTag); | ||
|
timon-ul marked this conversation as resolved.
|
||
| constexpr unsigned MaxTag = static_cast<unsigned>(SymbolTag::LastTag); | ||
| for (unsigned I = MinTag; I <= MaxTag; ++I) { | ||
| auto ST = static_cast<SymbolTag>(I); | ||
| if (STGS & toSymbolTagBitmask(ST)) | ||
| Tags.push_back(ST); | ||
| } | ||
| return Tags; | ||
| } | ||
|
|
||
| std::vector<SymbolTag> getSymbolTags(const NamedDecl &ND) { | ||
| const auto symbolTags = computeSymbolTags(ND); | ||
|
timon-ul marked this conversation as resolved.
Outdated
|
||
| std::vector<SymbolTag> Tags; | ||
|
|
||
| if (symbolTags == 0) | ||
| return Tags; | ||
|
|
||
| // Apply specific filter to the symbol tags. | ||
| const auto filteredTags = filterSymbolTags(ND, symbolTags); | ||
|
timon-ul marked this conversation as resolved.
Outdated
|
||
|
|
||
| // Iterate through SymbolTag enum values and collect any that are present in | ||
| // the bitmask. SymbolTag values are in the numeric range | ||
| // [FirstTag .. LastTag]. | ||
| constexpr unsigned MinTag = static_cast<unsigned>(SymbolTag::FirstTag); | ||
| constexpr unsigned MaxTag = static_cast<unsigned>(SymbolTag::LastTag); | ||
| for (unsigned I = MinTag; I <= MaxTag; ++I) { | ||
| auto ST = static_cast<SymbolTag>(I); | ||
| if (symbolTags & toSymbolTagBitmask(ST)) | ||
| if (filteredTags & toSymbolTagBitmask(ST)) | ||
| Tags.push_back(ST); | ||
| } | ||
| return Tags; | ||
|
|
@@ -359,6 +497,7 @@ getWorkspaceSymbols(llvm::StringRef Query, int Limit, | |
| Info.score = Relevance.NameMatch > std::numeric_limits<float>::epsilon() | ||
| ? Score / Relevance.NameMatch | ||
| : QualScore; | ||
| Info.tags = expandTagBitmask(Sym.Tags); | ||
| Top.push({Score, std::move(Info)}); | ||
| }); | ||
| for (auto &R : std::move(Top).items()) | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.