Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 25 additions & 0 deletions plugins/cpp/model/include/model/cpprecord.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,21 @@ typedef std::shared_ptr<CppMemberType> CppMemberTypePtr;
#pragma db object
struct CppRecord : CppEntity
{
enum Context {
TOP_LEVEL,
NAMESPACE,
RECORD,
FUNCTION,
OTHER
};

bool isAbstract = false;
bool isPOD = false;
bool isLambda = false;

// Context defines where the CppRecord is located in (e.g. in a namespace).
// Context = RECORD means it is nested within another record.
Context context = Context::OTHER;

std::string toString() const
{
Expand All @@ -72,6 +85,18 @@ struct CppRecord : CppEntity

return ret;
}

std::string getContextString() const
Copy link

Copilot AI Jul 8, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The switch in getContextString() covers all enum cases but has no default or final return, which can trigger a missing-return warning. Consider adding a default case or a return after the switch to handle unexpected values.

Copilot uses AI. Check for mistakes.
{
switch (context)
{
case Context::TOP_LEVEL: return "Top Level";
case Context::NAMESPACE: return "Namespace";
case Context::RECORD: return "Record";
case Context::FUNCTION: return "Function";
case Context::OTHER: return "Other";
}
}
};

typedef std::shared_ptr<CppRecord> CppRecordPtr;
Expand Down
14 changes: 14 additions & 0 deletions plugins/cpp/parser/src/clangastvisitor.h
Original file line number Diff line number Diff line change
Expand Up @@ -614,6 +614,20 @@ class ClangASTVisitor : public clang::RecursiveASTVisitor<ClangASTVisitor>
cppRecord->entityHash = astNode->entityHash;
cppRecord->name = rd_->getNameAsString();
cppRecord->qualifiedName = rd_->getQualifiedNameAsString();
cppRecord->isLambda = rd_->isLambda();

const clang::DeclContext* parentDecl = rd_->getParent();
if (parentDecl) {
if (parentDecl->isTranslationUnit()) {
cppRecord->context = model::CppRecord::Context::TOP_LEVEL;
} else if (parentDecl->isNamespace()) {
cppRecord->context = model::CppRecord::Context::NAMESPACE;
} else if (parentDecl->isRecord()) {
cppRecord->context = model::CppRecord::Context::RECORD;
} else if (parentDecl->isFunctionOrMethod()) {
cppRecord->context = model::CppRecord::Context::FUNCTION;
}
}

if (const clang::CXXRecordDecl* crd
= llvm::dyn_cast<clang::CXXRecordDecl>(rd_))
Expand Down
3 changes: 3 additions & 0 deletions plugins/cpp/service/src/cppservice.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -333,9 +333,12 @@ void CppServiceHandler::getProperties(
return_["Abstract type"] = "true";
if (type.isPOD)
return_["POD type"] = "true";
if(type.isLambda)
return_["Lambda"] = "true";

return_["Name"] = type.name;
return_["Qualified name"] = type.qualifiedName;
return_["Context"] = type.getContextString();
}
else
LOG(warning)
Expand Down
5 changes: 5 additions & 0 deletions plugins/cpp/test/src/cpppropertiesservicetest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ TEST_F(CppPropertiesServiceTest, ClassPropertiesTest)
std::map<std::string, std::string> expected =
{
{"Name", "SimpleClass"},
{"Context", "Namespace"},
{"Qualified name", "cc::test::SimpleClass"}
};

Expand All @@ -96,6 +97,7 @@ TEST_F(CppPropertiesServiceTest, ClassPropertiesTest)
std::map<std::string, std::string> expected =
{
{"Name", "NestedClass"},
{"Context", "Namespace"},
{"Qualified name", "cc::test::NestedClass"},
{"POD type", "true"}
};
Expand All @@ -107,6 +109,7 @@ TEST_F(CppPropertiesServiceTest, ClassPropertiesTest)
std::map<std::string, std::string> expected =
{
{"Name", "InnerClass"},
{"Context", "Record"},
{"Qualified name", "cc::test::NestedClass::InnerClass"},
{"POD type", "true"}
};
Expand All @@ -121,6 +124,7 @@ TEST_F(CppPropertiesServiceTest, InheritancePropertiesTest)
std::map<std::string, std::string> expected =
{
{"Name", "BaseClass1"},
{"Context", "Namespace"},
{"Qualified name", "cc::test::BaseClass1"},
{"Abstract type", "true"}
};
Expand All @@ -132,6 +136,7 @@ TEST_F(CppPropertiesServiceTest, InheritancePropertiesTest)
std::map<std::string, std::string> expected =
{
{"Name", "DerivedClass"},
{"Context", "Namespace"},
{"Qualified name", "cc::test::DerivedClass"}
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@
#include <model/cppastnodemetrics.h>
#include <model/cppastnodemetrics-odb.hxx>

#include <model/cppcohesionmetrics.h>
#include <model/cppcohesionmetrics-odb.hxx>

#include <model/cppfunction.h>
#include <model/cppfunction-odb.hxx>

Expand Down Expand Up @@ -88,6 +91,8 @@ class CppMetricsParser : public AbstractParser
void relationalCohesionModuleLevel();
// Returns module path query based on parser configuration.
odb::query<model::File> getModulePathsQuery();
// Returns cohesion record query based on parser configuration.
odb::query<model::CohesionCppRecordView> getCohesionRecordQuery();

/// @brief Constructs an ODB query that you can use to filter only
/// the database records of the given parameter type whose path
Expand Down
27 changes: 24 additions & 3 deletions plugins/cpp_metrics/parser/src/cppmetricsparser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -264,13 +264,28 @@ void CppMetricsParser::typeMcCabe()
});
}

odb::query<model::CohesionCppRecordView> CppMetricsParser::getCohesionRecordQuery()
{
odb::query<model::CohesionCppRecordView> query = getFilterPathsQuery<model::CohesionCppRecordView>();

if (_ctx.options.count("cppmetrics-ignore-lambdas")) {
query = query && odb::query<model::CohesionCppRecordView>::CppRecord::isLambda == false;
}

if (_ctx.options.count("cppmetrics-ignore-nested-classes")) {
query = query && odb::query<model::CohesionCppRecordView>::CppRecord::context != model::CppRecord::Context::RECORD;
}

return query;
}

void CppMetricsParser::lackOfCohesion()
{
// Calculate the cohesion metric for all types on parallel threads.
parallelCalcMetric<model::CohesionCppRecordView>(
"Lack of cohesion",
_threadCount * lackOfCohesionPartitionMultiplier, // number of jobs; adjust for granularity
getFilterPathsQuery<model::CohesionCppRecordView>(),
getCohesionRecordQuery(),
[&, this](const MetricsTasks<model::CohesionCppRecordView>& tasks)
{
util::OdbTransaction {_ctx.db} ([&, this]
Expand Down Expand Up @@ -376,7 +391,7 @@ void CppMetricsParser::efferentTypeLevel()
parallelCalcMetric<model::CohesionCppRecordView>(
"Efferent coupling of types",
_threadCount * efferentCouplingTypesPartitionMultiplier,// number of jobs; adjust for granularity
getFilterPathsQuery<model::CohesionCppRecordView>(),
getCohesionRecordQuery(),
[&, this](const MetricsTasks<model::CohesionCppRecordView>& tasks)
{
util::OdbTransaction{_ctx.db}([&, this]
Expand Down Expand Up @@ -456,7 +471,7 @@ void CppMetricsParser::afferentTypeLevel()
parallelCalcMetric<model::CohesionCppRecordView>(
"Afferent coupling of types",
_threadCount * afferentCouplingTypesPartitionMultiplier,// number of jobs; adjust for granularity
getFilterPathsQuery<model::CohesionCppRecordView>(),
getCohesionRecordQuery(),
[&, this](const MetricsTasks<model::CohesionCppRecordView>& tasks)
{
util::OdbTransaction{_ctx.db}([&, this]
Expand Down Expand Up @@ -697,6 +712,12 @@ extern "C"
{
boost::program_options::options_description description("C++ Metrics Plugin");

description.add_options()
("cppmetrics-ignore-lambdas",
"Skip Efferent/Afferent Coupling, Lack of Cohesion C++ metrics calculations for lambdas.")
("cppmetrics-ignore-nested-classes",
"Skip Efferent/Afferent Coupling, Lack of Cohesion C++ metrics calculations for nested classes.");

return description;
}

Expand Down