Skip to content

Commit 0f86064

Browse files
Implement relational cohesion metric (#795)
Co-authored-by: Máté Cserép <mcserep@gmail.com>
1 parent dc03735 commit 0f86064

File tree

17 files changed

+236
-9
lines changed

17 files changed

+236
-9
lines changed

plugins/cpp_metrics/model/include/model/cppcohesionmetrics.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,16 @@ struct CohesionCppRecordView
2525
CppAstNodeId astNodeId;
2626
};
2727

28+
#pragma db view \
29+
object(CppRecord) \
30+
object(CppAstNode : CppRecord::astNodeId == CppAstNode::id) \
31+
object(File : CppAstNode::location.file)
32+
struct CohesionCppRecord_Count
33+
{
34+
#pragma db column("count(" + CppEntity::id + ")")
35+
std::size_t count;
36+
};
37+
2838
#pragma db view \
2939
object(CppMemberType) \
3040
object(CppAstNode : CppMemberType::memberAstNode) \

plugins/cpp_metrics/model/include/model/cppfilemetrics.h

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,9 @@ struct CppFileMetrics
1313
{
1414
enum Type
1515
{
16-
EFFERENT_MODULE,
17-
AFFERENT_MODULE
16+
EFFERENT_MODULE = 1,
17+
AFFERENT_MODULE = 2,
18+
RELATIONAL_COHESION_MODULE = 3
1819
};
1920

2021
#pragma db id auto
@@ -27,7 +28,7 @@ struct CppFileMetrics
2728
Type type;
2829

2930
#pragma db not_null
30-
unsigned value;
31+
double value;
3132
};
3233

3334
#pragma db view \
@@ -45,7 +46,7 @@ struct CppModuleMetricsForPathView
4546
CppFileMetrics::Type type;
4647

4748
#pragma db column(CppFileMetrics::value)
48-
unsigned value;
49+
double value;
4950
};
5051

5152
#pragma db view \

plugins/cpp_metrics/model/include/model/cpptypedependencymetrics.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,20 @@ struct CppTypeDependencyMetricsPathView
4949
std::string dependencyPath;
5050
};
5151

52+
#pragma db view \
53+
object(CppTypeDependencyMetrics) \
54+
object(CppAstNode = EntityAstNode : CppTypeDependencyMetrics::entityHash == EntityAstNode::entityHash \
55+
&& EntityAstNode::astType == cc::model::CppAstNode::AstType::Definition) \
56+
object(File = EntityFile : EntityAstNode::location.file == EntityFile::id) \
57+
object(CppAstNode = DependencyAstNode : CppTypeDependencyMetrics::dependencyHash == DependencyAstNode::entityHash \
58+
&& DependencyAstNode::astType == cc::model::CppAstNode::AstType::Definition) \
59+
object(File = DependencyFile : DependencyAstNode::location.file == DependencyFile::id)
60+
struct CppTypeDependencyMetrics_Count
61+
{
62+
#pragma db column("count(" + CppTypeDependencyMetrics::id + ")")
63+
std::size_t count;
64+
};
65+
5266
#pragma db view \
5367
object(CppTypeDependencyMetrics) \
5468
object(CppAstNode = EntityAstNode : CppTypeDependencyMetrics::entityHash == EntityAstNode::entityHash \

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,8 @@ class CppMetricsParser : public AbstractParser
8484
void efferentModuleLevel();
8585
// Calculate the afferent coupling at module level.
8686
void afferentModuleLevel();
87+
// Calculate relational cohesion at module level.
88+
void relationalCohesionModuleLevel();
8789
// Returns module path query based on parser configuration.
8890
odb::query<model::File> getModulePathsQuery();
8991

@@ -210,6 +212,7 @@ class CppMetricsParser : public AbstractParser
210212
static const int afferentCouplingTypesPartitionMultiplier = 5;
211213
static const int efferentCouplingModulesPartitionMultiplier = 5;
212214
static const int afferentCouplingModulesPartitionMultiplier = 5;
215+
static const int relationalCohesionPartitionMultiplier = 5;
213216
};
214217

215218
} // parser

plugins/cpp_metrics/parser/src/cppmetricsparser.cpp

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -383,8 +383,6 @@ void CppMetricsParser::efferentTypeLevel()
383383
{
384384
typedef odb::query<cc::model::CppMemberType> MemTypeQuery;
385385
typedef odb::query<cc::model::CppInheritanceCount> InheritanceQuery;
386-
typedef odb::query<cc::model::CppFunctionParamTypeView> ParamQuery;
387-
typedef odb::query<cc::model::CppFunctionLocalTypeView> LocalQuery;
388386
typedef odb::query<cc::model::CppFunction> FuncQuery;
389387

390388
std::set<std::uint64_t> dependentTypes;
@@ -605,6 +603,53 @@ void CppMetricsParser::afferentModuleLevel()
605603
});
606604
}
607605

606+
void CppMetricsParser::relationalCohesionModuleLevel()
607+
{
608+
// Compute relational cohesion defined by CppDepend:
609+
// https://www.cppdepend.com/documentation/code-metrics#RelationalCohesion
610+
611+
parallelCalcMetric<model::File>(
612+
"Relational cohesion at module level",
613+
_threadCount * relationalCohesionPartitionMultiplier, // number of jobs; adjust for granularity
614+
getModulePathsQuery(),
615+
[&, this](const MetricsTasks<model::File>& tasks)
616+
{
617+
util::OdbTransaction{_ctx.db}([&, this]
618+
{
619+
typedef odb::query<model::CppTypeDependencyMetrics_Count> TypeDependencyQuery;
620+
typedef model::CppTypeDependencyMetrics_Count TypeDependencyResult;
621+
typedef odb::query<model::CohesionCppRecord_Count> RecordQuery;
622+
typedef model::CohesionCppRecord_Count RecordResult;
623+
624+
for (const model::File& file : tasks)
625+
{
626+
TypeDependencyResult dependencies = _ctx.db->query_value<model::CppTypeDependencyMetrics_Count>(
627+
TypeDependencyQuery::EntityFile::path.like(file.path + '%') &&
628+
TypeDependencyQuery::DependencyFile::path.like(file.path + '%'));
629+
630+
// Let R be the number of type relationships that are internal to a module
631+
// (i.e that do not connect to types outside the module)
632+
const std::size_t r = dependencies.count;
633+
634+
RecordResult types = _ctx.db->query_value<model::CohesionCppRecord_Count>(
635+
RecordQuery::File::path.like(file.path + '%'));
636+
637+
// Let N be the number of types within the module.
638+
const std::size_t n = types.count;
639+
640+
// Relational cohesion
641+
const double h = (n != 0) ? (double)(r + 1) / n : 0;
642+
643+
model::CppFileMetrics metric;
644+
metric.file = file.id;
645+
metric.type = model::CppFileMetrics::Type::RELATIONAL_COHESION_MODULE;
646+
metric.value = h;
647+
_ctx.db->persist(metric);
648+
}
649+
});
650+
});
651+
}
652+
608653
bool CppMetricsParser::parse()
609654
{
610655
LOG(info) << "[cppmetricsparser] Computing function parameter count metric.";
@@ -625,6 +670,8 @@ bool CppMetricsParser::parse()
625670
efferentModuleLevel(); // This metric needs to be calculated after efferentTypeLevel
626671
LOG(info) << "[cppmetricsparser] Computing afferent coupling metric at module level.";
627672
afferentModuleLevel(); // This metric needs to be calculated after afferentTypeLevel
673+
LOG(info) << "[cppmetricsparser] Computing relational cohesion metric at module level.";
674+
relationalCohesionModuleLevel(); // This metric needs to be calculated after efferentTypeLevel
628675
return true;
629676
}
630677

plugins/cpp_metrics/service/cxxmetrics.thrift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,9 @@ enum CppAstNodeMetricsType
1818

1919
enum CppModuleMetricsType
2020
{
21-
Placeholder = 1
21+
EfferentModule = 1,
22+
AfferentModule = 2,
23+
RelationalCohesionModule = 3
2224
}
2325

2426
struct CppAstNodeMetricsTypeName

plugins/cpp_metrics/service/src/cppmetricsservice.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,8 +70,16 @@ void CppMetricsServiceHandler::getCppModuleMetricsTypeNames(
7070
{
7171
CppModuleMetricsTypeName typeName;
7272

73-
typeName.type = CppModuleMetricsType::Placeholder;
74-
typeName.name = "Placeholder";
73+
typeName.type = CppModuleMetricsType::EfferentModule;
74+
typeName.name = "Efferent coupling of module";
75+
_return.push_back(typeName);
76+
77+
typeName.type = CppModuleMetricsType::AfferentModule;
78+
typeName.name = "Afferent coupling of module";
79+
_return.push_back(typeName);
80+
81+
typeName.type = CppModuleMetricsType::RelationalCohesionModule;
82+
typeName.name = "Relational cohesion of module";
7583
_return.push_back(typeName);
7684
}
7785

plugins/cpp_metrics/test/sources/parser/modulemetrics.cpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,12 @@
55
#include "./module_c/c1.h"
66
#include "./module_c/c2.h"
77
#include "./module_d/d1.h"
8+
9+
#include "./rc_module_a/a1.h"
10+
#include "./rc_module_a/a2.h"
11+
#include "./rc_module_a/a3.h"
12+
#include "./rc_module_b/b1.h"
13+
#include "./rc_module_c/c1.h"
14+
#include "./rc_module_c/c2.h"
15+
#include "./rc_module_c/c3.h"
16+
#include "./rc_module_c/c4.h"
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#ifndef CC_CPP_RC_MODULE_METRICS_TEST_A1
2+
#define CC_CPP_RC_MODULE_METRICS_TEST_A1
3+
4+
#include "./a2.h"
5+
#include "./a3.h"
6+
7+
namespace CC_CPP_RC_MODULE_METRICS_TEST
8+
{
9+
class A1 {
10+
A2 a2;
11+
A3 a3;
12+
};
13+
}
14+
15+
#endif
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#ifndef CC_CPP_RC_MODULE_METRICS_TEST_A2
2+
#define CC_CPP_RC_MODULE_METRICS_TEST_A2
3+
4+
#include "./a3.h"
5+
6+
namespace CC_CPP_RC_MODULE_METRICS_TEST
7+
{
8+
class A2 {
9+
A3 a3;
10+
};
11+
}
12+
13+
#endif

0 commit comments

Comments
 (0)