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 &param, 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 &param, 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