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.
This commit is contained in:
Eberhard Graether
2014-07-08 16:38:40 +02:00
parent 0f60a9a556
commit af54da4ce4
13 changed files with 466 additions and 7 deletions
+4
View File
@@ -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
+12
View File
@@ -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;
+2
View File
@@ -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;
+2
View File
@@ -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.");
}
+2
View File
@@ -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
@@ -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<ASTBodyVisitor*>(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);
}
+23
View File
@@ -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<ASTBodyVisitor>
{
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
@@ -0,0 +1,9 @@
#include "data/parser/cxx/ASTBodyVisitorClient.h"
ASTBodyVisitorClient::ASTBodyVisitorClient()
{
}
ASTBodyVisitorClient::~ASTBodyVisitorClient()
{
}
@@ -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
+73
View File
@@ -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<clang::BlockExpr>(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();
+15 -5
View File
@@ -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<ASTVisitor>
class ASTVisitor
: public clang::RecursiveASTVisitor<ASTVisitor>
, public ASTBodyVisitorClient
{
public:
ASTVisitor(clang::ASTContext* context, std::shared_ptr<ParserClient> 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;
+263
View File
@@ -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<TestParserClient> client = std::make_shared<TestParserClient>();
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<TestParserClient> client = std::make_shared<TestParserClient>();
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<TestParserClient> client = std::make_shared<TestParserClient>();
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<TestParserClient> client = std::make_shared<TestParserClient>();
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<TestParserClient> client = std::make_shared<TestParserClient>();
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<TestParserClient> client = std::make_shared<TestParserClient>();
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<TestParserClient> client = std::make_shared<TestParserClient>();
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<TestParserClient> client = std::make_shared<TestParserClient>();
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<TestParserClient> client = std::make_shared<TestParserClient>();
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<TestParserClient> client = std::make_shared<TestParserClient>();
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<TestParserClient> client = std::make_shared<TestParserClient>();
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<TestParserClient> client = std::make_shared<TestParserClient>();
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<TestParserClient> client = std::make_shared<TestParserClient>();
@@ -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<std::string> typedefs;
std::vector<std::string> classes;
std::vector<std::string> enums;
@@ -648,6 +910,7 @@ private:
std::vector<std::string> namespaces;
std::vector<std::string> structs;
std::vector<std::string> inheritances;
std::vector<std::string> calls;
private:
std::string addAccessPrefix(const std::string& str, AccessType access)