From 38c8aaebe80a3c2b8756bc8386d39854372d30ec Mon Sep 17 00:00:00 2001 From: Eberhard Graether Date: Mon, 19 May 2014 12:51:00 +0200 Subject: [PATCH] data: Extended CxxParser to detect elements Detects most elements in the code, currently classes, structs, global variables, fields, functions, methods, namespaces and enums. The structs ParseLocation and ParseVariable are helper objects to transmit information to the ParserClient. Currently the parsed information only gets logged in the Storage. review id = 15 fortune cookie message = In dreams and in life, nothing is impossible. --- bin/data/test_code.cpp | 53 ++++- bin/data/test_header.h | 6 +- src/lib/CMakeLists.txt | 5 +- src/lib/data/Storage.cpp | 59 +++++- src/lib/data/Storage.h | 23 ++- src/lib/data/parser/ParseLocation.cpp | 8 + src/lib/data/parser/ParseLocation.h | 15 ++ src/lib/data/parser/ParseObject.h | 25 --- src/lib/data/parser/ParseVariable.cpp | 9 + src/lib/data/parser/ParseVariable.h | 16 ++ src/lib/data/parser/ParserClient.h | 35 +++- src/lib/data/parser/cxx/ASTVisitor.cpp | 254 ++++++++++++++++++++++- src/lib/data/parser/cxx/ASTVisitor.h | 24 ++- src/lib/data/parser/cxx/CxxParser.cpp | 6 +- src/test/CxxParserTestSuite.h | 268 +++++++++++++++++++++++-- 15 files changed, 738 insertions(+), 68 deletions(-) create mode 100644 src/lib/data/parser/ParseLocation.cpp create mode 100644 src/lib/data/parser/ParseLocation.h delete mode 100644 src/lib/data/parser/ParseObject.h create mode 100644 src/lib/data/parser/ParseVariable.cpp create mode 100644 src/lib/data/parser/ParseVariable.h diff --git a/bin/data/test_code.cpp b/bin/data/test_code.cpp index c6e9659d..65cefe91 100644 --- a/bin/data/test_code.cpp +++ b/bin/data/test_code.cpp @@ -1,12 +1,61 @@ #include "test_header.h" +char* name; +H g; + +int ceil(float a) +{ + return static_cast(a) + 1; +} + namespace X { - class A; + struct A; + + enum E + { + P, + Q + }; } class B { public: - class C; + class C + { + static const int amount; + }; + + B(); + + virtual ~B() + { + } + +protected: + virtual void process() = 0; + +private: + int getCount() const + { + return count; + } + + const int count; + H h; }; + +B::B() + : count(0) +{ +} + +namespace +{ + int sum(int a, int b) + { + int c = a + b; + return c; + } +} diff --git a/bin/data/test_header.h b/bin/data/test_header.h index 0de35584..e7f19f32 100644 --- a/bin/data/test_header.h +++ b/bin/data/test_header.h @@ -1 +1,5 @@ -class H; +class H +{ +private: + int width; +}; diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index ae58730d..b51fe965 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -42,11 +42,14 @@ add_files( data/access/GraphAccess.cpp data/access/GraphAccess.h - data/parser/ParseObject.h + data/parser/ParseLocation.cpp + data/parser/ParseLocation.h data/parser/Parser.cpp data/parser/Parser.h data/parser/ParserClient.cpp data/parser/ParserClient.h + data/parser/ParseVariable.cpp + data/parser/ParseVariable.h data/Edge.cpp data/Edge.h diff --git a/src/lib/data/Storage.cpp b/src/lib/data/Storage.cpp index 3c8f0ad6..1e8752ba 100644 --- a/src/lib/data/Storage.cpp +++ b/src/lib/data/Storage.cpp @@ -2,6 +2,8 @@ #include +#include "data/parser/ParseLocation.h" +#include "data/parser/ParseVariable.h" #include "utility/logging/logging.h" Storage::Storage() @@ -12,10 +14,61 @@ Storage::~Storage() { } -void Storage::addClass(const ParseObject& object) +void Storage::onClassParsed(const ParseLocation& location, const std::string& fullName, AccessType access) +{ + log("class", fullName, location); +} + +void Storage::onStructParsed(const ParseLocation& location, const std::string& fullName, AccessType access) +{ + log("struct", fullName, location); +} + +void Storage::onGlobalVariableParsed(const ParseLocation& location, const ParseVariable& variable) +{ + log("global", variable.fullName, location); +} + +void Storage::onFieldParsed(const ParseLocation& location, const ParseVariable& variable, AccessType access) +{ + log("field", variable.fullName, location); +} + +void Storage::onFunctionParsed( + const ParseLocation& location, const std::string& fullName, const std::string& returnTypeName, + const std::vector& parameters +) +{ + log("function", fullName, location); +} + +void Storage::onMethodParsed( + const ParseLocation& location, const std::string& fullName, const std::string& returnTypeName, + const std::vector& parameters, AccessType access, AbstractionType abstraction, + bool isConst, bool isStatic +) +{ + log("method", fullName, location); +} + +void Storage::onNamespaceParsed(const ParseLocation& location, const std::string& fullName) +{ + log("namespace", fullName, location); +} + +void Storage::onEnumParsed(const ParseLocation& location, const std::string& fullName, AccessType access) +{ + log("enum", fullName, location); +} + +void Storage::onEnumFieldParsed(const ParseLocation& location, const std::string& fullName) +{ + log("enum field", fullName, location); +} + +void Storage::log(std::string type, std::string str, const ParseLocation& location) const { std::stringstream info; - info << "class " << object.name - << " <" << object.fileName << " " << object.lineNumber << ":" << object.columnNumber << ">"; + info << type << ": " << str << " <" << location.file << " " << location.line << ":" << location.column << ">"; LOG_INFO(info.str()); } diff --git a/src/lib/data/Storage.h b/src/lib/data/Storage.h index 0f85ab27..61560fc3 100644 --- a/src/lib/data/Storage.h +++ b/src/lib/data/Storage.h @@ -12,11 +12,30 @@ class Storage: public ParserClient { public: Storage(); - ~Storage(); + virtual ~Storage(); - virtual void addClass(const ParseObject& object); + virtual void onClassParsed(const ParseLocation& location, const std::string& fullName, AccessType access); + virtual void onStructParsed(const ParseLocation& location, const std::string& fullName, AccessType access); + + virtual void onGlobalVariableParsed(const ParseLocation& location, const ParseVariable& variable); + virtual void onFieldParsed(const ParseLocation& location, const ParseVariable& variable, AccessType access); + + virtual void onFunctionParsed( + const ParseLocation& location, const std::string& fullName, const std::string& returnTypeName, + const std::vector& parameters); + virtual void onMethodParsed( + const ParseLocation& location, const std::string& fullName, const std::string& returnTypeName, + const std::vector& parameters, AccessType access, AbstractionType abstraction, + bool isConst, bool isStatic); + + virtual void onNamespaceParsed(const ParseLocation& location, const std::string& fullName); + + virtual void onEnumParsed(const ParseLocation& location, const std::string& fullName, AccessType access); + virtual void onEnumFieldParsed(const ParseLocation& location, const std::string& fullName); private: + void log(std::string type, std::string str, const ParseLocation& location) const; + Graph m_graph; std::vector > m_textLocationFiles; diff --git a/src/lib/data/parser/ParseLocation.cpp b/src/lib/data/parser/ParseLocation.cpp new file mode 100644 index 00000000..45bf967f --- /dev/null +++ b/src/lib/data/parser/ParseLocation.cpp @@ -0,0 +1,8 @@ +#include "data/parser/ParseLocation.h" + +ParseLocation::ParseLocation(const std::string& file, unsigned int line, unsigned int column) + : file(file) + , line(line) + , column(column) +{ +} diff --git a/src/lib/data/parser/ParseLocation.h b/src/lib/data/parser/ParseLocation.h new file mode 100644 index 00000000..47999120 --- /dev/null +++ b/src/lib/data/parser/ParseLocation.h @@ -0,0 +1,15 @@ +#ifndef PARSE_LOCATION_H +#define PARSE_LOCATION_H + +#include + +struct ParseLocation +{ + ParseLocation(const std::string& file, unsigned int line, unsigned int column); + + const std::string file; + const unsigned int line; + const unsigned int column; +}; + +#endif // PARSE_LOCATION_H diff --git a/src/lib/data/parser/ParseObject.h b/src/lib/data/parser/ParseObject.h deleted file mode 100644 index c481e089..00000000 --- a/src/lib/data/parser/ParseObject.h +++ /dev/null @@ -1,25 +0,0 @@ -#ifndef PARSE_OBJECT_H -#define PARSE_OBJECT_H - -struct ParseObject -{ - ParseObject( - const std::string& name, - const std::string& fileName, - unsigned int lineNumber, - unsigned int columnNumber - ) - : name(name) - , fileName(fileName) - , lineNumber(lineNumber) - , columnNumber(columnNumber) - { - } - - const std::string name; - const std::string fileName; - const unsigned int lineNumber; - const unsigned int columnNumber; -}; - -#endif // PARSE_OBJECT_H diff --git a/src/lib/data/parser/ParseVariable.cpp b/src/lib/data/parser/ParseVariable.cpp new file mode 100644 index 00000000..5694ea5c --- /dev/null +++ b/src/lib/data/parser/ParseVariable.cpp @@ -0,0 +1,9 @@ +#include "data/parser/ParseVariable.h" + +ParseVariable::ParseVariable(const std::string& typeName, const std::string& fullName, bool isConst, bool isStatic) + : typeName(typeName) + , fullName(fullName) + , isConst(isConst) + , isStatic(isStatic) +{ +} diff --git a/src/lib/data/parser/ParseVariable.h b/src/lib/data/parser/ParseVariable.h new file mode 100644 index 00000000..965a6684 --- /dev/null +++ b/src/lib/data/parser/ParseVariable.h @@ -0,0 +1,16 @@ +#ifndef PARSE_VARIABLE_H +#define PARSE_VARIABLE_H + +#include + +struct ParseVariable +{ + ParseVariable(const std::string& typeName, const std::string& fullName, bool isConst, bool isStatic); + + const std::string typeName; + const std::string fullName; + const bool isConst; + const bool isStatic; +}; + +#endif // PARSE_VARIABLE_H diff --git a/src/lib/data/parser/ParserClient.h b/src/lib/data/parser/ParserClient.h index 9f089cfe..7f11217e 100644 --- a/src/lib/data/parser/ParserClient.h +++ b/src/lib/data/parser/ParserClient.h @@ -2,16 +2,47 @@ #define PARSER_CLIENT_H #include +#include -#include "data/parser/ParseObject.h" +struct ParseLocation; +struct ParseVariable; class ParserClient { public: + enum AccessType { + ACCESS_PUBLIC, + ACCESS_PROTECTED, + ACCESS_PRIVATE, + ACCESS_NONE + }; + + enum AbstractionType { + ABSTRACTION_VIRTUAL, + ABSTRACTION_PURE_VIRTUAL, + ABSTRACTION_NONE + }; + ParserClient(); virtual ~ParserClient(); - virtual void addClass(const ParseObject& object) = 0; + virtual void onClassParsed(const ParseLocation& location, const std::string& fullName, AccessType access) = 0; + virtual void onStructParsed(const ParseLocation& location, const std::string& fullName, AccessType access) = 0; + + virtual void onGlobalVariableParsed(const ParseLocation& location, const ParseVariable& variable) = 0; + virtual void onFieldParsed(const ParseLocation& location, const ParseVariable& variable, AccessType access) = 0; + + virtual void onFunctionParsed( + const ParseLocation& location, const std::string& fullName, const std::string& returnTypeName, + const std::vector& parameters) = 0; + virtual void onMethodParsed(const ParseLocation& location, const std::string& fullName, + const std::string& returnTypeName, const std::vector& parameters, AccessType access, + AbstractionType abstraction, bool isConst, bool isStatic) = 0; + + virtual void onNamespaceParsed(const ParseLocation& location, const std::string& fullName) = 0; + + virtual void onEnumParsed(const ParseLocation& location, const std::string& fullName, AccessType access) = 0; + virtual void onEnumFieldParsed(const ParseLocation& location, const std::string& fullName) = 0; }; #endif // PARSER_CLIENT_H diff --git a/src/lib/data/parser/cxx/ASTVisitor.cpp b/src/lib/data/parser/cxx/ASTVisitor.cpp index c2019715..b5922e21 100644 --- a/src/lib/data/parser/cxx/ASTVisitor.cpp +++ b/src/lib/data/parser/cxx/ASTVisitor.cpp @@ -1,5 +1,8 @@ #include "data/parser/cxx/ASTVisitor.h" +#include "data/parser/ParseLocation.h" +#include "data/parser/ParseVariable.h" + ASTVisitor::ASTVisitor(clang::ASTContext* context, std::shared_ptr client) : m_context(context) , m_client(client) @@ -10,21 +13,250 @@ ASTVisitor::~ASTVisitor() { } -bool ASTVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl *declaration) +bool ASTVisitor::VisitCXXRecordDecl(clang::CXXRecordDecl* declaration) { - clang::FullSourceLoc location = m_context->getFullLoc(declaration->getLocStart()); + const clang::SourceLocation& location = declaration->getLocStart(); - if (location.isValid() && m_context->getSourceManager().isWrittenInMainFile(location)) + if (isValidLocation(location)) { - ParseObject parseObject( - declaration->getQualifiedNameAsString(), - m_context->getSourceManager().getFilename(location), - location.getSpellingLineNumber(), - location.getSpellingColumnNumber() - ); - - m_client->addClass(parseObject); + if (declaration->isClass()) + { + m_client->onClassParsed( + getParseLocation(location), + declaration->getQualifiedNameAsString(), + convertAccessType(declaration->getAccess()) + ); + } + else if (declaration->isStruct()) + { + m_client->onStructParsed( + getParseLocation(location), + declaration->getQualifiedNameAsString(), + convertAccessType(declaration->getAccess()) + ); + } } return true; } + +bool ASTVisitor::VisitVarDecl(clang::VarDecl* declaration) +{ + // Abort on local variables and parameters. + if (declaration->isFunctionOrMethodVarDecl() || clang::isa(declaration)) + { + return true; + } + + const clang::SourceLocation& location = declaration->getLocStart(); + + if (isValidLocation(location)) + { + clang::AccessSpecifier access = declaration->getAccess(); + + if (access == clang::AS_none) + { + m_client->onGlobalVariableParsed(getParseLocation(location), getParseVariable(declaration)); + } + else + { + m_client->onFieldParsed( + getParseLocation(location), + getParseVariable(declaration), + convertAccessType(declaration->getAccess()) + ); + } + } + + return true; +} + +bool ASTVisitor::VisitFieldDecl(clang::FieldDecl* declaration) +{ + const clang::SourceLocation& location = declaration->getLocStart(); + + if (isValidLocation(location)) + { + m_client->onFieldParsed( + getParseLocation(location), + getParseVariable(declaration), + convertAccessType(declaration->getAccess()) + ); + } + + return true; +} + +bool ASTVisitor::VisitFunctionDecl(clang::FunctionDecl* declaration) +{ + // Abort for CXXMethodDecls to avoid duplicate call. + if (clang::isa(declaration)) + { + return true; + } + + const clang::SourceLocation& location = declaration->getLocStart(); + + if (isValidLocation(location)) + { + m_client->onFunctionParsed( + getParseLocation(location), + declaration->getQualifiedNameAsString(), + getTypeName(declaration->getReturnType()), + getParameters(declaration) + ); + } + + return true; +} + +bool ASTVisitor::VisitCXXMethodDecl(clang::CXXMethodDecl* declaration) +{ + const clang::SourceLocation& location = declaration->getLocStart(); + + if (isValidLocation(location)) + { + ParserClient::AbstractionType abstraction = ParserClient::ABSTRACTION_NONE; + if (declaration->isPure()) + { + abstraction = ParserClient::ABSTRACTION_PURE_VIRTUAL; + } + else if (declaration->isVirtual()) + { + abstraction = ParserClient::ABSTRACTION_VIRTUAL; + } + + m_client->onMethodParsed( + getParseLocation(location), + declaration->getQualifiedNameAsString(), + getTypeName(declaration->getReturnType()), + getParameters(declaration), + convertAccessType(declaration->getAccess()), + abstraction, + declaration->isConst(), + declaration->isStatic() + ); + } + + return true; +} + +bool ASTVisitor::VisitNamespaceDecl(clang::NamespaceDecl* declaration) +{ + const clang::SourceLocation& location = declaration->getLocStart(); + + if (isValidLocation(location)) + { + m_client->onNamespaceParsed(getParseLocation(location), declaration->getQualifiedNameAsString()); + } + + return true; +} + +bool ASTVisitor::VisitEnumDecl(clang::EnumDecl* declaration) +{ + const clang::SourceLocation& location = declaration->getLocStart(); + + if (isValidLocation(location)) + { + m_client->onEnumParsed( + getParseLocation(location), + declaration->getQualifiedNameAsString(), + convertAccessType(declaration->getAccess()) + ); + } + + return true; +} + +bool ASTVisitor::VisitEnumConstantDecl(clang::EnumConstantDecl* declaration) +{ + const clang::SourceLocation& location = declaration->getLocStart(); + + if (isValidLocation(location)) + { + m_client->onEnumFieldParsed(getParseLocation(location), declaration->getQualifiedNameAsString()); + } + + return true; +} + +bool ASTVisitor::isValidLocation(const clang::SourceLocation& location) const +{ + return location.isValid() && m_context->getSourceManager().isWrittenInMainFile(location); +} + +ParseLocation ASTVisitor::getParseLocation(const clang::SourceLocation& location) const +{ + clang::FullSourceLoc fullLocation = m_context->getFullLoc(location); + return ParseLocation( + m_context->getSourceManager().getFilename(location), + fullLocation.getSpellingLineNumber(), + fullLocation.getSpellingColumnNumber() + ); +} + +ParseVariable ASTVisitor::getParseVariable(clang::ValueDecl* declaration) const +{ + clang::QualType type = declaration->getType(); + + bool isStatic = false; + if (clang::isa(declaration)) + { + isStatic = static_cast(declaration)->isStaticDataMember(); + } + + return ParseVariable( + getTypeName(type), + declaration->getQualifiedNameAsString(), + type.isConstQualified(), + isStatic + ); +} + +std::vector ASTVisitor::getParameters(clang::FunctionDecl* declaration) const +{ + std::vector parameters; + + for (unsigned i = 0; i < declaration->getNumParams(); i++) + { + parameters.push_back(getParseVariable(declaration->getParamDecl(i))); + } + + return parameters; +} + +std::string ASTVisitor::getTypeName(const clang::QualType& type) const +{ + std::string typeName = type.getUnqualifiedType().getAsString(); + + // Remove keywords from type. + std::string keyword; + size_t pos = typeName.find_first_of(' '); + if (pos != std::string::npos) + { + keyword = typeName.substr(0, pos); + } + + if (keyword == "class" || keyword == "struct" || keyword == "enum") + { + typeName = typeName.substr(pos + 1); + } + + return typeName; +} + +ParserClient::AccessType ASTVisitor::convertAccessType(clang::AccessSpecifier access) const +{ + switch (access) + { + case clang::AS_public: + return ParserClient::ACCESS_PUBLIC; + case clang::AS_protected: + return ParserClient::ACCESS_PROTECTED; + case clang::AS_private: + return ParserClient::ACCESS_PRIVATE; + case clang::AS_none: + return ParserClient::ACCESS_NONE; + } +} diff --git a/src/lib/data/parser/cxx/ASTVisitor.h b/src/lib/data/parser/cxx/ASTVisitor.h index 3e22ff26..74f7c590 100644 --- a/src/lib/data/parser/cxx/ASTVisitor.h +++ b/src/lib/data/parser/cxx/ASTVisitor.h @@ -14,9 +14,31 @@ public: ASTVisitor(clang::ASTContext* context, std::shared_ptr client); virtual ~ASTVisitor(); - virtual bool VisitCXXRecordDecl(clang::CXXRecordDecl *declaration); + // Left for debugging purposes. Uncomment to see a colored ast-dump of the parsed file. + // virtual bool VisitDecl(clang::Decl* declaration) + // { + // // declaration->print(llvm::outs()); + // declaration->dump(); + // return false; + // } + + virtual bool VisitCXXRecordDecl(clang::CXXRecordDecl* declaration); // classes and structs + 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 VisitNamespaceDecl(clang::NamespaceDecl* declaration); // namespaces + virtual bool VisitEnumDecl(clang::EnumDecl* declaration); // enums + virtual bool VisitEnumConstantDecl(clang::EnumConstantDecl* declaration); // enum fields private: + bool isValidLocation(const clang::SourceLocation& location) const; + ParseLocation getParseLocation(const clang::SourceLocation& location) const; + ParseVariable getParseVariable(clang::ValueDecl* declaration) const; + std::vector getParameters(clang::FunctionDecl* declaration) const; + std::string getTypeName(const clang::QualType& type) const; + ParserClient::AccessType convertAccessType(clang::AccessSpecifier) const; + clang::ASTContext* m_context; std::shared_ptr m_client; }; diff --git a/src/lib/data/parser/cxx/CxxParser.cpp b/src/lib/data/parser/cxx/CxxParser.cpp index f53a2f43..289a3d71 100644 --- a/src/lib/data/parser/cxx/CxxParser.cpp +++ b/src/lib/data/parser/cxx/CxxParser.cpp @@ -14,8 +14,10 @@ CxxParser::~CxxParser() void CxxParser::parseFiles(const std::vector& filePaths) { - const char* argv[] = { "app", "--" }; - int argc = 2; + // Fake commandline flags passed to the programm. Everything after '--' will be interpreted by the ClangTool. + // The option '-x c++' treats subsequent input files as C++. + const char* argv[] = { "app", "--", "-x", "c++" }; + int argc = 4; std::shared_ptr compilationDatabase( clang::tooling::FixedCompilationDatabase::loadFromCommandLine(argc, argv) diff --git a/src/test/CxxParserTestSuite.h b/src/test/CxxParserTestSuite.h index ce1fc34d..19ca4c84 100644 --- a/src/test/CxxParserTestSuite.h +++ b/src/test/CxxParserTestSuite.h @@ -1,18 +1,9 @@ #include "cxxtest/TestSuite.h" #include "data/parser/cxx/CxxParser.h" +#include "data/parser/ParseLocation.h" #include "data/parser/ParserClient.h" - -class TestParserClient: public ParserClient -{ -public: - virtual void addClass(const ParseObject& object) - { - classes.push_back(object.name); - } - - std::vector classes; -}; +#include "data/parser/ParseVariable.h" class CxxParserTestSuite: public CxxTest::TestSuite { @@ -20,15 +11,256 @@ public: void test_cxx_parser_finds_classes() { std::shared_ptr client = std::make_shared(); - - std::vector filePaths; - filePaths.push_back("data/test_code.cpp"); - CxxParser parser(client); + + std::vector filePaths(1, "data/test_code.cpp"); parser.parseFiles(filePaths); - TS_ASSERT_EQUALS(client->classes[0], "X::A"); - TS_ASSERT_EQUALS(client->classes[1], "B"); - TS_ASSERT_EQUALS(client->classes[2], "B::C"); + TS_ASSERT_EQUALS(client->classes.size(), 2); + TS_ASSERT_EQUALS(client->classes[0], "B"); + TS_ASSERT_EQUALS(client->classes[1], "public B::C"); } + + void test_cxx_parser_finds_structs() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + + std::vector filePaths(1, "data/test_code.cpp"); + parser.parseFiles(filePaths); + + TS_ASSERT_EQUALS(client->structs.size(), 1); + TS_ASSERT_EQUALS(client->structs[0], "X::A"); + } + + void test_cxx_parser_finds_globals() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + + std::vector filePaths(1, "data/test_code.cpp"); + parser.parseFiles(filePaths); + + TS_ASSERT_EQUALS(client->globals.size(), 2); + TS_ASSERT_EQUALS(client->globals[0], "char * name"); + TS_ASSERT_EQUALS(client->globals[1], "H g"); + } + + void test_cxx_parser_finds_fields() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + + std::vector filePaths(1, "data/test_code.cpp"); + parser.parseFiles(filePaths); + + TS_ASSERT_EQUALS(client->fields.size(), 3); + TS_ASSERT_EQUALS(client->fields[0], "private static const int B::C::amount"); + TS_ASSERT_EQUALS(client->fields[1], "private const int B::count"); + TS_ASSERT_EQUALS(client->fields[2], "private H B::h"); + } + + void test_cxx_parser_finds_functions() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + + std::vector filePaths(1, "data/test_code.cpp"); + parser.parseFiles(filePaths); + + TS_ASSERT_EQUALS(client->functions.size(), 2); + TS_ASSERT_EQUALS(client->functions[0], "int ceil(float a)"); + TS_ASSERT_EQUALS(client->functions[1], "int (anonymous namespace)::sum(int a, int b)"); + } + + void test_cxx_parser_finds_methods() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + + std::vector filePaths(1, "data/test_code.cpp"); + parser.parseFiles(filePaths); + + TS_ASSERT_EQUALS(client->methods.size(), 5); + TS_ASSERT_EQUALS(client->methods[0], "public void B::B()"); + TS_ASSERT_EQUALS(client->methods[1], "public virtual void B::~B()"); + TS_ASSERT_EQUALS(client->methods[2], "protected pure virtual void B::process()"); + TS_ASSERT_EQUALS(client->methods[3], "private int B::getCount() const"); + TS_ASSERT_EQUALS(client->methods[4], "public void B::B()"); + } + + void test_cxx_parser_finds_namespaces() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + + std::vector filePaths(1, "data/test_code.cpp"); + parser.parseFiles(filePaths); + + TS_ASSERT_EQUALS(client->namespaces.size(), 2); + TS_ASSERT_EQUALS(client->namespaces[0], "X"); + TS_ASSERT_EQUALS(client->namespaces[1], "(anonymous)"); + } + + void test_cxx_parser_finds_enums() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + + std::vector filePaths(1, "data/test_code.cpp"); + parser.parseFiles(filePaths); + + TS_ASSERT_EQUALS(client->enums.size(), 1); + TS_ASSERT_EQUALS(client->enums[0], "X::E"); + } + + void test_cxx_parser_finds_enum_fields() + { + std::shared_ptr client = std::make_shared(); + CxxParser parser(client); + + std::vector filePaths(1, "data/test_code.cpp"); + parser.parseFiles(filePaths); + + TS_ASSERT_EQUALS(client->enumFields.size(), 2); + TS_ASSERT_EQUALS(client->enumFields[0], "X::E::P"); + TS_ASSERT_EQUALS(client->enumFields[1], "X::E::Q"); + } + +private: + class TestParserClient: public ParserClient + { + public: + virtual void onClassParsed(const ParseLocation& location, const std::string& fullName, AccessType access) + { + classes.push_back(addAccessPrefix(fullName, access)); + } + + virtual void onStructParsed(const ParseLocation& location, const std::string& fullName, AccessType access) + { + structs.push_back(addAccessPrefix(fullName, access)); + } + + virtual void onGlobalVariableParsed(const ParseLocation& location, const ParseVariable& variable) + { + globals.push_back(variableStr(variable)); + } + + virtual void onFieldParsed(const ParseLocation& location, const ParseVariable& variable, AccessType access) + { + fields.push_back(addAccessPrefix(variableStr(variable), access)); + } + + virtual void onFunctionParsed( + const ParseLocation& location, const std::string& fullName, const std::string& returnTypeName, + const std::vector& parameters + ) + { + functions.push_back(returnTypeName + " " + fullName + parameterStr(parameters)); + } + + virtual void onMethodParsed( + const ParseLocation& location, const std::string& fullName, const std::string& returnTypeName, + const std::vector& parameters, AccessType access, AbstractionType abstraction, + bool isConst, bool isStatic + ) + { + std::string str = returnTypeName + " " + fullName + parameterStr(parameters); + str = addStaticPrefix(addAbstractionPrefix(str, abstraction), isStatic); + str = addConstPrefix(addAccessPrefix(str, access), isConst, false); + methods.push_back(str); + } + + virtual void onNamespaceParsed(const ParseLocation& location, const std::string& fullName) + { + namespaces.push_back(fullName); + } + + virtual void onEnumParsed(const ParseLocation& location, const std::string& fullName, AccessType access) + { + enums.push_back(addAccessPrefix(fullName, access)); + } + + virtual void onEnumFieldParsed(const ParseLocation& location, const std::string& fullName) + { + enumFields.push_back(fullName); + } + + std::vector classes; + std::vector enums; + std::vector enumFields; + std::vector functions; + std::vector fields; + std::vector globals; + std::vector methods; + std::vector namespaces; + std::vector structs; + + private: + std::string addAccessPrefix(const std::string& str, AccessType access) + { + switch (access) + { + case ACCESS_PUBLIC: + return "public " + str; + case ACCESS_PROTECTED: + return "protected " + str; + case ACCESS_PRIVATE: + return "private " + str; + case ACCESS_NONE: + return str; + } + } + + std::string addAbstractionPrefix(const std::string& str, AbstractionType abstraction) + { + switch (abstraction) + { + case ABSTRACTION_VIRTUAL: + return "virtual " + str; + case ABSTRACTION_PURE_VIRTUAL: + return "pure virtual " + str; + case ABSTRACTION_NONE: + return str; + } + } + + std::string addStaticPrefix(const std::string& str, bool isStatic) + { + if (isStatic) + { + return "static " + str; + } + return str; + } + + std::string addConstPrefix(const std::string& str, bool isConst, bool atFront) + { + if (isConst) + { + return atFront ? "const " + str : str + " const"; + } + return str; + } + + std::string variableStr(const ParseVariable& variable) + { + std::string str = variable.typeName + " " + variable.fullName; + return addStaticPrefix(addConstPrefix(str, variable.isConst, true), variable.isStatic); + } + + std::string parameterStr(const std::vector parameters) + { + std::string str = "("; + for (size_t i = 0; i < parameters.size(); i++) + { + str += variableStr(parameters[i]); + if (i < parameters.size() - 1) + { + str += ", "; + } + } + return str + ")"; + } + }; };