Skip to content

Commit 8dec0d0

Browse files
authored
Merge pull request Ericsson#780 from wbqpk3/afferent-coupling
Added afferent coupling for types.
2 parents 2f0cd48 + fa16bab commit 8dec0d0

File tree

9 files changed

+338
-4
lines changed

9 files changed

+338
-4
lines changed

plugins/cpp/model/include/model/cpprecord.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,15 @@ struct CppRecordCount
9090
std::size_t count;
9191
};
9292

93+
#pragma db view \
94+
object(CppMemberType) \
95+
object(CppAstNode : CppMemberType::memberAstNode == CppAstNode::id)
96+
struct CppMemberTypeAstView
97+
{
98+
#pragma db column(CppMemberType::typeHash)
99+
std::uint64_t typeHash;
100+
};
101+
93102
}
94103
}
95104

plugins/cpp_metrics/model/include/model/cppastnodemetrics.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ struct CppAstNodeMetrics
2222
BUMPY_ROAD = 4,
2323
LACK_OF_COHESION = 5,
2424
LACK_OF_COHESION_HS = 6,
25-
EFFERENT_TYPE = 7
25+
EFFERENT_TYPE = 7,
26+
AFFERENT_TYPE = 8
2627
};
2728

2829
#pragma db id auto

plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,8 @@ class CppMetricsParser : public AbstractParser
7878
void lackOfCohesion();
7979
// Calculate the efferent coupling of types.
8080
void efferentTypeLevel();
81+
// Calculate the afferent coupling of types.
82+
void afferentTypeLevel();
8183

8284

8385
/// @brief Constructs an ODB query that you can use to filter only
@@ -200,6 +202,7 @@ class CppMetricsParser : public AbstractParser
200202
static const int functionBumpyRoadPartitionMultiplier = 5;
201203
static const int lackOfCohesionPartitionMultiplier = 25;
202204
static const int efferentCouplingTypesPartitionMultiplier = 5;
205+
static const int afferentCouplingTypesPartitionMultiplier = 5;
203206
};
204207

205208
} // parser

plugins/cpp_metrics/parser/src/cppmetricsparser.cpp

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
#include <model/cppfilemetrics-odb.hxx>
99
#include <model/cppinheritance.h>
1010
#include <model/cppinheritance-odb.hxx>
11+
#include <model/cpprecord.h>
12+
#include <model/cpprecord-odb.hxx>
1113

1214
#include <model/cppastnode.h>
1315
#include <model/cppastnode-odb.hxx>
@@ -428,6 +430,89 @@ void CppMetricsParser::efferentTypeLevel()
428430
});
429431
}
430432

433+
void CppMetricsParser::afferentTypeLevel()
434+
{
435+
parallelCalcMetric<model::CohesionCppRecordView>(
436+
"Afferent coupling of types",
437+
_threadCount * afferentCouplingTypesPartitionMultiplier,// number of jobs; adjust for granularity
438+
getFilterPathsQuery<model::CohesionCppRecordView>(),
439+
[&, this](const MetricsTasks<model::CohesionCppRecordView>& tasks)
440+
{
441+
util::OdbTransaction{_ctx.db}([&, this]
442+
{
443+
typedef odb::query<cc::model::CppAstNode> AstQuery;
444+
typedef odb::query<cc::model::CppInheritance> InheritanceQuery;
445+
typedef odb::query<cc::model::CppMemberType> MemTypeQuery;
446+
typedef odb::result<cc::model::CppAstNode> AstResult;
447+
typedef odb::result<cc::model::CppMemberTypeAstView> MemTypeAstResult;
448+
449+
std::set<std::uint64_t> dependentTypes;
450+
for (const model::CohesionCppRecordView& type : tasks)
451+
{
452+
dependentTypes.clear();
453+
454+
// Find derived types
455+
for (const model::CppInheritance& inheritance : _ctx.db->query<model::CppInheritance>(
456+
InheritanceQuery::base == type.entityHash))
457+
{
458+
dependentTypes.insert(inheritance.derived);
459+
}
460+
461+
// Find usages of the type
462+
for (const model::CppAstNode& usage : _ctx.db->query<model::CppAstNode>(
463+
AstQuery::entityHash == type.entityHash &&
464+
AstQuery::location.range.end.line != model::Position::npos))
465+
{
466+
// Check if usage is in class member function or attribute
467+
MemTypeAstResult memberNode = _ctx.db->query<model::CppMemberTypeAstView>(
468+
AstQuery::symbolType.in(model::CppAstNode::SymbolType::Function, model::CppAstNode::SymbolType::Variable) &&
469+
AstQuery::astType.in(model::CppAstNode::AstType::Definition, model::CppAstNode::AstType::Declaration) &&
470+
AstQuery::location.file == usage.location.file.object_id() &&
471+
AstQuery::location.range.start.line <= usage.location.range.start.line &&
472+
AstQuery::location.range.end.line >= usage.location.range.end.line &&
473+
MemTypeQuery::typeHash != usage.entityHash);
474+
475+
if (!memberNode.empty())
476+
{
477+
dependentTypes.insert(memberNode.begin()->typeHash);
478+
} else {
479+
// The usage can be in a member function defined outside of the class definition
480+
// E.g. void ClassName::foo() { A a; }
481+
// ^ usage here
482+
483+
// Find parent function
484+
AstResult parentFunction = _ctx.db->query<model::CppAstNode>(
485+
AstQuery::symbolType == model::CppAstNode::SymbolType::Function &&
486+
AstQuery::astType == model::CppAstNode::AstType::Definition &&
487+
AstQuery::location.file == usage.location.file.object_id() &&
488+
AstQuery::location.range.start.line <= usage.location.range.start.line &&
489+
AstQuery::location.range.end.line >= usage.location.range.end.line);
490+
491+
if (!parentFunction.empty())
492+
{
493+
// Find if the function is a member function of a class
494+
MemTypeAstResult memberFunction = _ctx.db->query<model::CppMemberTypeAstView>(
495+
AstQuery::entityHash == parentFunction.begin()->entityHash &&
496+
MemTypeQuery::typeHash != usage.entityHash);
497+
498+
if (!memberFunction.empty())
499+
{
500+
dependentTypes.insert(memberFunction.begin()->typeHash);
501+
}
502+
}
503+
}
504+
}
505+
506+
model::CppAstNodeMetrics metric;
507+
metric.astNodeId = type.astNodeId;
508+
metric.type = model::CppAstNodeMetrics::Type::AFFERENT_TYPE;
509+
metric.value = dependentTypes.size();
510+
_ctx.db->persist(metric);
511+
}
512+
});
513+
});
514+
}
515+
431516
bool CppMetricsParser::parse()
432517
{
433518
LOG(info) << "[cppmetricsparser] Computing function parameter count metric.";
@@ -442,6 +527,8 @@ bool CppMetricsParser::parse()
442527
lackOfCohesion();
443528
LOG(info) << "[cppmetricsparser] Computing efferent coupling metric for types.";
444529
efferentTypeLevel();
530+
LOG(info) << "[cppmetricsparser] Computing afferent coupling metric for types.";
531+
afferentTypeLevel();
445532
return true;
446533
}
447534

plugins/cpp_metrics/service/cxxmetrics.thrift

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@ enum CppAstNodeMetricsType
1212
BumpyRoad = 4,
1313
LackOfCohesion = 5,
1414
LackOfCohesionHS = 6,
15-
EfferentType = 7
15+
EfferentType = 7,
16+
AfferentType = 8
1617
}
1718

1819
enum CppModuleMetricsType
@@ -124,4 +125,4 @@ service CppMetricsService
124125
* This function returns the names of module-level C++ metrics.
125126
*/
126127
list<CppModuleMetricsTypeName> getCppModuleMetricsTypeNames()
127-
}
128+
}

plugins/cpp_metrics/service/src/cppmetricsservice.cpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,10 @@ void CppMetricsServiceHandler::getCppAstNodeMetricsTypeNames(
5757
typeName.type = CppAstNodeMetricsType::EfferentType;
5858
typeName.name = "Efferent coupling of type";
5959
_return.push_back(typeName);
60+
61+
typeName.type = CppAstNodeMetricsType::AfferentType;
62+
typeName.name = "Afferent coupling of type";
63+
_return.push_back(typeName);
6064
}
6165

6266
void CppMetricsServiceHandler::getCppModuleMetricsTypeNames(

plugins/cpp_metrics/test/sources/parser/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ add_library(CppMetricsTestProject STATIC
55
functionmccabe.cpp
66
typemccabe.cpp
77
lackofcohesion.cpp
8-
bumpyroad.cpp)
8+
bumpyroad.cpp
9+
afferentcoupling.cpp)
Lines changed: 176 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,176 @@
1+
#include <memory>
2+
#include <vector>
3+
4+
// Member types
5+
class A {};
6+
class A_D {
7+
A a;
8+
};
9+
10+
class A2 {};
11+
class A2_D {
12+
A2* a2;
13+
};
14+
15+
class A3 {};
16+
class A3_D {
17+
A3& a3;
18+
};
19+
20+
class A4 {};
21+
class A4_D {
22+
std::vector<A4> a4_vec;
23+
};
24+
25+
class A5 {};
26+
class A5_D {
27+
std::unique_ptr<A5> a5_ptr;
28+
};
29+
// ==========
30+
31+
// Function parameters
32+
class B {};
33+
class B_D {
34+
void foo(B b);
35+
};
36+
37+
class B2 {};
38+
class B2_D {
39+
void foo(B2* b);
40+
};
41+
42+
class B3 {};
43+
class B3_D {
44+
void foo(B3& b);
45+
};
46+
47+
class B4 {};
48+
class B4_D {
49+
void foo(std::vector<B4>& b4_vec);
50+
};
51+
52+
class B5 {};
53+
class B5_D {
54+
void foo(std::unique_ptr<B5>& b5_ptr);
55+
};
56+
57+
class B6 {};
58+
class B6_D {
59+
void foo(int, bool, B6* b);
60+
};
61+
62+
class B7 {};
63+
class B7_D {
64+
void foo(int, bool, B7* b);
65+
};
66+
67+
class B8 {};
68+
class B8_D {
69+
void foo(int, bool);
70+
void foo(int, bool, B8* b);
71+
};
72+
// ==========
73+
74+
// Function local variables
75+
class C {};
76+
class C_D {
77+
void foo()
78+
{
79+
C c;
80+
}
81+
};
82+
83+
class C2 {};
84+
class C2_D {
85+
void foo()
86+
{
87+
auto c2 = C2();
88+
}
89+
};
90+
91+
class C3 {};
92+
class C3_D {
93+
void foo()
94+
{
95+
std::vector<C3> c3_vec;
96+
}
97+
};
98+
// ==========
99+
100+
// Out-of-class member functions
101+
class D {};
102+
class D_D {
103+
void foo();
104+
};
105+
106+
void D_D::foo()
107+
{
108+
D d;
109+
}
110+
111+
class D2 {};
112+
class D2_D {
113+
void foo();
114+
};
115+
116+
void D2_D::foo()
117+
{
118+
std::unique_ptr<D2> d2_ptr;
119+
}
120+
// ==========
121+
122+
// Multiple usage of the same type
123+
class E {};
124+
class E_D {
125+
E* e;
126+
127+
void foo()
128+
{
129+
E e;
130+
}
131+
132+
void bar(std::vector<E> e_vec);
133+
};
134+
// ==========
135+
136+
// Inheritance
137+
class F {};
138+
class F_D : public F {};
139+
// ==========
140+
141+
// Multi inheritance
142+
class G {};
143+
class G_C {};
144+
class G_D : public G_C, public G {};
145+
// ==========
146+
147+
// Multiple dependant types
148+
class H {};
149+
class H_D1 {
150+
H h;
151+
};
152+
153+
class H_D2 {
154+
void foo()
155+
{
156+
std::vector<H> h_vec;
157+
}
158+
};
159+
160+
// ----------
161+
162+
class H2 {};
163+
class H2_D1 {
164+
H2 h2;
165+
void bar(std::unique_ptr<H2> h2_ptr);
166+
};
167+
168+
class H2_D2 {
169+
H2* h2_ptr;
170+
171+
void foo()
172+
{
173+
std::vector<H2> h2_vec;
174+
}
175+
};
176+
// ==========

0 commit comments

Comments
 (0)