Skip to content

Commit e0c814f

Browse files
committed
Function call diagram
1 parent 7680f6d commit e0c814f

7 files changed

Lines changed: 800 additions & 2 deletions

File tree

plugins/go/service/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ include_directories(SYSTEM
1313

1414
add_library(goservice SHARED
1515
src/goservice.cpp
16+
src/diagram.cpp
1617
src/plugin.cpp)
1718

1819
target_compile_options(goservice PUBLIC -Wno-unknown-pragmas)

plugins/go/service/include/service/goservice.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,13 @@ class GoServiceHandler : virtual public LanguageServiceIf
171171
UNDEFINITION, /*!< Macro undefinition. */
172172
};
173173

174+
enum DiagramType
175+
{
176+
FUNCTION_CALL, /*!< In the function call diagram the nodes are functions and
177+
the edges are the function calls between them. The diagram also displays
178+
some dynamic information such as virtual function calls. */
179+
};
180+
174181
private:
175182
std::vector<model::GoAstNode> queryDefinitions(
176183
const core::AstNodeId& astNodeId_);
@@ -200,13 +207,37 @@ class GoServiceHandler : virtual public LanguageServiceIf
200207
const odb::query<model::GoAstNode>& query_
201208
= odb::query<model::GoAstNode>(true));
202209

210+
/**
211+
* This function returns the function calls in a given function.
212+
* @param astNodeId_ An AST node ID which belongs to a function.
213+
*/
214+
std::vector<model::GoAstNode> queryCalls(const core::AstNodeId& astNodeId_);
215+
216+
/**
217+
* This function returns the number of function calls in a given function.
218+
* @param astNodeId_ An AST node ID which belongs to a function.
219+
*/
220+
std::size_t queryCallsCount(
221+
const core::AstNodeId& astNodeId_);
222+
223+
/**
224+
* This function returns an AST query to get the function calls in the given
225+
* function.
226+
*/
227+
odb::query<model::GoAstNode> astCallsQuery(
228+
const model::GoAstNode& astNode_);
229+
203230
/**
204231
* This function returns meta information of the AST nodes
205232
* (e.g. public, static, virtual etc.)
206233
*/
207234
std::map<model::GoAstNodeId, std::vector<std::string>> getTags(
208235
const std::vector<model::GoAstNode>& nodes_);
209236

237+
util::Graph returnDiagram(
238+
const core::AstNodeId& astNodeId_,
239+
const std::int32_t diagramId_);
240+
210241
std::shared_ptr<odb::database> _db;
211242
std::shared_ptr<std::string> _datadir;
212243
util::OdbTransaction _transaction;

plugins/go/service/src/diagram.cpp

Lines changed: 306 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,306 @@
1+
#include <model/govariable.h>
2+
#include <model/govariable-odb.hxx>
3+
4+
#include <util/legendbuilder.h>
5+
#include <util/util.h>
6+
#include "diagram.h"
7+
8+
namespace
9+
{
10+
11+
/**
12+
* This function checks if the given container contains a specified value.
13+
* @param v_ A container to inspect.
14+
* @param val_ Value which will be searched.
15+
* @return True if the container contains the value.
16+
*/
17+
template <typename Cont>
18+
bool contains(const Cont& c_, const typename Cont::value_type& val_)
19+
{
20+
return std::find(c_.begin(), c_.end(), val_) != c_.end();
21+
}
22+
23+
/**
24+
* This function wraps the content to a HTML tag and adds attributes to it.
25+
*/
26+
std::string graphHtmlTag(
27+
const std::string& tag_,
28+
const std::string& content_,
29+
const std::string& attr_ = "")
30+
{
31+
return std::string("<")
32+
.append(tag_)
33+
.append(" ")
34+
.append(attr_)
35+
.append(">")
36+
.append(content_)
37+
.append("</")
38+
.append(tag_)
39+
.append(">");
40+
}
41+
42+
}
43+
44+
namespace cc
45+
{
46+
namespace service
47+
{
48+
namespace language
49+
{
50+
51+
GoDiagram::GoDiagram(
52+
std::shared_ptr<odb::database> db_,
53+
std::shared_ptr<std::string> datadir_,
54+
const cc::webserver::ServerContext& context_)
55+
: _goHandler(db_, datadir_, context_),
56+
_projectHandler(db_, datadir_, context_)
57+
{
58+
}
59+
60+
void GoDiagram::getFunctionCallDiagram(
61+
util::Graph& graph_,
62+
const core::AstNodeId& astNodeId_)
63+
{
64+
std::map<core::AstNodeId, util::Graph::Node> visitedNodes;
65+
std::vector<AstNodeInfo> nodes;
66+
67+
graph_.setAttribute("rankdir", "LR");
68+
69+
//--- Center node ---//
70+
71+
_goHandler.getReferences(
72+
nodes, astNodeId_, GoServiceHandler::DEFINITION, {});
73+
74+
if (nodes.empty())
75+
return;
76+
77+
util::Graph::Node centerNode = addNode(graph_, nodes.front());
78+
decorateNode(graph_, centerNode, centerNodeDecoration);
79+
visitedNodes[astNodeId_] = centerNode;
80+
81+
//--- Callees ---//
82+
83+
nodes.clear();
84+
_goHandler.getReferences(nodes, astNodeId_, GoServiceHandler::CALLEE, {});
85+
86+
for (const AstNodeInfo& node : nodes)
87+
{
88+
util::Graph::Node calleeNode;
89+
90+
auto it = visitedNodes.find(node.id);
91+
if (it == visitedNodes.end())
92+
{
93+
calleeNode = addNode(graph_, node);
94+
decorateNode(graph_, calleeNode, calleeNodeDecoration);
95+
visitedNodes.insert(it, std::make_pair(node.id, calleeNode));
96+
}
97+
else
98+
calleeNode = it->second;
99+
100+
if (!graph_.hasEdge(centerNode, calleeNode))
101+
{
102+
util::Graph::Edge edge = graph_.createEdge(centerNode, calleeNode);
103+
decorateEdge(graph_, edge, calleeEdgeDecoration);
104+
}
105+
}
106+
107+
//--- Callers ---//
108+
109+
nodes.clear();
110+
_goHandler.getReferences(nodes, astNodeId_, GoServiceHandler::CALLER, {});
111+
112+
for (const AstNodeInfo& node : nodes)
113+
{
114+
util::Graph::Node callerNode;
115+
116+
auto it = visitedNodes.find(node.id);
117+
if (it == visitedNodes.end())
118+
{
119+
callerNode = addNode(graph_, node);
120+
decorateNode(graph_, callerNode, callerNodeDecoration);
121+
visitedNodes.insert(it, std::make_pair(node.id, callerNode));
122+
}
123+
else
124+
callerNode = it->second;
125+
126+
if (!graph_.hasEdge(callerNode, centerNode))
127+
{
128+
util::Graph::Edge edge = graph_.createEdge(callerNode, centerNode);
129+
decorateEdge(graph_, edge, callerEdgeDecoration);
130+
}
131+
}
132+
133+
_subgraphs.clear();
134+
}
135+
136+
std::string GoDiagram::visibilityToHtml(const AstNodeInfo& node_)
137+
{
138+
if (contains(node_.tags, "public"))
139+
return graphHtmlTag("font", "+", "color='green'");
140+
if (contains(node_.tags, "private"))
141+
return graphHtmlTag("font", "-", "color='red'");
142+
if (contains(node_.tags, "protected"))
143+
return graphHtmlTag("font", "#", "color='blue'");
144+
145+
return "";
146+
}
147+
148+
std::string GoDiagram::memberContentToHtml(
149+
const AstNodeInfo& node_,
150+
const std::string& content_)
151+
{
152+
std::string startTags;
153+
std::string endTags;
154+
155+
if (contains(node_.tags, "static"))
156+
{
157+
startTags += "<b>";
158+
endTags.insert(0, "</b>");
159+
}
160+
161+
if (contains(node_.tags, "virtual"))
162+
{
163+
startTags += "<i>";
164+
endTags.insert(0, "</i>");
165+
}
166+
167+
return startTags + util::escapeHtml(content_) + endTags;
168+
}
169+
170+
std::string GoDiagram::getProperty(
171+
const core::AstNodeId& astNodeId_,
172+
const std::string& property_)
173+
{
174+
std::map<std::string, std::string> properties;
175+
_goHandler.getProperties(properties, astNodeId_);
176+
return properties[property_];
177+
}
178+
179+
util::Graph::Node GoDiagram::addNode(
180+
util::Graph& graph_,
181+
const AstNodeInfo& nodeInfo_)
182+
{
183+
util::Graph::Node node
184+
= graph_.getOrCreateNode(nodeInfo_.id,
185+
addSubgraph(graph_, nodeInfo_.range.file));
186+
187+
graph_.setNodeAttribute(node, "label", nodeInfo_.astNodeValue);
188+
189+
return node;
190+
}
191+
192+
std::string GoDiagram::getFunctionCallLegend()
193+
{
194+
util::LegendBuilder builder("Function Call Diagram");
195+
196+
builder.addNode("center function", centerNodeDecoration);
197+
builder.addNode("called function", calleeNodeDecoration);
198+
builder.addNode("caller function", callerNodeDecoration);
199+
builder.addNode("virtual function", virtualNodeDecoration);
200+
builder.addEdge("called", calleeEdgeDecoration);
201+
builder.addEdge("caller", callerEdgeDecoration);
202+
203+
return builder.getOutput();
204+
}
205+
206+
util::Graph::Subgraph GoDiagram::addSubgraph(
207+
util::Graph& graph_,
208+
const core::FileId& fileId_)
209+
{
210+
auto it = _subgraphs.find(fileId_);
211+
212+
if (it != _subgraphs.end())
213+
return it->second;
214+
215+
core::FileInfo fileInfo;
216+
_projectHandler.getFileInfo(fileInfo, fileId_);
217+
218+
util::Graph::Subgraph subgraph
219+
= graph_.getOrCreateSubgraph("cluster_" + fileInfo.path);
220+
221+
graph_.setSubgraphAttribute(subgraph, "id", fileInfo.id);
222+
graph_.setSubgraphAttribute(subgraph, "label", fileInfo.path);
223+
224+
_subgraphs.insert(it, std::make_pair(fileInfo.path, subgraph));
225+
226+
return subgraph;
227+
}
228+
229+
void GoDiagram::decorateNode(
230+
util::Graph& graph_,
231+
const util::Graph::Node& node_,
232+
const Decoration& decoration_) const
233+
{
234+
for (const auto& attr : decoration_)
235+
graph_.setNodeAttribute(node_, attr.first, attr.second);
236+
}
237+
238+
void GoDiagram::decorateEdge(
239+
util::Graph& graph_,
240+
const util::Graph::Edge& edge_,
241+
const Decoration& decoration_) const
242+
{
243+
for (const auto& attr : decoration_)
244+
graph_.setEdgeAttribute(edge_, attr.first, attr.second);
245+
}
246+
247+
void GoDiagram::decorateSubgraph(
248+
util::Graph& graph_,
249+
const util::Graph::Subgraph& subgraph_,
250+
const Decoration& decoration_) const
251+
{
252+
for (const auto& attr : decoration_)
253+
graph_.setSubgraphAttribute(subgraph_, attr.first, attr.second);
254+
}
255+
256+
const GoDiagram::Decoration GoDiagram::centerNodeDecoration = {
257+
{"style", "filled"},
258+
{"fillcolor", "gold"}
259+
};
260+
261+
const GoDiagram::Decoration GoDiagram::calleeNodeDecoration = {
262+
{"style", "filled"},
263+
{"fillcolor", "lightblue"}
264+
};
265+
266+
const GoDiagram::Decoration GoDiagram::callerNodeDecoration = {
267+
{"style", "filled"},
268+
{"fillcolor", "coral"}
269+
};
270+
271+
const GoDiagram::Decoration GoDiagram::virtualNodeDecoration = {
272+
{"shape", "diamond"},
273+
{"style", "filled"},
274+
{"fillcolor", "cyan"}
275+
};
276+
277+
const GoDiagram::Decoration GoDiagram::calleeEdgeDecoration = {
278+
{"color", "blue"}
279+
};
280+
281+
const GoDiagram::Decoration GoDiagram::callerEdgeDecoration = {
282+
{"color", "red"}
283+
};
284+
285+
const GoDiagram::Decoration GoDiagram::centerClassNodeDecoration = {
286+
{"style", "filled"},
287+
{"fillcolor", "gold"},
288+
{"shape", "box"}
289+
};
290+
291+
const GoDiagram::Decoration GoDiagram::classNodeDecoration = {
292+
{"shape", "box"}
293+
};
294+
295+
const GoDiagram::Decoration GoDiagram::usedClassEdgeDecoration = {
296+
{"style", "dashed"},
297+
{"color", "mediumpurple"}
298+
};
299+
300+
const GoDiagram::Decoration GoDiagram::inheritClassEdgeDecoration = {
301+
{"arrowhead", "empty"}
302+
};
303+
304+
}
305+
}
306+
}

0 commit comments

Comments
 (0)