1 // Copyright 2017-2018 ccls Authors
2 // SPDX-License-Identifier: Apache-2.0
3
4 #include "hierarchy.hh"
5 #include "message_handler.hh"
6 #include "pipeline.hh"
7 #include "query.hh"
8
9 #include <unordered_set>
10
11 namespace ccls {
12 namespace {
13 struct Param : TextDocumentPositionParam {
14 // If id+kind are specified, expand a node; otherwise textDocument+position
15 // should be specified for building the root and |levels| of nodes below.
16 Usr usr;
17 std::string id;
18 Kind kind = Kind::Invalid;
19
20 // true: derived classes/functions; false: base classes/functions
21 bool derived = false;
22 bool qualified = true;
23 int levels = 1;
24 bool hierarchy = false;
25 };
26
27 REFLECT_STRUCT(Param, textDocument, position, id, kind, derived, qualified,
28 levels, hierarchy);
29
30 struct Out_cclsInheritance {
31 Usr usr;
32 std::string id;
33 Kind kind;
34 std::string_view name;
35 Location location;
36 // For unexpanded nodes, this is an upper bound because some entities may be
37 // undefined. If it is 0, there are no members.
38 int numChildren;
39 // Empty if the |levels| limit is reached.
40 std::vector<Out_cclsInheritance> children;
41 };
42 REFLECT_STRUCT(Out_cclsInheritance, id, kind, name, location, numChildren,
43 children);
44
45 bool expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
46 bool qualified, int levels);
47
48 template <typename Q>
expandHelper(MessageHandler * m,Out_cclsInheritance * entry,bool derived,bool qualified,int levels,Q & entity)49 bool expandHelper(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
50 bool qualified, int levels, Q &entity) {
51 const auto *def = entity.anyDef();
52 if (def) {
53 entry->name = def->name(qualified);
54 if (def->spell) {
55 if (auto loc = getLsLocation(m->db, m->wfiles, *def->spell))
56 entry->location = *loc;
57 } else if (entity.declarations.size()) {
58 if (auto loc = getLsLocation(m->db, m->wfiles, entity.declarations[0]))
59 entry->location = *loc;
60 }
61 } else if (!derived) {
62 entry->numChildren = 0;
63 return false;
64 }
65 std::unordered_set<Usr> seen;
66 if (derived) {
67 if (levels > 0) {
68 for (auto usr : entity.derived) {
69 if (!seen.insert(usr).second)
70 continue;
71 Out_cclsInheritance entry1;
72 entry1.id = std::to_string(usr);
73 entry1.usr = usr;
74 entry1.kind = entry->kind;
75 if (expand(m, &entry1, derived, qualified, levels - 1))
76 entry->children.push_back(std::move(entry1));
77 }
78 entry->numChildren = int(entry->children.size());
79 } else
80 entry->numChildren = int(entity.derived.size());
81 } else {
82 if (levels > 0) {
83 for (auto usr : def->bases) {
84 if (!seen.insert(usr).second)
85 continue;
86 Out_cclsInheritance entry1;
87 entry1.id = std::to_string(usr);
88 entry1.usr = usr;
89 entry1.kind = entry->kind;
90 if (expand(m, &entry1, derived, qualified, levels - 1))
91 entry->children.push_back(std::move(entry1));
92 }
93 entry->numChildren = int(entry->children.size());
94 } else
95 entry->numChildren = int(def->bases.size());
96 }
97 return true;
98 }
99
expand(MessageHandler * m,Out_cclsInheritance * entry,bool derived,bool qualified,int levels)100 bool expand(MessageHandler *m, Out_cclsInheritance *entry, bool derived,
101 bool qualified, int levels) {
102 if (entry->kind == Kind::Func)
103 return expandHelper(m, entry, derived, qualified, levels,
104 m->db->getFunc(entry->usr));
105 else
106 return expandHelper(m, entry, derived, qualified, levels,
107 m->db->getType(entry->usr));
108 }
109
buildInitial(MessageHandler * m,SymbolRef sym,bool derived,bool qualified,int levels)110 std::optional<Out_cclsInheritance> buildInitial(MessageHandler *m,
111 SymbolRef sym, bool derived,
112 bool qualified, int levels) {
113 Out_cclsInheritance entry;
114 entry.id = std::to_string(sym.usr);
115 entry.usr = sym.usr;
116 entry.kind = sym.kind;
117 expand(m, &entry, derived, qualified, levels);
118 return entry;
119 }
120
inheritance(MessageHandler * m,Param & param,ReplyOnce & reply)121 void inheritance(MessageHandler *m, Param ¶m, ReplyOnce &reply) {
122 std::optional<Out_cclsInheritance> result;
123 if (param.id.size()) {
124 try {
125 param.usr = std::stoull(param.id);
126 } catch (...) {
127 return;
128 }
129 result.emplace();
130 result->id = std::to_string(param.usr);
131 result->usr = param.usr;
132 result->kind = param.kind;
133 if (!(((param.kind == Kind::Func && m->db->hasFunc(param.usr)) ||
134 (param.kind == Kind::Type && m->db->hasType(param.usr))) &&
135 expand(m, &*result, param.derived, param.qualified, param.levels)))
136 result.reset();
137 } else {
138 auto [file, wf] = m->findOrFail(param.textDocument.uri.getPath(), reply);
139 if (!wf) {
140 return;
141 }
142 for (SymbolRef sym : findSymbolsAtLocation(wf, file, param.position))
143 if (sym.kind == Kind::Func || sym.kind == Kind::Type) {
144 result =
145 buildInitial(m, sym, param.derived, param.qualified, param.levels);
146 break;
147 }
148 }
149
150 if (param.hierarchy)
151 reply(result);
152 else
153 reply(flattenHierarchy(result));
154 }
155 } // namespace
156
ccls_inheritance(JsonReader & reader,ReplyOnce & reply)157 void MessageHandler::ccls_inheritance(JsonReader &reader, ReplyOnce &reply) {
158 Param param;
159 reflect(reader, param);
160 inheritance(this, param, reply);
161 }
162
textDocument_implementation(TextDocumentPositionParam & param,ReplyOnce & reply)163 void MessageHandler::textDocument_implementation(
164 TextDocumentPositionParam ¶m, ReplyOnce &reply) {
165 Param param1;
166 param1.textDocument = param.textDocument;
167 param1.position = param.position;
168 param1.derived = true;
169 inheritance(this, param1, reply);
170 }
171 } // namespace ccls
172