diff --git a/bin/app/data/src/main.cpp b/bin/app/data/src/main.cpp index 825e2010..0dd91d90 100644 --- a/bin/app/data/src/main.cpp +++ b/bin/app/data/src/main.cpp @@ -7,8 +7,8 @@ int diff(int a, int b); int main() { - int a = 6; - int b = 7; + int a = sum(1, 2); + int b = diff(a, 3); int c = a * b; return 0; } diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index b7a24136..f9ed0555 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -5,6 +5,10 @@ add_files( data/parser/cxx/ASTAction.h data/parser/cxx/ASTActionFactory.cpp data/parser/cxx/ASTActionFactory.h + data/parser/cxx/ASTBodyVisitor.cpp + data/parser/cxx/ASTBodyVisitor.h + data/parser/cxx/ASTBodyVisitorClient.cpp + data/parser/cxx/ASTBodyVisitorClient.h data/parser/cxx/ASTConsumer.cpp data/parser/cxx/ASTConsumer.h data/parser/cxx/ASTVisitor.cpp diff --git a/src/lib/data/Storage.cpp b/src/lib/data/Storage.cpp index 79ac2187..c2045655 100644 --- a/src/lib/data/Storage.cpp +++ b/src/lib/data/Storage.cpp @@ -176,6 +176,18 @@ void Storage::onInheritanceParsed( addTokenLocation(edge, location); } +void Storage::onCallParsed(const ParseLocation& location, const std::string& callerName, const std::string& calleeName) +{ + log("call", callerName + " -> " + calleeName, location); + + Node* callerNode = m_graph.createNodeHierarchy(callerName); + Node* calleeNode = m_graph.createNodeHierarchy(calleeName); + + Edge* edge = m_graph.createEdge(Edge::EDGE_CALL, callerNode, calleeNode); + + addTokenLocation(edge, location); +} + void Storage::logGraph() const { std::stringstream str; diff --git a/src/lib/data/Storage.h b/src/lib/data/Storage.h index 95ef4663..87ceb5f8 100644 --- a/src/lib/data/Storage.h +++ b/src/lib/data/Storage.h @@ -44,6 +44,8 @@ public: virtual void onInheritanceParsed( const ParseLocation& location, const std::string& fullName, const std::string& baseName, AccessType access); + virtual void onCallParsed( + const ParseLocation& location, const std::string& callerName, const std::string& calleeName); void logGraph() const; void logLocations() const; diff --git a/src/lib/data/graph/Edge.cpp b/src/lib/data/graph/Edge.cpp index 486200cb..89cef4a2 100644 --- a/src/lib/data/graph/Edge.cpp +++ b/src/lib/data/graph/Edge.cpp @@ -95,6 +95,8 @@ std::string Edge::getTypeString() const return "has parameter of type"; case EDGE_INHERITANCE: return "is derived from"; + case EDGE_CALL: + return "calls"; 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 f1b5bff4..087ce19c 100644 --- a/src/lib/data/parser/ParserClient.h +++ b/src/lib/data/parser/ParserClient.h @@ -50,6 +50,8 @@ public: virtual void onInheritanceParsed( const ParseLocation& location, const std::string& fullName, const std::string& baseName, AccessType access) = 0; + virtual void onCallParsed( + const ParseLocation& location, const std::string& callerName, const std::string& calleeName) = 0; }; #endif // PARSER_CLIENT_H diff --git a/src/lib/data/parser/cxx/ASTBodyVisitor.cpp b/src/lib/data/parser/cxx/ASTBodyVisitor.cpp new file mode 100644 index 00000000..a282eeae --- /dev/null +++ b/src/lib/data/parser/cxx/ASTBodyVisitor.cpp @@ -0,0 +1,41 @@ +#include "data/parser/cxx/ASTBodyVisitor.h" + +ASTBodyVisitor::ASTBodyVisitor(ASTBodyVisitorClient* client, clang::NamedDecl* parentDecl) + : m_client(client) + , m_parentDecl(parentDecl) +{ +} + +ASTBodyVisitor::~ASTBodyVisitor() +{ +} + +void ASTBodyVisitor::VisitStmt(clang::Stmt* stmt) +{ + VisitChildren(stmt); +} + +void ASTBodyVisitor::VisitChildren(clang::Stmt* stmt) +{ + for (clang::Stmt::child_range it = stmt->children(); it; it++) + { + if (*it) + { + static_cast(this)->Visit(*it); + } + } +} + +void ASTBodyVisitor::VisitCallExpr(clang::CallExpr* expr) +{ + m_client->VisitCallExprInDeclBody(m_parentDecl, expr); + + VisitStmt(expr); +} + +void ASTBodyVisitor::VisitCXXConstructExpr(clang::CXXConstructExpr* expr) +{ + m_client->VisitCXXConstructExprInDeclBody(m_parentDecl, expr); + + VisitStmt(expr); +} diff --git a/src/lib/data/parser/cxx/ASTBodyVisitor.h b/src/lib/data/parser/cxx/ASTBodyVisitor.h new file mode 100644 index 00000000..b518812c --- /dev/null +++ b/src/lib/data/parser/cxx/ASTBodyVisitor.h @@ -0,0 +1,23 @@ +#ifndef AST_BODY_VISITOR +#define AST_BODY_VISITOR + +#include "clang/AST/StmtVisitor.h" +#include "data/parser/cxx/ASTBodyVisitorClient.h" + +class ASTBodyVisitor: public clang::StmtVisitor +{ +public: + ASTBodyVisitor(ASTBodyVisitorClient* client, clang::NamedDecl* parentDecl); + virtual ~ASTBodyVisitor(); + + void VisitStmt(clang::Stmt* stmt); + void VisitChildren(clang::Stmt* stmt); + void VisitCallExpr(clang::CallExpr* expr); + void VisitCXXConstructExpr(clang::CXXConstructExpr* expr); + +private: + ASTBodyVisitorClient* m_client; + clang::NamedDecl* m_parentDecl; +}; + +#endif // AST_BODY_VISITOR diff --git a/src/lib/data/parser/cxx/ASTBodyVisitorClient.cpp b/src/lib/data/parser/cxx/ASTBodyVisitorClient.cpp new file mode 100644 index 00000000..cc51496e --- /dev/null +++ b/src/lib/data/parser/cxx/ASTBodyVisitorClient.cpp @@ -0,0 +1,9 @@ +#include "data/parser/cxx/ASTBodyVisitorClient.h" + +ASTBodyVisitorClient::ASTBodyVisitorClient() +{ +} + +ASTBodyVisitorClient::~ASTBodyVisitorClient() +{ +} diff --git a/src/lib/data/parser/cxx/ASTBodyVisitorClient.h b/src/lib/data/parser/cxx/ASTBodyVisitorClient.h new file mode 100644 index 00000000..3a98b3e7 --- /dev/null +++ b/src/lib/data/parser/cxx/ASTBodyVisitorClient.h @@ -0,0 +1,18 @@ +#ifndef AST_BODY_VISITOR_CLIENT_H +#define AST_BODY_VISITOR_CLIENT_H + +#include "clang/AST/Decl.h" +#include "clang/AST/Expr.h" +#include "clang/AST/ExprCXX.h" + +class ASTBodyVisitorClient +{ +public: + ASTBodyVisitorClient(); + virtual ~ASTBodyVisitorClient(); + + virtual void VisitCallExprInDeclBody(clang::NamedDecl* decl, clang::CallExpr* expr) = 0; + virtual void VisitCXXConstructExprInDeclBody(clang::NamedDecl* decl, clang::CXXConstructExpr* expr) = 0; +}; + +#endif // AST_BODY_VISITOR_CLIENT_H diff --git a/src/lib/data/parser/cxx/ASTVisitor.cpp b/src/lib/data/parser/cxx/ASTVisitor.cpp index d4357594..8024f2dd 100644 --- a/src/lib/data/parser/cxx/ASTVisitor.cpp +++ b/src/lib/data/parser/cxx/ASTVisitor.cpp @@ -1,5 +1,6 @@ #include "data/parser/cxx/ASTVisitor.h" +#include "data/parser/cxx/ASTBodyVisitor.h" #include "data/parser/ParseLocation.h" #include "data/parser/ParseVariable.h" @@ -13,6 +14,11 @@ ASTVisitor::~ASTVisitor() { } +bool ASTVisitor::VisitStmt(const clang::Stmt* statement) +{ + return true; +} + bool ASTVisitor::VisitTypedefDecl(const clang::TypedefDecl* declaration) { if (hasValidLocation(declaration)) @@ -84,6 +90,12 @@ bool ASTVisitor::VisitVarDecl(clang::VarDecl* declaration) getParseLocation(declaration->getSourceRange()), getParseVariable(declaration) ); + + if (declaration->getInit()) + { + ASTBodyVisitor bodyVisitor(this, declaration); + bodyVisitor.Visit(declaration->getInit()); + } } else { @@ -128,6 +140,12 @@ bool ASTVisitor::VisitFunctionDecl(clang::FunctionDecl* declaration) getTypeName(declaration->getReturnType()), getParameters(declaration) ); + + if (declaration->hasBody()) + { + ASTBodyVisitor bodyVisitor(this, declaration); + bodyVisitor.Visit(declaration->getBody()); + } } return true; @@ -157,6 +175,29 @@ bool ASTVisitor::VisitCXXMethodDecl(clang::CXXMethodDecl* declaration) declaration->isConst(), declaration->isStatic() ); + + if (declaration->hasBody()) + { + ASTBodyVisitor bodyVisitor(this, declaration); + bodyVisitor.Visit(declaration->getBody()); + } + } + + return true; +} + +bool ASTVisitor::VisitCXXConstructorDecl(clang::CXXConstructorDecl* declaration) +{ + if (hasValidLocation(declaration)) + { + for (clang::CXXConstructorDecl::init_const_iterator it = declaration->init_begin(); it != declaration->init_end(); it++) + { + if ((*it)->getInit()) + { + ASTBodyVisitor bodyVisitor(this, declaration); + bodyVisitor.Visit((*it)->getInit()); + } + } } return true; @@ -202,6 +243,38 @@ bool ASTVisitor::VisitEnumConstantDecl(clang::EnumConstantDecl* declaration) return true; } +void ASTVisitor::VisitCallExprInDeclBody(clang::NamedDecl* decl, clang::CallExpr* expr) +{ + // if (clang::FunctionDecl *CalleeDecl = CE->getDirectCallee()) + // { + // return CalleeDecl; + // } + + // clang::Expr *CEE = CE->getCallee()->IgnoreParenImpCasts(); + // if (clang::BlockExpr *Block = clang::dyn_cast(CEE)) + // { + // NumBlockCallEdges++; + // return Block->getBlockDecl(); + // } + + // return nullptr; + + m_client->onCallParsed( + getParseLocation(expr->getSourceRange()), + decl->getQualifiedNameAsString(), + expr->getDirectCallee()->getQualifiedNameAsString() + ); +} + +void ASTVisitor::VisitCXXConstructExprInDeclBody(clang::NamedDecl* decl, clang::CXXConstructExpr* expr) +{ + m_client->onCallParsed( + getParseLocation(expr->getSourceRange()), + decl->getQualifiedNameAsString(), + expr->getConstructor()->getQualifiedNameAsString() + ); +} + bool ASTVisitor::hasValidLocation(const clang::Decl* declaration) const { const clang::SourceLocation& location = declaration->getLocStart(); diff --git a/src/lib/data/parser/cxx/ASTVisitor.h b/src/lib/data/parser/cxx/ASTVisitor.h index cd8cbcc8..8414b0af 100644 --- a/src/lib/data/parser/cxx/ASTVisitor.h +++ b/src/lib/data/parser/cxx/ASTVisitor.h @@ -6,32 +6,42 @@ #include "clang/AST/ASTContext.h" #include "clang/AST/RecursiveASTVisitor.h" +#include "data/parser/cxx/ASTBodyVisitorClient.h" #include "data/parser/ParserClient.h" -class ASTVisitor: public clang::RecursiveASTVisitor +class ASTVisitor + : public clang::RecursiveASTVisitor + , public ASTBodyVisitorClient { public: ASTVisitor(clang::ASTContext* context, std::shared_ptr client); virtual ~ASTVisitor(); // Left for debugging purposes. Uncomment to see a colored ast-dump of the parsed file. - // virtual bool VisitDecl(clang::Decl* declaration) + // virtual bool VisitTranslationUnitDecl(clang::TranslationUnitDecl* decl) // { - // // declaration->print(llvm::outs()); - // declaration->dump(); - // return false; + // decl->dump(); + // return true; // } + // RecursiveASTVisitor implementation + virtual bool VisitStmt(const clang::Stmt* statement); // avoid visiting + virtual bool VisitTypedefDecl(const clang::TypedefDecl* declaration); // typedefs 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 virtual bool VisitCXXMethodDecl(clang::CXXMethodDecl* declaration); // methods + virtual bool VisitCXXConstructorDecl(clang::CXXConstructorDecl* declaration); // initialization list virtual bool VisitNamespaceDecl(clang::NamespaceDecl* declaration); // namespaces virtual bool VisitEnumDecl(clang::EnumDecl* declaration); // enums virtual bool VisitEnumConstantDecl(clang::EnumConstantDecl* declaration); // enum fields + // ASTBodyVisitorClient implementation + virtual void VisitCallExprInDeclBody(clang::NamedDecl* decl, clang::CallExpr* expr); // calls + virtual void VisitCXXConstructExprInDeclBody(clang::NamedDecl* decl, clang::CXXConstructExpr* expr); // constructor calls + private: bool hasValidLocation(const clang::Decl* declaration) const; ParseLocation getParseLocation(const clang::SourceRange& sourceRange) const; diff --git a/src/test/CxxParserTestSuite.h b/src/test/CxxParserTestSuite.h index bb86bf60..03ed9f3b 100644 --- a/src/test/CxxParserTestSuite.h +++ b/src/test/CxxParserTestSuite.h @@ -538,6 +538,262 @@ public: TS_ASSERT_EQUALS(client->inheritances[1], "C : private B <5:4 5:12>"); } + void test_cxx_parser_finds_call_in_function() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + std::string text = + "int sum(int a, int b)\n" + "{\n" + " return a + b;\n" + "}\n" + "int main()\n" + "{\n" + " sum(1, 2);\n" + "}\n"; + + parser.parseFile(TextAccess::createFromString(text)); + + TS_ASSERT_EQUALS(client->calls.size(), 1); + TS_ASSERT_EQUALS(client->calls[0], "main -> sum <7:2 7:10>"); + } + + void test_cxx_parser_finds_call_within_call_in_function() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + std::string text = + "int sum(int a, int b)\n" + "{\n" + " return a + b;\n" + "}\n" + "int main()\n" + "{\n" + " return sum(1, sum(2, 3));\n" + "}\n"; + + parser.parseFile(TextAccess::createFromString(text)); + + TS_ASSERT_EQUALS(client->calls.size(), 2); + TS_ASSERT_EQUALS(client->calls[0], "main -> sum <7:9 7:25>"); + TS_ASSERT_EQUALS(client->calls[1], "main -> sum <7:16 7:24>"); + } + + void test_cxx_parser_finds_call_in_method() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + std::string text = + "int sum(int a, int b)\n" + "{\n" + " return a + b;\n" + "}\n" + "class App\n" + "{\n" + " int main()\n" + " {\n" + " return sum(1, 2);\n" + " }\n" + "};\n"; + + parser.parseFile(TextAccess::createFromString(text)); + + TS_ASSERT_EQUALS(client->calls.size(), 1); + TS_ASSERT_EQUALS(client->calls[0], "App::main -> sum <9:10 9:18>"); + } + + void test_cxx_parser_finds_constructor_call() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + std::string text = + "class App\n" + "{\n" + "public:\n" + " App() {}\n" + "};\n" + "int main()\n" + "{\n" + " App app;\n" + "}\n"; + + parser.parseFile(TextAccess::createFromString(text)); + + TS_ASSERT_EQUALS(client->calls.size(), 1); + TS_ASSERT_EQUALS(client->calls[0], "main -> App::App <8:6 8:6>"); + } + + void test_cxx_parser_finds_constructor_without_definition_call() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + std::string text = + "class App\n" + "{\n" + "};\n" + "int main()\n" + "{\n" + " App app;\n" + "}\n"; + + parser.parseFile(TextAccess::createFromString(text)); + + TS_ASSERT_EQUALS(client->calls.size(), 1); + TS_ASSERT_EQUALS(client->calls[0], "main -> App::App <6:6 6:6>"); + } + + void test_cxx_parser_finds_constructor_call_of_field() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + std::string text = + "class Item\n" + "{\n" + "};\n" + "class App\n" + "{\n" + "public:\n" + " App() {}\n" + " Item item;\n" + "};\n"; + + parser.parseFile(TextAccess::createFromString(text)); + + TS_ASSERT_EQUALS(client->calls.size(), 1); + TS_ASSERT_EQUALS(client->calls[0], "App::App -> Item::Item <7:2 7:2>"); + } + + void test_cxx_parser_finds_constructor_call_of_field_in_initialization_list() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + std::string text = + "class Item\n" + "{\n" + "public:\n" + " Item(int n) {}\n" + "};\n" + "class App\n" + "{\n" + " App()\n" + " : item(1)" + " {}\n" + " Item item;\n" + "};\n"; + + parser.parseFile(TextAccess::createFromString(text)); + + TS_ASSERT_EQUALS(client->calls.size(), 1); + TS_ASSERT_EQUALS(client->calls[0], "App::App -> Item::Item <9:5 9:11>"); + } + + void test_cxx_parser_finds_function_call_within_constructor_call_of_field_in_initialization_list() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + std::string text = + "int one() { return 1; }\n" + "class Item\n" + "{\n" + "public:\n" + " Item(int n) {}\n" + "};\n" + "class App\n" + "{\n" + " App()\n" + " : item(one())" + " {}\n" + " Item item;\n" + "};\n"; + + parser.parseFile(TextAccess::createFromString(text)); + + TS_ASSERT_EQUALS(client->calls.size(), 2); + TS_ASSERT_EQUALS(client->calls[0], "App::App -> Item::Item <10:5 10:15>"); + TS_ASSERT_EQUALS(client->calls[1], "App::App -> one <10:10 10:14>"); + } + + void test_cxx_parser_finds_copy_constructor_call() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + std::string text = + "class App\n" + "{\n" + "public:\n" + " App() {}\n" + " App(const App& other) {}\n" + "};\n" + "int main()\n" + "{\n" + " App app;\n" + " App app2(app);\n" + "}\n"; + + parser.parseFile(TextAccess::createFromString(text)); + + TS_ASSERT_EQUALS(client->calls.size(), 2); + TS_ASSERT_EQUALS(client->calls[0], "main -> App::App <9:6 9:6>"); + TS_ASSERT_EQUALS(client->calls[1], "main -> App::App <10:6 10:14>"); + } + + void test_cxx_parser_finds_global_constructor_call() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + std::string text = + "class App\n" + "{\n" + "public:\n" + " App() {}\n" + "};\n" + "App app;\n"; + + parser.parseFile(TextAccess::createFromString(text)); + + TS_ASSERT_EQUALS(client->calls.size(), 1); + TS_ASSERT_EQUALS(client->calls[0], "app -> App::App <6:5 6:5>"); + } + + void test_cxx_parser_finds_global_function_call() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + std::string text = + "int one() { return 1; }\n" + "int a = one();\n"; + + parser.parseFile(TextAccess::createFromString(text)); + + TS_ASSERT_EQUALS(client->calls.size(), 1); + TS_ASSERT_EQUALS(client->calls[0], "a -> one <2:9 2:13>"); + } + + void test_cxx_parser_finds_operator_call() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + std::string text = + "class App\n" + "{\n" + "public:\n" + " void operator+(int a)\n" + " {\n" + " }\n" + "};\n" + "int main()\n" + "{\n" + " App app;\n" + " app + 2;\n" + "}\n"; + + parser.parseFile(TextAccess::createFromString(text)); + + TS_ASSERT_EQUALS(client->calls.size(), 2); + TS_ASSERT_EQUALS(client->calls[0], "main -> App::App <10:6 10:6>"); + TS_ASSERT_EQUALS(client->calls[1], "main -> App::operator+ <11:2 11:8>"); + } + void test_cxx_parser_parses_multiple_files() { std::shared_ptr client = std::make_shared(); @@ -637,6 +893,12 @@ private: inheritances.push_back(addLocationSuffix(str, location)); } + virtual void onCallParsed( + const ParseLocation& location, const std::string& callerName, const std::string& calleeName) + { + calls.push_back(addLocationSuffix(callerName + " -> " + calleeName, location)); + } + std::vector typedefs; std::vector classes; std::vector enums; @@ -648,6 +910,7 @@ private: std::vector namespaces; std::vector structs; std::vector inheritances; + std::vector calls; private: std::string addAccessPrefix(const std::string& str, AccessType access)