From af54da4ce466dd03d849f3cfb58382dc91337e3e Mon Sep 17 00:00:00 2001 From: Eberhard Graether Date: Tue, 8 Jul 2014 16:38:40 +0200 Subject: [PATCH] data: parsing and saving calls This change parses calls in CxxParser by using the class ASTBodyVisitor which traverses the Stmts and Exprs in the body of the function and method declarations. The calls include: * calls in functions * calls in methods * constructor calls * constructor calls in initialization lists * implicit constructor calls of fields * calls to operators * global constructor calls of global variables * global function calls for global variables The calls don't include: * destructor calls * some calls to implicit constructors and copy constructors fortune cookie message = Don't be afraid to take that big step. --- bin/app/data/src/main.cpp | 4 +- src/lib/CMakeLists.txt | 4 + src/lib/data/Storage.cpp | 12 + src/lib/data/Storage.h | 2 + src/lib/data/graph/Edge.cpp | 2 + src/lib/data/parser/ParserClient.h | 2 + src/lib/data/parser/cxx/ASTBodyVisitor.cpp | 41 +++ src/lib/data/parser/cxx/ASTBodyVisitor.h | 23 ++ .../data/parser/cxx/ASTBodyVisitorClient.cpp | 9 + .../data/parser/cxx/ASTBodyVisitorClient.h | 18 ++ src/lib/data/parser/cxx/ASTVisitor.cpp | 73 +++++ src/lib/data/parser/cxx/ASTVisitor.h | 20 +- src/test/CxxParserTestSuite.h | 263 ++++++++++++++++++ 13 files changed, 466 insertions(+), 7 deletions(-) create mode 100644 src/lib/data/parser/cxx/ASTBodyVisitor.cpp create mode 100644 src/lib/data/parser/cxx/ASTBodyVisitor.h create mode 100644 src/lib/data/parser/cxx/ASTBodyVisitorClient.cpp create mode 100644 src/lib/data/parser/cxx/ASTBodyVisitorClient.h 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)