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 + ")"; + } + }; };