diff --git a/bin/app/data/ProjectSettings.xml b/bin/app/data/ProjectSettings.xml
index cdc9c1fc..3ed67512 100644
--- a/bin/app/data/ProjectSettings.xml
+++ b/bin/app/data/ProjectSettings.xml
@@ -1,4 +1,4 @@
data/src
-
\ No newline at end of file
+
diff --git a/bin/app/data/src/header.h b/bin/app/data/src/header.h
index 84af058e..40d3d197 100644
--- a/bin/app/data/src/header.h
+++ b/bin/app/data/src/header.h
@@ -2,3 +2,6 @@ int main();
void foo();
int sum(int a, int b);
int diff(int a, int b);
+
+class A {};
+class B : public A {};
diff --git a/src/lib/data/Storage.cpp b/src/lib/data/Storage.cpp
index ef186d45..79ac2187 100644
--- a/src/lib/data/Storage.cpp
+++ b/src/lib/data/Storage.cpp
@@ -162,6 +162,20 @@ void Storage::onEnumFieldParsed(const ParseLocation& location, const std::string
addTokenLocation(node, location);
}
+void Storage::onInheritanceParsed(
+ const ParseLocation& location, const std::string& fullName, const std::string& baseName, AccessType access)
+{
+ log("inheritance", fullName + " : " + baseName, location);
+
+ Node* node = m_graph.createNodeHierarchy(fullName);
+ Node* baseNode = m_graph.createNodeHierarchy(baseName);
+
+ Edge* edge = m_graph.createEdge(Edge::EDGE_INHERITANCE, node, baseNode);
+ edge->setAccess(convertAccessType(access));
+
+ addTokenLocation(edge, location);
+}
+
void Storage::logGraph() const
{
std::stringstream str;
@@ -210,7 +224,13 @@ TokenLocationCollection Storage::getTokenLocationsForTokenId(Id id) const
{
TokenLocationCollection ret;
- std::vector locationIds = m_graph.getTokenById(id)->getLocationIds();
+ Token* token = m_graph.getTokenById(id);
+ if (!token)
+ {
+ return ret;
+ }
+
+ std::vector locationIds = token->getLocationIds();
for (Id locationId: locationIds)
{
TokenLocation* location = m_locationCollection.findTokenLocationById(locationId);
diff --git a/src/lib/data/Storage.h b/src/lib/data/Storage.h
index ca0af783..95ef4663 100644
--- a/src/lib/data/Storage.h
+++ b/src/lib/data/Storage.h
@@ -42,6 +42,9 @@ public:
virtual void onEnumParsed(const ParseLocation& location, const std::string& fullName, AccessType access);
virtual void onEnumFieldParsed(const ParseLocation& location, const std::string& fullName);
+ virtual void onInheritanceParsed(
+ const ParseLocation& location, const std::string& fullName, const std::string& baseName, AccessType access);
+
void logGraph() const;
void logLocations() const;
diff --git a/src/lib/data/graph/Edge.cpp b/src/lib/data/graph/Edge.cpp
index 953c9abc..486200cb 100644
--- a/src/lib/data/graph/Edge.cpp
+++ b/src/lib/data/graph/Edge.cpp
@@ -93,6 +93,8 @@ std::string Edge::getTypeString() const
return "has return type";
case EDGE_PARAMETER_OF:
return "has parameter of type";
+ case EDGE_INHERITANCE:
+ return "is derived from";
default:
LOG_ERROR("TypeString not implemented for edge type.");
}
diff --git a/src/lib/data/parser/ParserClient.h b/src/lib/data/parser/ParserClient.h
index e522a601..f1b5bff4 100644
--- a/src/lib/data/parser/ParserClient.h
+++ b/src/lib/data/parser/ParserClient.h
@@ -47,6 +47,9 @@ public:
virtual void onEnumParsed(const ParseLocation& location, const std::string& fullName, AccessType access) = 0;
virtual void onEnumFieldParsed(const ParseLocation& location, const std::string& fullName) = 0;
+
+ virtual void onInheritanceParsed(
+ const ParseLocation& location, const std::string& fullName, const std::string& baseName, AccessType access) = 0;
};
#endif // PARSER_CLIENT_H
diff --git a/src/lib/data/parser/cxx/ASTVisitor.cpp b/src/lib/data/parser/cxx/ASTVisitor.cpp
index 3beddcd4..d4357594 100644
--- a/src/lib/data/parser/cxx/ASTVisitor.cpp
+++ b/src/lib/data/parser/cxx/ASTVisitor.cpp
@@ -18,7 +18,7 @@ bool ASTVisitor::VisitTypedefDecl(const clang::TypedefDecl* declaration)
if (hasValidLocation(declaration))
{
m_client->onTypedefParsed(
- getParseLocation(declaration),
+ getParseLocation(declaration->getSourceRange()),
declaration->getQualifiedNameAsString(),
declaration->getUnderlyingType().getAsString(),
convertAccessType(declaration->getAccess())
@@ -35,15 +35,28 @@ bool ASTVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl* declaration)
if (declaration->isClass())
{
m_client->onClassParsed(
- getParseLocation(declaration),
+ getParseLocation(declaration->getSourceRange()),
declaration->getQualifiedNameAsString(),
convertAccessType(declaration->getAccess())
);
+
+ if (declaration->hasDefinition() && declaration->getNumBases())
+ {
+ for (const auto& it : declaration->bases())
+ {
+ m_client->onInheritanceParsed(
+ getParseLocation(it.getSourceRange()),
+ declaration->getQualifiedNameAsString(),
+ getTypeName(it.getType()),
+ convertAccessType(it.getAccessSpecifier())
+ );
+ }
+ }
}
else if (declaration->isStruct())
{
m_client->onStructParsed(
- getParseLocation(declaration),
+ getParseLocation(declaration->getSourceRange()),
declaration->getQualifiedNameAsString(),
convertAccessType(declaration->getAccess())
);
@@ -67,12 +80,15 @@ bool ASTVisitor::VisitVarDecl(clang::VarDecl* declaration)
if (access == clang::AS_none)
{
- m_client->onGlobalVariableParsed(getParseLocation(declaration), getParseVariable(declaration));
+ m_client->onGlobalVariableParsed(
+ getParseLocation(declaration->getSourceRange()),
+ getParseVariable(declaration)
+ );
}
else
{
m_client->onFieldParsed(
- getParseLocation(declaration),
+ getParseLocation(declaration->getSourceRange()),
getParseVariable(declaration),
convertAccessType(declaration->getAccess())
);
@@ -87,7 +103,7 @@ bool ASTVisitor::VisitFieldDecl(clang::FieldDecl* declaration)
if (hasValidLocation(declaration))
{
m_client->onFieldParsed(
- getParseLocation(declaration),
+ getParseLocation(declaration->getSourceRange()),
getParseVariable(declaration),
convertAccessType(declaration->getAccess())
);
@@ -107,7 +123,7 @@ bool ASTVisitor::VisitFunctionDecl(clang::FunctionDecl* declaration)
if (hasValidLocation(declaration))
{
m_client->onFunctionParsed(
- getParseLocation(declaration),
+ getParseLocation(declaration->getSourceRange()),
declaration->getQualifiedNameAsString(),
getTypeName(declaration->getReturnType()),
getParameters(declaration)
@@ -132,7 +148,7 @@ bool ASTVisitor::VisitCXXMethodDecl(clang::CXXMethodDecl* declaration)
}
m_client->onMethodParsed(
- getParseLocation(declaration),
+ getParseLocation(declaration->getSourceRange()),
declaration->getQualifiedNameAsString(),
getTypeName(declaration->getReturnType()),
getParameters(declaration),
@@ -150,7 +166,10 @@ bool ASTVisitor::VisitNamespaceDecl(clang::NamespaceDecl* declaration)
{
if (hasValidLocation(declaration))
{
- m_client->onNamespaceParsed(getParseLocation(declaration), declaration->getQualifiedNameAsString());
+ m_client->onNamespaceParsed(
+ getParseLocation(declaration->getSourceRange()),
+ declaration->getQualifiedNameAsString()
+ );
}
return true;
@@ -161,7 +180,7 @@ bool ASTVisitor::VisitEnumDecl(clang::EnumDecl* declaration)
if (hasValidLocation(declaration))
{
m_client->onEnumParsed(
- getParseLocation(declaration),
+ getParseLocation(declaration->getSourceRange()),
declaration->getQualifiedNameAsString(),
convertAccessType(declaration->getAccess())
);
@@ -174,7 +193,10 @@ bool ASTVisitor::VisitEnumConstantDecl(clang::EnumConstantDecl* declaration)
{
if (hasValidLocation(declaration))
{
- m_client->onEnumFieldParsed(getParseLocation(declaration), declaration->getQualifiedNameAsString());
+ m_client->onEnumFieldParsed(
+ getParseLocation(declaration->getSourceRange()),
+ declaration->getQualifiedNameAsString()
+ );
}
return true;
@@ -186,9 +208,8 @@ bool ASTVisitor::hasValidLocation(const clang::Decl* declaration) const
return location.isValid() && m_context->getSourceManager().isWrittenInMainFile(location);
}
-ParseLocation ASTVisitor::getParseLocation(const clang::Decl* declaration) const
+ParseLocation ASTVisitor::getParseLocation(const clang::SourceRange& sourceRange) const
{
- const clang::SourceRange& sourceRange = declaration->getSourceRange();
const clang::SourceManager& sourceManager = m_context->getSourceManager();
const clang::PresumedLoc& presumedBegin = sourceManager.getPresumedLoc(sourceRange.getBegin());
diff --git a/src/lib/data/parser/cxx/ASTVisitor.h b/src/lib/data/parser/cxx/ASTVisitor.h
index 05bb77cc..cd8cbcc8 100644
--- a/src/lib/data/parser/cxx/ASTVisitor.h
+++ b/src/lib/data/parser/cxx/ASTVisitor.h
@@ -23,7 +23,7 @@ public:
// }
virtual bool VisitTypedefDecl(const clang::TypedefDecl* declaration); // typedefs
- virtual bool VisitCXXRecordDecl(clang::CXXRecordDecl* declaration); // classes and structs
+ virtual bool VisitCXXRecordDecl(clang::CXXRecordDecl* declaration); // structs, classes and inheritance
virtual bool VisitVarDecl(clang::VarDecl* declaration); // global variables and static fields
virtual bool VisitFieldDecl(clang::FieldDecl* declaration); // fields
virtual bool VisitFunctionDecl(clang::FunctionDecl* declaration); // functions
@@ -34,7 +34,7 @@ public:
private:
bool hasValidLocation(const clang::Decl* declaration) const;
- ParseLocation getParseLocation(const clang::Decl* declaration) const;
+ ParseLocation getParseLocation(const clang::SourceRange& sourceRange) const;
ParseVariable getParseVariable(clang::ValueDecl* declaration) const;
std::vector getParameters(clang::FunctionDecl* declaration) const;
std::string getTypeName(const clang::QualType& type) const;
diff --git a/src/test/CxxParserTestSuite.h b/src/test/CxxParserTestSuite.h
index 16c440f4..bb86bf60 100644
--- a/src/test/CxxParserTestSuite.h
+++ b/src/test/CxxParserTestSuite.h
@@ -477,6 +477,67 @@ public:
TS_ASSERT_EQUALS(client->typedefs[0], "test::TestStruct -> globalTestStruct <5:1 5:26>");
}
+ void test_cxx_parser_finds_public_inheritance()
+ {
+ std::shared_ptr client = std::make_shared();
+ CxxParser parser(client);
+ std::string text =
+ "class A {};\n"
+ "class B : public A {};\n";
+
+ parser.parseFile(TextAccess::createFromString(text));
+
+ TS_ASSERT_EQUALS(client->inheritances.size(), 1);
+ TS_ASSERT_EQUALS(client->inheritances[0], "B : public A <2:11 2:18>");
+ }
+
+ void test_cxx_parser_finds_protected_inheritance()
+ {
+ std::shared_ptr client = std::make_shared();
+ CxxParser parser(client);
+ std::string text =
+ "class A {};\n"
+ "class B : protected A {};\n";
+
+ parser.parseFile(TextAccess::createFromString(text));
+
+ TS_ASSERT_EQUALS(client->inheritances.size(), 1);
+ TS_ASSERT_EQUALS(client->inheritances[0], "B : protected A <2:11 2:21>");
+ }
+
+ void test_cxx_parser_finds_private_inheritance()
+ {
+ std::shared_ptr client = std::make_shared();
+ CxxParser parser(client);
+ std::string text =
+ "class A {};\n"
+ "class B : private A {};\n";
+
+ parser.parseFile(TextAccess::createFromString(text));
+
+ TS_ASSERT_EQUALS(client->inheritances.size(), 1);
+ TS_ASSERT_EQUALS(client->inheritances[0], "B : private A <2:11 2:19>");
+ }
+
+ void test_cxx_parser_finds_multiple_inheritance()
+ {
+ std::shared_ptr client = std::make_shared();
+ CxxParser parser(client);
+ std::string text =
+ "class A {};\n"
+ "class B {};\n"
+ "class C\n"
+ " : public A\n"
+ " , private B\n"
+ "{};\n";
+
+ parser.parseFile(TextAccess::createFromString(text));
+
+ TS_ASSERT_EQUALS(client->inheritances.size(), 2);
+ TS_ASSERT_EQUALS(client->inheritances[0], "C : public A <4:4 4:11>");
+ TS_ASSERT_EQUALS(client->inheritances[1], "C : private B <5:4 5:12>");
+ }
+
void test_cxx_parser_parses_multiple_files()
{
std::shared_ptr client = std::make_shared();
@@ -569,6 +630,13 @@ private:
enumFields.push_back(addLocationSuffix(fullName, location));
}
+ virtual void onInheritanceParsed(
+ const ParseLocation& location, const std::string& fullName, const std::string& baseName, AccessType access)
+ {
+ std::string str = fullName + " : " + addAccessPrefix(baseName, access);
+ inheritances.push_back(addLocationSuffix(str, location));
+ }
+
std::vector typedefs;
std::vector classes;
std::vector enums;
@@ -579,6 +647,7 @@ private:
std::vector methods;
std::vector namespaces;
std::vector structs;
+ std::vector inheritances;
private:
std::string addAccessPrefix(const std::string& str, AccessType access)