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)