Add kind to $ccls/member and iterate all QueryType::def

kind:2 => member functions
kind:3 => nested classes / namespace members
This commit is contained in:
Fangrui Song 2018-09-22 22:55:17 -07:00
parent daa6fbf4b8
commit e24079366b
2 changed files with 106 additions and 55 deletions

View File

@ -7,12 +7,14 @@ ccls, which originates from [cquery](https://github.com/cquery-project/cquery),
* code completion (with both signature help and snippets) * code completion (with both signature help and snippets)
* [definition](src/messages/textDocument_definition.cc)/[references](src/messages/textDcument_references.cc), and other cross references * [definition](src/messages/textDocument_definition.cc)/[references](src/messages/textDcument_references.cc), and other cross references
* hierarchies: [call (caller/callee) hierarchy](src/messages/ccls_callHierarchy.cc), [inheritance (base/derived) hierarchy](src/messages/ccls_inheritanceHierarchy.cc), [member hierarchy](src/messages/ccls_memberHierarchy.cc) * cross reference extensions: `$ccls/call` `$ccls/inheritance` `$ccls/member` `$ccls/vars` ...
* [symbol rename](src/messages/text_documentRename.cc) * hierarchies: [call (caller/callee) hierarchy](src/messages/ccls_call.cc), [inheritance (base/derived) hierarchy](src/messages/ccls_inheritance.cc), [member hierarchy](src/messages/ccls_member.cc)
* [symbol rename](src/messages/textDocument_rename.cc)
* [document symbols](src/messages/textDocument_documentSymbol.cc) and approximate search of [workspace symbol](src/messages/workspace_symbol.cc) * [document symbols](src/messages/textDocument_documentSymbol.cc) and approximate search of [workspace symbol](src/messages/workspace_symbol.cc)
* [hover information](src/messages/textDocument_hover.cc) * [hover information](src/messages/textDocument_hover.cc)
* diagnostics and code actions (clang FixIts) * diagnostics and code actions (clang FixIts)
* semantic highlighting and preprocessor skipped regions * semantic highlighting and preprocessor skipped regions
* semantic navigation: `$ccls/navigate`
It has a global view of the code base and support a lot of cross reference features, see [wiki/FAQ](../../wiki/FAQ). It has a global view of the code base and support a lot of cross reference features, see [wiki/FAQ](../../wiki/FAQ).
It starts indexing the whole project (including subprojects if exist) parallelly when you open the first file, while the main thread can serve requests before the indexing is complete. It starts indexing the whole project (including subprojects if exist) parallelly when you open the first file, while the main thread can serve requests before the indexing is complete.

View File

@ -8,6 +8,7 @@
using namespace ccls; using namespace ccls;
#include <clang/AST/Type.h> #include <clang/AST/Type.h>
#include <llvm/ADT/DenseSet.h>
using namespace clang; using namespace clang;
#include <unordered_set> #include <unordered_set>
@ -30,12 +31,15 @@ struct In_CclsMember : public RequestInMessage {
bool qualified = false; bool qualified = false;
int levels = 1; int levels = 1;
// If SymbolKind::Func and the point is at a type, list member functions
// instead of member variables.
SymbolKind kind = SymbolKind::Var;
bool hierarchy = false; bool hierarchy = false;
} params; } params;
}; };
MAKE_REFLECT_STRUCT(In_CclsMember::Params, textDocument, position, id, MAKE_REFLECT_STRUCT(In_CclsMember::Params, textDocument, position, id,
qualified, levels, hierarchy); qualified, levels, kind, hierarchy);
MAKE_REFLECT_STRUCT(In_CclsMember, id, params); MAKE_REFLECT_STRUCT(In_CclsMember, id, params);
REGISTER_IN_MESSAGE(In_CclsMember); REGISTER_IN_MESSAGE(In_CclsMember);
@ -61,7 +65,7 @@ MAKE_REFLECT_STRUCT_MANDATORY_OPTIONAL(Out_CclsMember, jsonrpc, id,
result); result);
bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry, bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry,
bool qualified, int levels); bool qualified, int levels, SymbolKind memberKind);
// Add a field to |entry| which is a Func/Type. // Add a field to |entry| which is a Func/Type.
void DoField(MessageHandler *m, Out_CclsMember::Entry *entry, void DoField(MessageHandler *m, Out_CclsMember::Entry *entry,
@ -96,7 +100,7 @@ void DoField(MessageHandler *m, Out_CclsMember::Entry *entry,
if (def1->type) { if (def1->type) {
entry1.id = std::to_string(def1->type); entry1.id = std::to_string(def1->type);
entry1.usr = def1->type; entry1.usr = def1->type;
if (Expand(m, &entry1, qualified, levels)) if (Expand(m, &entry1, qualified, levels, SymbolKind::Var))
entry->children.push_back(std::move(entry1)); entry->children.push_back(std::move(entry1));
} else { } else {
entry1.id = "0"; entry1.id = "0";
@ -107,13 +111,13 @@ void DoField(MessageHandler *m, Out_CclsMember::Entry *entry,
// Expand a type node by adding members recursively to it. // Expand a type node by adding members recursively to it.
bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry, bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry,
bool qualified, int levels) { bool qualified, int levels, SymbolKind memberKind) {
if (0 < entry->usr && entry->usr <= BuiltinType::LastKind) { if (0 < entry->usr && entry->usr <= BuiltinType::LastKind) {
entry->name = ClangBuiltinTypeName(int(entry->usr)); entry->name = ClangBuiltinTypeName(int(entry->usr));
return true; return true;
} }
const QueryType &type = m->db->Type(entry->usr); const QueryType *type = &m->db->Type(entry->usr);
const QueryType::Def *def = type.AnyDef(); const QueryType::Def *def = type->AnyDef();
// builtin types have no declaration and empty |qualified|. // builtin types have no declaration and empty |qualified|.
if (!def) if (!def)
return false; return false;
@ -121,51 +125,96 @@ bool Expand(MessageHandler *m, Out_CclsMember::Entry *entry,
std::unordered_set<Usr> seen; std::unordered_set<Usr> seen;
if (levels > 0) { if (levels > 0) {
std::vector<const QueryType *> stack; std::vector<const QueryType *> stack;
seen.insert(type.usr); seen.insert(type->usr);
stack.push_back(&type); stack.push_back(type);
while (stack.size()) { while (stack.size()) {
const auto *def = stack.back()->AnyDef(); type = stack.back();
stack.pop_back(); stack.pop_back();
if (def) { const auto *def = type->AnyDef();
for (Usr usr : def->bases) { if (!def) continue;
auto &type1 = m->db->Type(usr); for (Usr usr : def->bases) {
if (type1.def.size()) { auto &type1 = m->db->Type(usr);
seen.insert(type1.usr); if (type1.def.size()) {
stack.push_back(&type1); seen.insert(type1.usr);
} stack.push_back(&type1);
} }
if (def->alias_of) { }
const QueryType::Def *def1 = m->db->Type(def->alias_of).AnyDef(); if (def->alias_of) {
Out_CclsMember::Entry entry1; const QueryType::Def *def1 = m->db->Type(def->alias_of).AnyDef();
entry1.id = std::to_string(def->alias_of); Out_CclsMember::Entry entry1;
entry1.usr = def->alias_of; entry1.id = std::to_string(def->alias_of);
if (def1 && def1->spell) { entry1.usr = def->alias_of;
// The declaration of target type. if (def1 && def1->spell) {
if (std::optional<lsLocation> loc = // The declaration of target type.
if (std::optional<lsLocation> loc =
GetLsLocation(m->db, m->working_files, *def1->spell))
entry1.location = *loc;
} else if (def->spell) {
// Builtin types have no declaration but the typedef declaration
// itself is useful.
if (std::optional<lsLocation> loc =
GetLsLocation(m->db, m->working_files, *def->spell))
entry1.location = *loc;
}
if (def1 && qualified)
entry1.fieldName = def1->detailed_name;
if (Expand(m, &entry1, qualified, levels - 1, memberKind)) {
// For builtin types |name| is set.
if (entry1.fieldName.empty())
entry1.fieldName = std::string(entry1.name);
entry->children.push_back(std::move(entry1));
}
} else if (memberKind == SymbolKind::Func) {
llvm::DenseSet<Usr, DenseMapInfoForUsr> seen1;
for (auto &def : type->def)
for (Usr usr : def.funcs)
if (seen1.insert(usr).second) {
QueryFunc &func1 = m->db->Func(usr);
if (const QueryFunc::Def *def1 = func1.AnyDef()) {
Out_CclsMember::Entry entry1;
entry1.fieldName = def1->Name(false);
if (def1->spell) {
if (auto loc =
GetLsLocation(m->db, m->working_files, *def1->spell))
entry1.location = *loc;
} else if (func1.declarations.size()) {
if (auto loc = GetLsLocation(m->db, m->working_files,
func1.declarations[0]))
entry1.location = *loc;
}
entry->children.push_back(std::move(entry1));
}
}
} else if (memberKind == SymbolKind::Type) {
llvm::DenseSet<Usr, DenseMapInfoForUsr> seen1;
for (auto &def : type->def)
for (Usr usr : def.types)
if (seen1.insert(usr).second) {
QueryType &type1 = m->db->Type(usr);
if (const QueryType::Def *def1 = type1.AnyDef()) {
Out_CclsMember::Entry entry1;
entry1.fieldName = def1->Name(false);
if (def1->spell) {
if (auto loc =
GetLsLocation(m->db, m->working_files, *def1->spell)) GetLsLocation(m->db, m->working_files, *def1->spell))
entry1.location = *loc; entry1.location = *loc;
} else if (def->spell) { } else if (type1.declarations.size()) {
// Builtin types have no declaration but the typedef declaration if (auto loc = GetLsLocation(m->db, m->working_files,
// itself is useful. type1.declarations[0]))
if (std::optional<lsLocation> loc = entry1.location = *loc;
GetLsLocation(m->db, m->working_files, *def->spell)) }
entry1.location = *loc; entry->children.push_back(std::move(entry1));
} }
if (def1 && qualified) }
entry1.fieldName = def1->detailed_name; } else {
if (Expand(m, &entry1, qualified, levels - 1)) { llvm::DenseSet<Usr, DenseMapInfoForUsr> seen1;
// For builtin types |name| is set. for (auto &def : type->def)
if (entry1.fieldName.empty()) for (auto it : def.vars)
entry1.fieldName = std::string(entry1.name); if (seen1.insert(it.first).second) {
entry->children.push_back(std::move(entry1)); QueryVar &var = m->db->Var(it.first);
} if (!var.def.empty())
} else { DoField(m, entry, var, it.second, qualified, levels - 1);
for (auto it : def->vars) { }
QueryVar &var = m->db->Var(it.first);
if (!var.def.empty())
DoField(m, entry, var, it.second, qualified, levels - 1);
}
}
} }
} }
entry->numChildren = int(entry->children.size()); entry->numChildren = int(entry->children.size());
@ -179,7 +228,7 @@ struct Handler_CclsMember
MethodType GetMethodType() const override { return kMethodType; } MethodType GetMethodType() const override { return kMethodType; }
std::optional<Out_CclsMember::Entry> std::optional<Out_CclsMember::Entry>
BuildInitial(SymbolKind kind, Usr root_usr, bool qualified, int levels) { BuildInitial(SymbolKind kind, Usr root_usr, bool qualified, int levels, SymbolKind memberKind) {
switch (kind) { switch (kind) {
default: default:
return {}; return {};
@ -216,7 +265,7 @@ struct Handler_CclsMember
GetLsLocation(db, working_files, *def->spell)) GetLsLocation(db, working_files, *def->spell))
entry.location = *loc; entry.location = *loc;
} }
Expand(this, &entry, qualified, levels); Expand(this, &entry, qualified, levels, memberKind);
return entry; return entry;
} }
} }
@ -238,7 +287,7 @@ struct Handler_CclsMember
entry.usr = params.usr; entry.usr = params.usr;
// entry.name is empty as it is known by the client. // entry.name is empty as it is known by the client.
if (db->HasType(entry.usr) && if (db->HasType(entry.usr) &&
Expand(this, &entry, params.qualified, params.levels)) Expand(this, &entry, params.qualified, params.levels, params.kind))
out.result = std::move(entry); out.result = std::move(entry);
} else { } else {
QueryFile *file; QueryFile *file;
@ -251,14 +300,14 @@ struct Handler_CclsMember
switch (sym.kind) { switch (sym.kind) {
case SymbolKind::Func: case SymbolKind::Func:
case SymbolKind::Type: case SymbolKind::Type:
out.result = out.result = BuildInitial(sym.kind, sym.usr, params.qualified,
BuildInitial(sym.kind, sym.usr, params.qualified, params.levels); params.levels, params.kind);
break; break;
case SymbolKind::Var: { case SymbolKind::Var: {
const QueryVar::Def *def = db->GetVar(sym).AnyDef(); const QueryVar::Def *def = db->GetVar(sym).AnyDef();
if (def && def->type) if (def && def->type)
out.result = BuildInitial(SymbolKind::Type, def->type, out.result = BuildInitial(SymbolKind::Type, def->type,
params.qualified, params.levels); params.qualified, params.levels, params.kind);
break; break;
} }
default: default: