diff --git a/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h b/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h index 9601e9b1d..63e1550c3 100644 --- a/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h +++ b/plugins/cpp_metrics/parser/include/cppmetricsparser/cppmetricsparser.h @@ -218,6 +218,7 @@ class CppMetricsParser : public AbstractParser static const int efferentCouplingModulesPartitionMultiplier = 5; static const int afferentCouplingModulesPartitionMultiplier = 5; static const int relationalCohesionPartitionMultiplier = 5; + static const int typeMcCabePartitionMultiplier = 5; }; } // parser diff --git a/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp b/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp index 1379bea4a..35944c256 100644 --- a/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp +++ b/plugins/cpp_metrics/parser/src/cppmetricsparser.cpp @@ -178,89 +178,90 @@ void CppMetricsParser::functionBumpyRoad() void CppMetricsParser::typeMcCabe() { - util::OdbTransaction {_ctx.db} ([&, this] + using MemberT = model::CppMemberType; + using AstNode = model::CppAstNode; + using Entity = model::CppEntity; + using AstNodeMet = model::CppAstNodeMetrics; + + // Calculate type level McCabe metric for all types on parallel threads. + parallelCalcMetric( + "Type-level McCabe", + _threadCount * typeMcCabePartitionMultiplier,// number of jobs; adjust for granularity + odb::query::symbolType == AstNode::SymbolType::Type && + odb::query::astType == AstNode::AstType::Definition, + [&, this](const MetricsTasks& tasks) { - using MemberT = model::CppMemberType; - using AstNode = model::CppAstNode; - using Entity = model::CppEntity; - using AstNodeMet = model::CppAstNodeMetrics; - - std::map mcValues; - - // Process all class definitions - for (const auto& type : _ctx.db->query( - odb::query::symbolType == AstNode::SymbolType::Type && - odb::query::astType == AstNode::AstType::Definition)) + util::OdbTransaction {_ctx.db} ([&, this] { - // Skip if class is included from external library - type.location.file.load(); - const auto typeFile = _ctx.db->query_one( - odb::query::id == type.location.file->id); - if (!typeFile || !cc::util::isRootedUnderAnyOf(_inputPaths, typeFile->path)) - continue; - - // Skip if its a template instantiation - const auto typeEntity = _ctx.db->query_one( - odb::query::astNodeId == type.id); - if (typeEntity && typeEntity->tags.find(model::Tag::TemplateInstantiation) - != typeEntity->tags.cend()) - continue; - - mcValues[type.id] = 0; - - // Process its methods - for (const auto& method : _ctx.db->query( - odb::query::typeHash == type.entityHash && - odb::query::kind == MemberT::Kind::Method)) + for (const AstNode& type : tasks) { - // Lookup AST node of method - method.memberAstNode.load(); - const auto methodAstNode = _ctx.db->query_one( - odb::query::id == method.memberAstNode->id); - if (!methodAstNode) + // Skip if class is included from external library + type.location.file.load(); + const auto typeFile = _ctx.db->query_one( + odb::query::id == type.location.file->id); + if (!typeFile || !cc::util::isRootedUnderAnyOf(_inputPaths, typeFile->path)) continue; - // Lookup its definition (different AST node if not defined in class body) - auto methodDefs = _ctx.db->query( - odb::query::entityHash == methodAstNode->entityHash && - odb::query::symbolType == AstNode::SymbolType::Function && - odb::query::astType == AstNode::AstType::Definition); - if (methodDefs.empty()) - continue; - const auto methodDef = *methodDefs.begin(); - // Note: we cannot use query_one, because a project might have multiple - // functions with the same entityHash compiled to different binaries - // So we take the first result, which introduces a small level of - // potential inaccuracy - // This could be optimized in the future if linkage information about - // translation units got added to the database - - // Skip implicitly defined methods (constructors, operator=, etc.) - const auto entity = _ctx.db->query_one( - odb::query::astNodeId == methodDef.id); - if (entity && entity->tags.find(model::Tag::Implicit) != entity->tags.cend()) + // Skip if its a template instantiation + const auto typeEntity = _ctx.db->query_one( + odb::query::astNodeId == type.id); + if (typeEntity && typeEntity->tags.find(model::Tag::TemplateInstantiation) + != typeEntity->tags.cend()) continue; - // Lookup metrics of this definition - const auto funcMetrics = _ctx.db->query_one( - odb::query::astNodeId == methodDef.id && - odb::query::type == model::CppAstNodeMetrics::Type::MCCABE_FUNCTION); - if (funcMetrics) + unsigned int value = 0; + + // Process its methods + for (const auto& method : _ctx.db->query( + odb::query::typeHash == type.entityHash && + odb::query::kind == MemberT::Kind::Method)) { - // Increase class mccabe by the method's - mcValues[type.id] += funcMetrics->value; + // Lookup AST node of method + method.memberAstNode.load(); + const auto methodAstNode = _ctx.db->query_one( + odb::query::id == method.memberAstNode->id); + if (!methodAstNode) + continue; + + // Lookup its definition (different AST node if not defined in class body) + auto methodDefs = _ctx.db->query( + odb::query::entityHash == methodAstNode->entityHash && + odb::query::symbolType == AstNode::SymbolType::Function && + odb::query::astType == AstNode::AstType::Definition); + if (methodDefs.empty()) + continue; + const auto methodDef = *methodDefs.begin(); + // Note: we cannot use query_one, because a project might have multiple + // functions with the same entityHash compiled to different binaries + // So we take the first result, which introduces a small level of + // potential inaccuracy + // This could be optimized in the future if linkage information about + // translation units got added to the database + + // Skip implicitly defined methods (constructors, operator=, etc.) + const auto entity = _ctx.db->query_one( + odb::query::astNodeId == methodDef.id); + if (entity && entity->tags.find(model::Tag::Implicit) != entity->tags.cend()) + continue; + + // Lookup metrics of this definition + const auto funcMetrics = _ctx.db->query_one( + odb::query::astNodeId == methodDef.id && + odb::query::type == model::CppAstNodeMetrics::Type::MCCABE_FUNCTION); + if (funcMetrics) + { + // Increase class mccabe by the method's + value += funcMetrics->value; + } } - } - } - for (const auto& mcValue : mcValues) - { - model::CppAstNodeMetrics typeMcMetric; - typeMcMetric.astNodeId = mcValue.first; - typeMcMetric.type = model::CppAstNodeMetrics::Type::MCCABE_TYPE; - typeMcMetric.value = mcValue.second; - _ctx.db->persist(typeMcMetric); - } + model::CppAstNodeMetrics typeMcMetric; + typeMcMetric.astNodeId = type.id; + typeMcMetric.type = model::CppAstNodeMetrics::Type::MCCABE_TYPE; + typeMcMetric.value = value; + _ctx.db->persist(typeMcMetric); + } + }); }); }