From 0dc27bd3acfc0526ac713d15d32c8d784297b6a9 Mon Sep 17 00:00:00 2001 From: Jacob Dufault Date: Fri, 17 Feb 2017 01:57:44 -0800 Subject: [PATCH] Update --- libclangmm/CodeCompleteResults.cc | 4 +- libclangmm/CompileCommand.cc | 4 +- libclangmm/CompletionString.cc | 4 +- libclangmm/Cursor.cc | 64 +- libclangmm/Cursor.h | 97 ++- libclangmm/Diagnostic.cc | 4 +- libclangmm/Diagnostic.h | 39 +- libclangmm/SourceLocation.cc | 2 +- libclangmm/Token.cc | 2 +- libclangmm/TranslationUnit.cc | 100 +-- libclangmm/TranslationUnit.h | 2 + libclangmm/Utility.cc | 6 +- libclangmm/Utility.h | 12 +- main.cpp | 548 ++++++++++-- test.cc | 2 +- tests/function_declaration.cc | 9 + tests/function_declaration_in_namespace.cc | 11 + tests/function_definition.cc | 9 + tests/function_definition_in_namespace.cc | 11 + tests/method_declaration.cc | 11 + tests/method_declaration_in_namespace.cc | 13 + tests/method_definition.cc | 13 + tests/method_definition_in_namespace.cc | 15 + tests/method_inline_declaration.cc | 11 + .../method_inline_declaration_in_namespace.cc | 13 + tinydir.h | 804 ++++++++++++++++++ utils.cc | 68 ++ utils.h | 8 + 28 files changed, 1675 insertions(+), 211 deletions(-) create mode 100644 tests/function_declaration.cc create mode 100644 tests/function_declaration_in_namespace.cc create mode 100644 tests/function_definition.cc create mode 100644 tests/function_definition_in_namespace.cc create mode 100644 tests/method_declaration.cc create mode 100644 tests/method_declaration_in_namespace.cc create mode 100644 tests/method_definition.cc create mode 100644 tests/method_definition_in_namespace.cc create mode 100644 tests/method_inline_declaration.cc create mode 100644 tests/method_inline_declaration_in_namespace.cc create mode 100644 tinydir.h create mode 100644 utils.cc create mode 100644 utils.h diff --git a/libclangmm/CodeCompleteResults.cc b/libclangmm/CodeCompleteResults.cc index 7d09dd0b..60e8efe2 100644 --- a/libclangmm/CodeCompleteResults.cc +++ b/libclangmm/CodeCompleteResults.cc @@ -7,7 +7,7 @@ clang::CodeCompleteResults::CodeCompleteResults(CXTranslationUnit &cx_tu, const std::string &buffer, unsigned line_num, unsigned column) { CXUnsavedFile files[1]; - auto file_path=to_string(clang_getTranslationUnitSpelling(cx_tu)); + auto file_path=ToString(clang_getTranslationUnitSpelling(cx_tu)); files[0].Filename = file_path.c_str(); files[0].Contents = buffer.c_str(); files[0].Length = buffer.size(); @@ -41,5 +41,5 @@ clang::CompletionString clang::CodeCompleteResults::get(unsigned i) const { } std::string clang::CodeCompleteResults::get_usr() const { - return to_string(clang_codeCompleteGetContainerUSR(cx_results)); + return ToString(clang_codeCompleteGetContainerUSR(cx_results)); } diff --git a/libclangmm/CompileCommand.cc b/libclangmm/CompileCommand.cc index a291da50..c7e25842 100644 --- a/libclangmm/CompileCommand.cc +++ b/libclangmm/CompileCommand.cc @@ -6,7 +6,7 @@ std::string clang::CompileCommand::get_command() { std::string res; unsigned N = clang_CompileCommand_getNumArgs(cx_command); for (unsigned i = 0; i < N; i++) { - res += to_string(clang_CompileCommand_getArg(cx_command, i)); + res += ToString(clang_CompileCommand_getArg(cx_command, i)); } return res; } @@ -15,7 +15,7 @@ std::vector clang::CompileCommand::get_command_as_args() { unsigned N = clang_CompileCommand_getNumArgs(cx_command); std::vector res(N); for (unsigned i = 0; i < N; i++) { - res[i] = to_string(clang_CompileCommand_getArg(cx_command, i)); + res[i] = ToString(clang_CompileCommand_getArg(cx_command, i)); } return res; } diff --git a/libclangmm/CompletionString.cc b/libclangmm/CompletionString.cc index e9b465e1..0b52529f 100644 --- a/libclangmm/CompletionString.cc +++ b/libclangmm/CompletionString.cc @@ -15,13 +15,13 @@ unsigned clang::CompletionString::get_num_chunks() { std::vector clang::CompletionString::get_chunks() { std::vector res; for (unsigned i = 0; i < get_num_chunks(); i++) { - res.emplace_back(to_string(clang_getCompletionChunkText(cx_completion_sting, i)), static_cast (clang_getCompletionChunkKind(cx_completion_sting, i))); + res.emplace_back(ToString(clang_getCompletionChunkText(cx_completion_sting, i)), static_cast (clang_getCompletionChunkKind(cx_completion_sting, i))); } return res; } std::string clang::CompletionString::get_brief_comments() { - return to_string(clang_getCompletionBriefComment(cx_completion_sting)); + return ToString(clang_getCompletionBriefComment(cx_completion_sting)); } clang::CompletionChunk::CompletionChunk(std::string chunk, CompletionChunkKind kind) : diff --git a/libclangmm/Cursor.cc b/libclangmm/Cursor.cc index 3657599d..cc909be2 100644 --- a/libclangmm/Cursor.cc +++ b/libclangmm/Cursor.cc @@ -4,28 +4,38 @@ namespace clang { -std::string Cursor::Type::get_spelling() const { - return to_string(clang_getTypeSpelling(cx_type)); +static_assert(sizeof(Cursor) == sizeof(CXCursor), "Cursor must be the same size as CXCursor"); + +std::string Type::get_spelling() const { + return ToString(clang_getTypeSpelling(cx_type)); } -Cursor::Type Cursor::Type::get_result() const { - return Type(clang_getResultType(cx_type)); +Type Cursor::get_type() const { + return Type(clang_getCursorType(cx_cursor)); } -Cursor::Cursor() = default; - -Cursor::Cursor(const CXCursor& other) : cx_cursor(cx_cursor) {} - -bool Cursor::Type::operator==(const Cursor::Type& rhs) const { +bool Type::operator==(const Type& rhs) const { return clang_equalTypes(cx_type, rhs.cx_type); } -CXCursorKind Cursor::get_kind() const { - return clang_getCursorKind(cx_cursor); +Type Type::get_result() const { + return Type(clang_getResultType(cx_type)); } -Cursor::Type Cursor::get_type() const { - return Type(clang_getCursorType(cx_cursor)); +Cursor::Cursor() : cx_cursor(clang_getNullCursor()) {} + +Cursor::Cursor(const CXCursor& other) : cx_cursor(other) {} + +Cursor::operator bool() const { + return !clang_Cursor_isNull(cx_cursor); +} + +bool Cursor::operator==(const Cursor& rhs) const { + return clang_equalCursors(cx_cursor, rhs.cx_cursor); +} + +CXCursorKind Cursor::get_kind() const { + return cx_cursor.kind; } SourceLocation Cursor::get_source_location() const { @@ -37,15 +47,19 @@ SourceRange Cursor::get_source_range() const { } std::string Cursor::get_spelling() const { - return to_string(clang_getCursorSpelling(cx_cursor)); + return ToString(clang_getCursorSpelling(cx_cursor)); } std::string Cursor::get_display_name() const { - return to_string(clang_getCursorDisplayName(cx_cursor)); + return ToString(clang_getCursorDisplayName(cx_cursor)); } std::string Cursor::get_usr() const { - return to_string(clang_getCursorUSR(cx_cursor)); + return ToString(clang_getCursorUSR(cx_cursor)); +} + +bool Cursor::is_definition() const { + return clang_isCursorDefinition(cx_cursor); } Cursor Cursor::get_referenced() const { @@ -72,14 +86,6 @@ std::vector Cursor::get_arguments() const { return cursors; } -Cursor::operator bool() const { - return !clang_Cursor_isNull(cx_cursor); -} - -bool Cursor::operator==(const Cursor& rhs) const { - return clang_equalCursors(cx_cursor, rhs.cx_cursor); -} - bool Cursor::is_valid_kind() const { CXCursor referenced = clang_getCursorReferenced(cx_cursor); if (clang_Cursor_isNull(referenced)) @@ -96,13 +102,13 @@ std::string Cursor::get_type_description() const { auto referenced = clang_getCursorReferenced(cx_cursor); if (!clang_Cursor_isNull(referenced)) { auto type = clang_getCursorType(referenced); - spelling = to_string(clang_getTypeSpelling(type)); + spelling = ToString(clang_getTypeSpelling(type)); #if CINDEX_VERSION_MAJOR==0 && CINDEX_VERSION_MINOR<32 const std::string auto_str = "auto"; if (spelling.size() >= 4 && std::equal(auto_str.begin(), auto_str.end(), spelling.begin())) { auto canonical_type = clang_getCanonicalType(clang_getCursorType(cx_cursor)); - auto canonical_spelling = to_string(clang_getTypeSpelling(canonical_type)); + auto canonical_spelling = ToString(clang_getTypeSpelling(canonical_type)); if (spelling.size() > 5 && spelling[4] == ' ' && spelling[5] == '&' && spelling != canonical_spelling) return canonical_spelling + " &"; else @@ -112,7 +118,7 @@ std::string Cursor::get_type_description() const { const std::string const_auto_str = "const auto"; if (spelling.size() >= 10 && std::equal(const_auto_str.begin(), const_auto_str.end(), spelling.begin())) { auto canonical_type = clang_getCanonicalType(clang_getCursorType(cx_cursor)); - auto canonical_spelling = to_string(clang_getTypeSpelling(canonical_type)); + auto canonical_spelling = ToString(clang_getTypeSpelling(canonical_type)); if (spelling.size() > 11 && spelling[10] == ' ' && spelling[11] == '&' && spelling != canonical_spelling) return canonical_spelling + " &"; else @@ -127,10 +133,10 @@ std::string Cursor::get_type_description() const { return spelling; } -std::string Cursor::get_brief_comments() const { +std::string Cursor::get_comments() const { Cursor referenced = get_referenced(); if (referenced) - return to_string(clang_Cursor_getBriefCommentText(referenced.cx_cursor)); + return ToString(clang_Cursor_getRawCommentText(referenced.cx_cursor)); return ""; } diff --git a/libclangmm/Cursor.h b/libclangmm/Cursor.h index ebb3302e..a7bcab91 100644 --- a/libclangmm/Cursor.h +++ b/libclangmm/Cursor.h @@ -3,49 +3,76 @@ #include #include -#include +#include +#include #include "SourceLocation.h" #include "SourceRange.h" namespace clang { - class Cursor { - public: - class Type { - public: - Type(const CXType &cx_type) : cx_type(cx_type) {} - std::string get_spelling() const; - Type get_result() const; - bool operator==(const Cursor::Type& rhs) const; - - CXType cx_type; - }; - - Cursor(); - explicit Cursor(const CXCursor& cx_cursor); +class Type { +public: + Type(const CXType &cx_type) : cx_type(cx_type) {} - CXCursorKind get_kind() const; - Type get_type() const; - SourceLocation get_source_location() const; - SourceRange get_source_range() const; - std::string get_spelling() const; - std::string get_display_name() const; - std::string get_usr() const; - Cursor get_referenced() const; - Cursor get_canonical() const; - Cursor get_definition() const; - Cursor get_semantic_parent() const; - std::vector get_arguments() const; - operator bool() const; - bool operator==(const Cursor& rhs) const; - - bool is_valid_kind() const; - std::string get_type_description() const; - std::string get_brief_comments() const; - - CXCursor cx_cursor = clang_getNullCursor(); + bool operator==(const Type& rhs) const; + + std::string get_spelling() const; + Type get_result() const; + + CXType cx_type; +}; + +enum class VisiterResult { + Break, + Continue, + Recurse +}; + +class Cursor { +public: + Cursor(); + explicit Cursor(const CXCursor& other); + + operator bool() const; + bool operator==(const Cursor& rhs) const; + + CXCursorKind get_kind() const; + Type get_type() const; + SourceLocation get_source_location() const; + SourceRange get_source_range() const; + std::string get_spelling() const; + std::string get_display_name() const; + std::string get_usr() const; + + bool is_definition() const; + + Cursor get_referenced() const; + Cursor get_canonical() const; + Cursor get_definition() const; + Cursor get_semantic_parent() const; + std::vector get_arguments() const; + bool is_valid_kind() const; + std::string get_type_description() const; + std::string get_comments() const; + + template + using Visitor = VisiterResult(*)(Cursor cursor, Cursor parent, TClientData* client_data); + + enum class VisitResult { + Completed, EndedEarly }; + + template + VisitResult VisitChildren(Visitor visitor, TClientData* client_data) const { + if (clang_visitChildren(cx_cursor, reinterpret_cast(visitor), client_data) == 0) + return VisitResult::Completed; + return VisitResult::EndedEarly; + } + + CXCursor cx_cursor; +}; } // namespace clang + #endif // CURSOR_H_ diff --git a/libclangmm/Diagnostic.cc b/libclangmm/Diagnostic.cc index f7d4264b..5076e4f4 100644 --- a/libclangmm/Diagnostic.cc +++ b/libclangmm/Diagnostic.cc @@ -6,7 +6,7 @@ clang::Diagnostic::Diagnostic(CXTranslationUnit& cx_tu, CXDiagnostic& cx_diagnostic) { severity=clang_getDiagnosticSeverity(cx_diagnostic); severity_spelling=get_severity_spelling(severity); - spelling=to_string(clang_getDiagnosticSpelling(cx_diagnostic)); + spelling=ToString(clang_getDiagnosticSpelling(cx_diagnostic)); SourceLocation start_location(clang_getDiagnosticLocation(cx_diagnostic)); path=start_location.get_path(); @@ -18,7 +18,7 @@ clang::Diagnostic::Diagnostic(CXTranslationUnit& cx_tu, CXDiagnostic& cx_diagnos unsigned num_fix_its=clang_getDiagnosticNumFixIts(cx_diagnostic); for(unsigned c=0;c &offsets): - source(source), offsets(offsets) {} - std::string source; - std::pair offsets; - }; - - static const std::string get_severity_spelling(unsigned severity); - - unsigned severity; - std::string severity_spelling; - std::string spelling; - std::string path; + FixIt(const std::string &source, const std::pair &offsets) : + source(source), offsets(offsets) {} + std::string source; std::pair offsets; - std::vector fix_its; }; -} + static const std::string get_severity_spelling(unsigned severity); + + unsigned severity; + std::string severity_spelling; + std::string spelling; + std::string path; + std::pair offsets; + std::vector fix_its; +}; + +} #endif // DIAGNOSTIC_H_ diff --git a/libclangmm/SourceLocation.cc b/libclangmm/SourceLocation.cc index c7bb6f75..9100ca7b 100644 --- a/libclangmm/SourceLocation.cc +++ b/libclangmm/SourceLocation.cc @@ -31,7 +31,7 @@ void SourceLocation::get_data(std::string* path, unsigned *line, unsigned *colum CXFile file; clang_getExpansionLocation(cx_location, &file, line, column, offset); if (file != nullptr) { - *path = to_string(clang_getFileName(file)); + *path = ToString(clang_getFileName(file)); } } } diff --git a/libclangmm/Token.cc b/libclangmm/Token.cc index 5a0e3ddf..06c39056 100644 --- a/libclangmm/Token.cc +++ b/libclangmm/Token.cc @@ -17,7 +17,7 @@ clang::SourceRange clang::Token::get_source_range() const { } // returns a string description of this tokens kind std::string clang::Token::get_spelling() const { - return to_string(clang_getTokenSpelling(cx_tu, cx_token)); + return ToString(clang_getTokenSpelling(cx_tu, cx_token)); } clang::Token::Kind clang::Token::get_kind() const { diff --git a/libclangmm/TranslationUnit.cc b/libclangmm/TranslationUnit.cc index 23e3883f..729ec385 100644 --- a/libclangmm/TranslationUnit.cc +++ b/libclangmm/TranslationUnit.cc @@ -5,45 +5,44 @@ #include #include -#include //TODO: remove -using namespace std; //TODO: remove +namespace clang { -clang::TranslationUnit::TranslationUnit(Index &index, const std::string &file_path, - const std::vector &command_line_args, - const std::string &buffer, unsigned flags) { +TranslationUnit::TranslationUnit(Index &index, const std::string &file_path, + const std::vector &command_line_args, + const std::string &buffer, unsigned flags) { std::vector args; - for(auto &a: command_line_args) { + for (auto &a : command_line_args) { args.push_back(a.c_str()); } - + CXUnsavedFile files[1]; - files[0].Filename=file_path.c_str(); - files[0].Contents=buffer.c_str(); - files[0].Length=buffer.size(); - + files[0].Filename = file_path.c_str(); + files[0].Contents = buffer.c_str(); + files[0].Length = buffer.size(); + cx_tu = clang_parseTranslationUnit(index.cx_index, file_path.c_str(), args.data(), - args.size(), files, 1, flags); + args.size(), files, 1, flags); } -clang::TranslationUnit::TranslationUnit(Index &index, const std::string &file_path, - const std::vector &command_line_args, - unsigned flags) { +TranslationUnit::TranslationUnit(Index &index, const std::string &file_path, + const std::vector &command_line_args, + unsigned flags) { std::vector args; - for(auto &a: command_line_args) { + for (auto &a : command_line_args) { args.push_back(a.c_str()); } - + cx_tu = clang_parseTranslationUnit(index.cx_index, file_path.c_str(), args.data(), - args.size(), nullptr, 0, flags); + args.size(), nullptr, 0, flags); } -clang::TranslationUnit::~TranslationUnit() { +TranslationUnit::~TranslationUnit() { clang_disposeTranslationUnit(cx_tu); } -void clang::TranslationUnit::parse(Index &index, const std::string &file_path, - const std::vector &command_line_args, - const std::map &buffers, unsigned flags) { +void TranslationUnit::parse(Index &index, const std::string &file_path, + const std::vector &command_line_args, + const std::map &buffers, unsigned flags) { std::vector files; for (auto &buffer : buffers) { CXUnsavedFile file; @@ -53,76 +52,81 @@ void clang::TranslationUnit::parse(Index &index, const std::string &file_path, files.push_back(file); } std::vector args; - for(auto &a: command_line_args) { + for (auto &a : command_line_args) { args.push_back(a.c_str()); } - cx_tu = clang_parseTranslationUnit(index.cx_index, file_path.c_str(), args.data(), - args.size(), files.data(), files.size(), flags); + cx_tu = clang_parseTranslationUnit(index.cx_index, file_path.c_str(), args.data(), + args.size(), files.data(), files.size(), flags); } -int clang::TranslationUnit::ReparseTranslationUnit(const std::string &buffer, unsigned flags) { +int TranslationUnit::ReparseTranslationUnit(const std::string &buffer, unsigned flags) { CXUnsavedFile files[1]; - - auto file_path=to_string(clang_getTranslationUnitSpelling(cx_tu)); - - files[0].Filename=file_path.c_str(); - files[0].Contents=buffer.c_str(); - files[0].Length=buffer.size(); - + + auto file_path = ToString(clang_getTranslationUnitSpelling(cx_tu)); + + files[0].Filename = file_path.c_str(); + files[0].Contents = buffer.c_str(); + files[0].Length = buffer.size(); + return clang_reparseTranslationUnit(cx_tu, 1, files, flags); } -unsigned clang::TranslationUnit::DefaultFlags() { - auto flags= +unsigned TranslationUnit::DefaultFlags() { + auto flags = CXTranslationUnit_CacheCompletionResults | CXTranslationUnit_PrecompiledPreamble | CXTranslationUnit_Incomplete | CXTranslationUnit_IncludeBriefCommentsInCodeCompletion; #if CINDEX_VERSION_MAJOR>0 || (CINDEX_VERSION_MAJOR==0 && CINDEX_VERSION_MINOR>=35) - flags|=CXTranslationUnit_KeepGoing; + flags |= CXTranslationUnit_KeepGoing; #endif return flags; } -clang::CodeCompleteResults clang::TranslationUnit::get_code_completions(const std::string &buffer, - unsigned line_number, unsigned column) { +CodeCompleteResults TranslationUnit::get_code_completions(const std::string &buffer, + unsigned line_number, unsigned column) { CodeCompleteResults results(cx_tu, buffer, line_number, column); return results; } -std::vector clang::TranslationUnit::get_diagnostics() { +std::vector TranslationUnit::get_diagnostics() { std::vector diagnostics; - for(unsigned c=0;c clang::TranslationUnit::get_tokens(unsigned start_offset, unsigned end_offset) { - auto path=clang::to_string(clang_getTranslationUnitSpelling(cx_tu)); +std::unique_ptr TranslationUnit::get_tokens(unsigned start_offset, unsigned end_offset) { + auto path = ToString(clang_getTranslationUnitSpelling(cx_tu)); SourceLocation start_location(cx_tu, path, start_offset); SourceLocation end_location(cx_tu, path, end_offset); SourceRange range(start_location, end_location); return std::unique_ptr(new Tokens(cx_tu, range)); } -std::unique_ptr clang::TranslationUnit::get_tokens(unsigned start_line, unsigned start_column, - unsigned end_line, unsigned end_column) { - auto path=to_string(clang_getTranslationUnitSpelling(cx_tu)); +std::unique_ptr TranslationUnit::get_tokens(unsigned start_line, unsigned start_column, unsigned end_line, unsigned end_column) { + auto path = ToString(clang_getTranslationUnitSpelling(cx_tu)); SourceLocation start_location(cx_tu, path, start_line, start_column); SourceLocation end_location(cx_tu, path, end_line, end_column); SourceRange range(start_location, end_location); return std::unique_ptr(new Tokens(cx_tu, range)); } -clang::Cursor clang::TranslationUnit::get_cursor(std::string path, unsigned offset) { +Cursor TranslationUnit::document_cursor() const { + return Cursor(clang_getTranslationUnitCursor(cx_tu)); +} + +Cursor TranslationUnit::get_cursor(std::string path, unsigned offset) { SourceLocation location(cx_tu, path, offset); return Cursor(clang_getCursor(cx_tu, location.cx_location)); } -clang::Cursor clang::TranslationUnit::get_cursor(std::string path, unsigned line, unsigned column) { +Cursor TranslationUnit::get_cursor(std::string path, unsigned line, unsigned column) { SourceLocation location(cx_tu, path, line, column); return Cursor(clang_getCursor(cx_tu, location.cx_location)); } + +} \ No newline at end of file diff --git a/libclangmm/TranslationUnit.h b/libclangmm/TranslationUnit.h index e3d5123b..3def344c 100644 --- a/libclangmm/TranslationUnit.h +++ b/libclangmm/TranslationUnit.h @@ -44,6 +44,8 @@ namespace clang { std::unique_ptr get_tokens(unsigned start_line, unsigned start_column, unsigned end_line, unsigned end_column); + Cursor document_cursor() const; + clang::Cursor get_cursor(std::string path, unsigned offset); clang::Cursor get_cursor(std::string path, unsigned line, unsigned column); diff --git a/libclangmm/Utility.cc b/libclangmm/Utility.cc index 72f03004..b1eea58f 100644 --- a/libclangmm/Utility.cc +++ b/libclangmm/Utility.cc @@ -1,6 +1,6 @@ #include "Utility.h" -std::string clang::to_string(CXString cx_string) { +std::string clang::ToString(CXString cx_string) { std::string string; if(cx_string.data!=nullptr) { string=clang_getCString(cx_string); @@ -9,7 +9,7 @@ std::string clang::to_string(CXString cx_string) { return string; } -std::string clang::to_string(CXCursorKind kind) { +std::string clang::ToString(CXCursorKind kind) { switch (kind) { case CXCursor_UnexposedDecl: return "UnexposedDecl"; case CXCursor_StructDecl: return "StructDecl"; @@ -177,5 +177,5 @@ std::string clang::to_string(CXCursorKind kind) { //case CXCursor_FirstExtraDecl: return "FirstExtraDecl"; case CXCursor_LastExtraDecl: return "LastExtraDecl"; } - return ""; + return ""; } diff --git a/libclangmm/Utility.h b/libclangmm/Utility.h index de234b1d..44dd8424 100644 --- a/libclangmm/Utility.h +++ b/libclangmm/Utility.h @@ -1,11 +1,11 @@ -#ifndef UTILITY_H_ -#define UTILITY_H_ +#pragma once + #include #include namespace clang { - std::string to_string(CXString cx_string); - std::string to_string(CXCursorKind cursor_kind); -} -#endif // UTILITY_H_ \ No newline at end of file +std::string ToString(CXString cx_string); +std::string ToString(CXCursorKind cursor_kind); + +} // namespace clang \ No newline at end of file diff --git a/main.cpp b/main.cpp index b0db84c6..cfbc32a2 100644 --- a/main.cpp +++ b/main.cpp @@ -1,85 +1,513 @@ +#include #include +#include +#include +#include #include "libclangmm\clangmm.h" #include "libclangmm\Utility.h" + +#include "utils.h" //#include -CXChildVisitResult visitPrint(CXCursor cursor0, CXCursor parent, CXClientData param) { - int* level = static_cast(param); - clang::Cursor cursor(cursor0); +// While indexing, we should refer to symbols by USR. When joining into the db, we can have optimized access. - for (int i = 0; i < *level; ++i) - std::cout << " "; - std::cout << cursor.get_spelling() << " " << clang::to_string(cursor.get_kind()) << std::endl; +struct TypeDef; +struct FuncDef; +struct VarDef; - *level += 1; - clang_visitChildren(cursor0, &visitPrint, level); - *level -= 1; - return CXChildVisitResult::CXChildVisit_Continue; -/* - echo " ".repeat(param.level), cursor.kind, " ", getSpelling(cursor) +template +struct Id { + uint64_t file_id; + uint64_t local_id; - var visitParam : VisitPrintParam - visitParam.level = param.level + 1 - visitChildren(cursor, visitPrint, addr visitParam) + Id() : file_id(0), local_id(0) {} // Needed for containers. Do not use directly. + Id(uint64_t file_id, uint64_t local_id) + : file_id(file_id), local_id(local_id) {} +}; +using TypeId = Id; +using FuncId = Id; +using VarId = Id; - return VisitResult.Continu -*/ + +template +struct Ref { + Id id; + clang::SourceLocation loc; +}; +using TypeRef = Ref; +using FuncRef = Ref; +using VarRef = Ref; + + +struct TypeDef { + TypeDef(TypeId id); + + // General metadata. + TypeId id; + std::string usr; + std::string shortName; + std::string qualifiedName; + std::optional definition; + + // Immediate parent and immediate derived types. + std::vector parents; + std::vector derived; + + // Types, functions, and variables defined in this type. + std::vector types; + std::vector funcs; + std::vector vars; + + // Usages. + std::vector uses; +}; + +TypeDef::TypeDef(TypeId id) : id(id) {} + +struct FuncDef { + FuncDef(FuncId id); + + // General metadata. + FuncId id; + std::string usr; + std::string shortName; + std::string qualifiedName; + std::optional declaration; + std::optional definition; + + // Type which declares this one (ie, it is a method) + std::optional declaringType; + // Method this method overrides. + std::optional baseFunc; + // Methods which directly override this one. + std::vector derived; + + // Local variables defined in this function. + std::vector locals; + + // Functions which call this one. + std::vector callers; + // Functions that this function calls. + std::vector callees; + + // Usages. + std::vector uses; +}; + +FuncDef::FuncDef(FuncId id) : id(id) {} + +struct VarDef { + VarDef(VarId id); + + // General metadata. + VarId id; + std::string usr; + std::string shortName; + std::string qualifiedName; + std::optional declaration; + std::vector initializations; + + // Type of the variable. + std::optional variableType; + + // Type which declares this one (ie, it is a method) + std::optional declaringType; + + // Usages. + std::vector uses; +}; + +VarDef::VarDef(VarId id) : id(id) {} + + +struct ParsingDatabase { + // NOTE: Every Id is resolved to a file_id of 0. The correct file_id needs + // to get fixed up when inserting into the real db. + std::unordered_map usrToTypeId; + std::unordered_map usrToFuncId; + std::unordered_map usrToVarId; + + std::vector types; + std::vector funcs; + std::vector vars; + + TypeId ToTypeId(const std::string& usr); + FuncId ToFuncId(const std::string& usr); + VarId ToVarId(const std::string& usr); + + TypeDef* Resolve(TypeId id); + FuncDef* Resolve(FuncId id); + VarDef* Resolve(VarId id); + + std::vector ToString(); +}; + +TypeId ParsingDatabase::ToTypeId(const std::string& usr) { + auto it = usrToTypeId.find(usr); + if (it != usrToTypeId.end()) + return it->second; + + TypeId id(0, types.size()); + types.push_back(TypeDef(id)); + usrToTypeId[usr] = id; + return id; +} +FuncId ParsingDatabase::ToFuncId(const std::string& usr) { + auto it = usrToFuncId.find(usr); + if (it != usrToFuncId.end()) + return it->second; + + FuncId id(0, funcs.size()); + funcs.push_back(FuncDef(id)); + usrToFuncId[usr] = id; + return id; +} +VarId ParsingDatabase::ToVarId(const std::string& usr) { + auto it = usrToVarId.find(usr); + if (it != usrToVarId.end()) + return it->second; + + VarId id(0, vars.size()); + vars.push_back(VarDef(id)); + usrToVarId[usr] = id; + return id; } -int main(int argc, char** argv) { +TypeDef* ParsingDatabase::Resolve(TypeId id) { + return &types[id.local_id]; +} +FuncDef* ParsingDatabase::Resolve(FuncId id) { + return &funcs[id.local_id]; +} +VarDef* ParsingDatabase::Resolve(VarId id) { + return &vars[id.local_id]; +} + +std::vector ParsingDatabase::ToString() { + std::vector result; + + result.push_back("Types:"); + for (TypeDef& def : types) { + result.push_back(" " + def.qualifiedName); + } + + result.push_back("Funcs:"); + for (FuncDef& def : funcs) { + result.push_back(" " + def.qualifiedName); + } + + result.push_back("Vars:"); + for (VarDef& def : vars) { + result.push_back(" " + def.qualifiedName); + } + + return result; +} + +struct FileDef { + uint64_t id; + std::string path; + std::vector types; + std::vector funcs; + std::vector vars; +}; + + + +struct Database { + std::unordered_map usrToTypeId; + std::unordered_map usrToFuncId; + std::unordered_map usrToVarId; + + std::vector files; + + TypeId ToTypeId(const std::string& usr); + FuncId ToFuncId(const std::string& usr); + VarId ToVarId(const std::string& usr); +}; + +TypeId Database::ToTypeId(const std::string& usr) { + auto it = usrToTypeId.find(usr); + assert(it != usrToTypeId.end() && "Usr is not registered"); + return it->second; +} +FuncId Database::ToFuncId(const std::string& usr) { + auto it = usrToFuncId.find(usr); + assert(it != usrToFuncId.end() && "Usr is not registered"); + return it->second; +} +VarId Database::ToVarId(const std::string& usr) { + auto it = usrToVarId.find(usr); + assert(it != usrToVarId.end() && "Usr is not registered"); + return it->second; +} + +TypeDef* Resolve(FileDef* file, TypeId id) { + assert(file->id == id.file_id); + return &file->types[id.local_id]; +} +FuncDef* Resolve(FileDef* file, FuncId id) { + assert(file->id == id.file_id); + return &file->funcs[id.local_id]; +} +VarDef* Resolve(FileDef* file, VarId id) { + assert(file->id == id.file_id); + return &file->vars[id.local_id]; +} + +TypeDef* Resolve(Database* db, TypeId id) { + return Resolve(&db->files[id.file_id], id); +} +FuncDef* Resolve(Database* db, FuncId id) { + return Resolve(&db->files[id.file_id], id); +} +VarDef* Resolve(Database* db, VarId id) { + return Resolve(&db->files[id.file_id], id); +} + + +struct NamespaceStack { + std::vector stack; + + void Push(const std::string& ns); + void Pop(); + std::string ComputeQualifiedPrefix(); +}; + +void NamespaceStack::Push(const std::string& ns) { + stack.push_back(ns); +} + +void NamespaceStack::Pop() { + stack.pop_back(); +} + +std::string NamespaceStack::ComputeQualifiedPrefix() { + std::string result; + for (const std::string& ns : stack) + result += ns + "::"; + return result; +} + + + + +struct FuncDefinitionParam {}; + +clang::VisiterResult VisitFuncDefinition(clang::Cursor cursor, clang::Cursor parent, FuncDefinitionParam* param) { /* - echo "Parsing ", filename - let index : libclang.CXIndex = libclang.createIndex(#[excludeDeclsFromPCH]# 0, #[displayDiagnostics]# 0) + switch (cursor.get_kind()) { - let translationUnit : libclang.CXTranslationUnit = - libclang.parseTranslationUnit(index, filename.cstring, nil, 0, nil, 0, 0) - if translationUnit == nil : - echo "Error: cannot create translation unit for '", filename, "'" - return - - let cursor = libclang.getTranslationUnitCursor(translationUnit) - - var param : VisitFileParam - param.db = db - - echo "=== START AST ===" - # printChildren(cursor, 0) - echo "=== DONE AST ===" - - var file : File - file.path = filename - file.vars = newList[VarRef]() - file.funcs = newList[FuncRef]() - file.types = newList[TypeRef]() - - visitChildren(cursor, visitFile, addr param) - - libclang.disposeTranslationUnit(translationUnit) - libclang.disposeIndex(index) + default: + std::cerr << "Unhandled VisitFuncDefinition kind " << clang::ToString(cursor.get_kind()) << std::endl; + break; + } */ + return clang::VisiterResult::Break; +} + +void HandleFunc(ParsingDatabase* db, NamespaceStack* ns, clang::Cursor func, std::optional declaringType) { + // What this method must process: + // - function declaration + // - function definition + // - method declaration + // - method inline definition + // - method definition + + // TODO: Make sure we only process once for declared/defined types. + + // TODO: method_definition_in_namespace.cc is failing because we process decl with correct declaringType, but + // processing method definition fails to resolve correct declaringType. + //if (func.is_definition()) // RM after addressed above + // return; // RM after addressed above + + FuncId id = db->ToFuncId(func.get_usr()); + db->Resolve(id)->shortName = func.get_spelling(); + + std::string typeName; + if (declaringType) + typeName = db->Resolve(declaringType.value())->shortName + "::"; + db->Resolve(id)->qualifiedName = ns->ComputeQualifiedPrefix() + typeName + func.get_spelling(); + std::cout << func.get_usr() << ": Set qualified name to " << db->Resolve(id)->qualifiedName << std::endl; + + //std::cout << "!! HandleFunc " << func.get_type_description() << std::endl; + //std::cout << " comment: " << func.get_comments() << std::endl; + //std::cout << " spelling: " << func.get_spelling() << std::endl; + //for (clang::Cursor argument : func.get_arguments()) + // std::cout << " arg: " << clang::ToString(argument.get_kind()) << " " << argument.get_spelling() << std::endl; +} + + + + + +struct ClassDeclParam { + ParsingDatabase* db; + NamespaceStack* ns; + TypeId active_type; + + ClassDeclParam(ParsingDatabase* db, NamespaceStack* ns, TypeId active_type) + : db(db), ns(ns), active_type(active_type) {} +}; + +clang::VisiterResult VisitClassDecl(clang::Cursor cursor, clang::Cursor parent, ClassDeclParam* param) { + ParsingDatabase* db = param->db; + + switch (cursor.get_kind()) { + case CXCursor_CXXMethod: + HandleFunc(param->db, param->ns, cursor, param->active_type); + break; + + default: + std::cerr << "Unhandled VisitClassDecl kind " << clang::ToString(cursor.get_kind()) << std::endl; + break; + } + + return clang::VisiterResult::Continue; +} + +void HandleClassDecl(clang::Cursor cursor, ParsingDatabase* db, NamespaceStack* ns) { + TypeId active_type = db->ToTypeId(cursor.get_usr()); + db->Resolve(active_type)->shortName = cursor.get_spelling(); + db->Resolve(active_type)->qualifiedName = ns->ComputeQualifiedPrefix() + cursor.get_spelling(); + + ClassDeclParam classDeclParam(db, ns, active_type); + cursor.VisitChildren(&VisitClassDecl, &classDeclParam); +} + + + + + + +struct FileParam { + ParsingDatabase* db; + NamespaceStack* ns; + + FileParam(ParsingDatabase* db, NamespaceStack* ns) : db(db), ns(ns) {} +}; + +clang::VisiterResult VisitFile(clang::Cursor cursor, clang::Cursor parent, FileParam* param) { + switch (cursor.get_kind()) { + case CXCursor_Namespace: + // For a namespace, visit the children of the namespace, but this time with + // a pushed namespace stack. + param->ns->Push(cursor.get_display_name()); + cursor.VisitChildren(&VisitFile, param); + param->ns->Pop(); + break; + + case CXCursor_ClassDecl: + // TODO: Cleanup Handle* param order. + HandleClassDecl(cursor, param->db, param->ns); + break; + + case CXCursor_CXXMethod: + case CXCursor_FunctionDecl: + HandleFunc(param->db, param->ns, cursor, std::nullopt); + break; + + default: + std::cerr << "Unhandled VisitFile kind " << clang::ToString(cursor.get_kind()) << std::endl; + break; + } + + return clang::VisiterResult::Continue; +} + + + + + + + + + + + +clang::VisiterResult DumpVisitor(clang::Cursor cursor, clang::Cursor parent, int* level) { + for (int i = 0; i < *level; ++i) + std::cout << " "; + std::cout << cursor.get_spelling() << " " << clang::ToString(cursor.get_kind()) << std::endl; + + *level += 1; + cursor.VisitChildren(&DumpVisitor, level); + *level -= 1; + + return clang::VisiterResult::Continue; +} + +void Dump(clang::Cursor cursor) { + int level = 0; + cursor.VisitChildren(&DumpVisitor, &level); +} + +ParsingDatabase Parse(std::string filename) { std::vector args; clang::Index index(0 /*excludeDeclarationsFromPCH*/, 0 /*displayDiagnostics*/); - clang::TranslationUnit translationUnit(index, "test.cc", args); + clang::TranslationUnit tu(index, filename, args); - int level = 0; - auto cursor = clang_getTranslationUnitCursor(translationUnit.cx_tu); - clang_visitChildren(cursor, &visitPrint, &level); - /* - TranslationUnit(Index &index, - const std::string &file_path, - const std::vector &command_line_args, - const std::string &buffer, - unsigned flags = DefaultFlags()); - TranslationUnit(Index &index, - const std::string &file_path, - const std::vector &command_line_args, - unsigned flags = DefaultFlags()); - */ + //std::cout << "Start document dump" << std::endl; + //Dump(tu.document_cursor()); + //std::cout << "Done document dump" << std::endl << std::endl; + + ParsingDatabase db; + NamespaceStack ns; + FileParam fileParam(&db, &ns); + tu.document_cursor().VisitChildren(&VisitFile, &fileParam); + return db; +} + +template +bool AreEqual(const std::vector& a, const std::vector& b) { + if (a.size() != b.size()) + return false; + + for (int i = 0; i < a.size(); ++i) { + if (a[i] != b[i]) + return false; + } + + return true; +} + +void Write(const std::vector& strs) { + for (const std::string& str : strs) { + std::cout << str << std::endl; + } +} + +int main(int argc, char** argv) { + for (std::string path : GetFilesInFolder("tests")) { + // TODO: Fix all existing tests. + if (path != "tests/method_definition_in_namespace.cc") continue; + + std::vector expected_output; + ParseTestExpectation(path, &expected_output); + + std::cout << "[START] " << path << std::endl; + + ParsingDatabase db = Parse(path); + std::vector actual_output = db.ToString(); + + if (AreEqual(expected_output, actual_output)) { + std::cout << "[PASSED] " << path << std::endl; + } + else { + std::cout << "[FAILED] " << path << std::endl; + std::cout << "Expected output for " << path << ":" << std::endl; + Write(expected_output); + std::cout << "Actual output for " << path << ":" << std::endl; + Write(actual_output); + break; + } + } std::cin.get(); + return 0; } \ No newline at end of file diff --git a/test.cc b/test.cc index 818f97da..91f29bbe 100644 --- a/test.cc +++ b/test.cc @@ -1,2 +1,2 @@ -void bar() {} +void bar(int a, int b) {} void foo() {} \ No newline at end of file diff --git a/tests/function_declaration.cc b/tests/function_declaration.cc new file mode 100644 index 00000000..94fbe787 --- /dev/null +++ b/tests/function_declaration.cc @@ -0,0 +1,9 @@ +void foo(int a, int b); + +/* +OUTPUT: +Types: +Funcs: + foo +Vars: +*/ \ No newline at end of file diff --git a/tests/function_declaration_in_namespace.cc b/tests/function_declaration_in_namespace.cc new file mode 100644 index 00000000..e0db6a0b --- /dev/null +++ b/tests/function_declaration_in_namespace.cc @@ -0,0 +1,11 @@ +namespace hello { +void foo(int a, int b); +} + +/* +OUTPUT: +Types: +Funcs: + hello::foo +Vars: +*/ \ No newline at end of file diff --git a/tests/function_definition.cc b/tests/function_definition.cc new file mode 100644 index 00000000..1d3bbce9 --- /dev/null +++ b/tests/function_definition.cc @@ -0,0 +1,9 @@ +void foo(int a, int b) {} + +/* +OUTPUT: +Types: +Funcs: + foo +Vars: +*/ \ No newline at end of file diff --git a/tests/function_definition_in_namespace.cc b/tests/function_definition_in_namespace.cc new file mode 100644 index 00000000..d9fb4838 --- /dev/null +++ b/tests/function_definition_in_namespace.cc @@ -0,0 +1,11 @@ +namespace hello { +void foo(int a, int b) {} +} + +/* +OUTPUT: +Types: +Funcs: + hello::foo +Vars: +*/ \ No newline at end of file diff --git a/tests/method_declaration.cc b/tests/method_declaration.cc new file mode 100644 index 00000000..970b08c9 --- /dev/null +++ b/tests/method_declaration.cc @@ -0,0 +1,11 @@ +class Foo { + void foo(); +}; + +/* +OUTPUT: +Types: +Funcs: + foo +Vars: +*/ \ No newline at end of file diff --git a/tests/method_declaration_in_namespace.cc b/tests/method_declaration_in_namespace.cc new file mode 100644 index 00000000..ff3b7f37 --- /dev/null +++ b/tests/method_declaration_in_namespace.cc @@ -0,0 +1,13 @@ +namespace hello { +class Foo { + void foo(); +}; +} + +/* +OUTPUT: +Types: +Funcs: + hello::Foo::foo +Vars: +*/ \ No newline at end of file diff --git a/tests/method_definition.cc b/tests/method_definition.cc new file mode 100644 index 00000000..3d4015d5 --- /dev/null +++ b/tests/method_definition.cc @@ -0,0 +1,13 @@ +class Foo { + void foo(); +}; + +void Foo::foo() {} + +/* +OUTPUT: +Types: +Funcs: + foo +Vars: +*/ \ No newline at end of file diff --git a/tests/method_definition_in_namespace.cc b/tests/method_definition_in_namespace.cc new file mode 100644 index 00000000..d3d77f1e --- /dev/null +++ b/tests/method_definition_in_namespace.cc @@ -0,0 +1,15 @@ +namespace hello { +class Foo { + void foo(); +}; + +void Foo::foo() {} +} + +/* +OUTPUT: +Types: +Funcs: + hello::Foo::foo +Vars: +*/ \ No newline at end of file diff --git a/tests/method_inline_declaration.cc b/tests/method_inline_declaration.cc new file mode 100644 index 00000000..cfd05474 --- /dev/null +++ b/tests/method_inline_declaration.cc @@ -0,0 +1,11 @@ +class Foo { + void foo() {} +}; + +/* +OUTPUT: +Types: +Funcs: + foo +Vars: +*/ \ No newline at end of file diff --git a/tests/method_inline_declaration_in_namespace.cc b/tests/method_inline_declaration_in_namespace.cc new file mode 100644 index 00000000..571c3b8c --- /dev/null +++ b/tests/method_inline_declaration_in_namespace.cc @@ -0,0 +1,13 @@ +namespace hello { +class Foo { + void foo() {} +}; +} + +/* +OUTPUT: +Types: +Funcs: + hello::Foo::foo +Vars: +*/ \ No newline at end of file diff --git a/tinydir.h b/tinydir.h new file mode 100644 index 00000000..2d4418bc --- /dev/null +++ b/tinydir.h @@ -0,0 +1,804 @@ +/* +Copyright (c) 2013-2016, tinydir authors: +- Cong Xu +- Lautis Sun +- Baudouin Feildel +- Andargor +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ +#ifndef TINYDIR_H +#define TINYDIR_H + +#ifdef __cplusplus +extern "C" { +#endif + +#if ((defined _UNICODE) && !(defined UNICODE)) +#define UNICODE +#endif + +#if ((defined UNICODE) && !(defined _UNICODE)) +#define _UNICODE +#endif + +#include +#include +#include +#ifdef _MSC_VER +# define WIN32_LEAN_AND_MEAN +# include +# include +# pragma warning(push) +# pragma warning (disable : 4996) +#else +# include +# include +# include +# include +#endif +#ifdef __MINGW32__ +# include +#endif + + +/* types */ + +/* Windows UNICODE wide character support */ +#if defined _MSC_VER || defined __MINGW32__ +#define _tinydir_char_t TCHAR +#define TINYDIR_STRING(s) _TEXT(s) +#define _tinydir_strlen _tcslen +#define _tinydir_strcpy _tcscpy +#define _tinydir_strcat _tcscat +#define _tinydir_strcmp _tcscmp +#define _tinydir_strrchr _tcsrchr +#define _tinydir_strncmp _tcsncmp +#else +#define _tinydir_char_t char +#define TINYDIR_STRING(s) s +#define _tinydir_strlen strlen +#define _tinydir_strcpy strcpy +#define _tinydir_strcat strcat +#define _tinydir_strcmp strcmp +#define _tinydir_strrchr strrchr +#define _tinydir_strncmp strncmp +#endif + +#if (defined _MSC_VER || defined __MINGW32__) +#include +#define _TINYDIR_PATH_MAX MAX_PATH +#elif defined __linux__ +#include +#define _TINYDIR_PATH_MAX PATH_MAX +#else +#define _TINYDIR_PATH_MAX 4096 +#endif + +#ifdef _MSC_VER +/* extra chars for the "\\*" mask */ +# define _TINYDIR_PATH_EXTRA 2 +#else +# define _TINYDIR_PATH_EXTRA 0 +#endif + +#define _TINYDIR_FILENAME_MAX 256 + +#if (defined _MSC_VER || defined __MINGW32__) +#define _TINYDIR_DRIVE_MAX 3 +#endif + +#ifdef _MSC_VER +# define _TINYDIR_FUNC static __inline +#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L +# define _TINYDIR_FUNC static __inline__ +#else +# define _TINYDIR_FUNC static inline +#endif + +/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */ +#ifdef TINYDIR_USE_READDIR_R + +/* readdir_r is a POSIX-only function, and may not be available under various + * environments/settings, e.g. MinGW. Use readdir fallback */ +#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\ + _POSIX_SOURCE +# define _TINYDIR_HAS_READDIR_R +#endif +#if _POSIX_C_SOURCE >= 200112L +# define _TINYDIR_HAS_FPATHCONF +# include +#endif +#if _BSD_SOURCE || _SVID_SOURCE || \ + (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700) +# define _TINYDIR_HAS_DIRFD +# include +#endif +#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\ + defined _PC_NAME_MAX +# define _TINYDIR_USE_FPATHCONF +#endif +#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\ + !(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX) +# define _TINYDIR_USE_READDIR +#endif + +/* Use readdir by default */ +#else +# define _TINYDIR_USE_READDIR +#endif + +/* MINGW32 has two versions of dirent, ASCII and UNICODE*/ +#ifndef _MSC_VER +#if (defined __MINGW32__) && (defined _UNICODE) +#define _TINYDIR_DIR _WDIR +#define _tinydir_dirent _wdirent +#define _tinydir_opendir _wopendir +#define _tinydir_readdir _wreaddir +#define _tinydir_closedir _wclosedir +#else +#define _TINYDIR_DIR DIR +#define _tinydir_dirent dirent +#define _tinydir_opendir opendir +#define _tinydir_readdir readdir +#define _tinydir_closedir closedir +#endif +#endif + +/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */ +#if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE) +#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE) +#else +#error "Either define both alloc and free or none of them!" +#endif + +#if !defined(_TINYDIR_MALLOC) + #define _TINYDIR_MALLOC(_size) malloc(_size) + #define _TINYDIR_FREE(_ptr) free(_ptr) +#endif /* !defined(_TINYDIR_MALLOC) */ + +typedef struct tinydir_file +{ + _tinydir_char_t path[_TINYDIR_PATH_MAX]; + _tinydir_char_t name[_TINYDIR_FILENAME_MAX]; + _tinydir_char_t *extension; + int is_dir; + int is_reg; + +#ifndef _MSC_VER +#ifdef __MINGW32__ + struct _stat _s; +#else + struct stat _s; +#endif +#endif +} tinydir_file; + +typedef struct tinydir_dir +{ + _tinydir_char_t path[_TINYDIR_PATH_MAX]; + int has_next; + size_t n_files; + + tinydir_file *_files; +#ifdef _MSC_VER + HANDLE _h; + WIN32_FIND_DATA _f; +#else + _TINYDIR_DIR *_d; + struct _tinydir_dirent *_e; +#ifndef _TINYDIR_USE_READDIR + struct _tinydir_dirent *_ep; +#endif +#endif +} tinydir_dir; + + +/* declarations */ + +_TINYDIR_FUNC +int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path); +_TINYDIR_FUNC +int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path); +_TINYDIR_FUNC +void tinydir_close(tinydir_dir *dir); + +_TINYDIR_FUNC +int tinydir_next(tinydir_dir *dir); +_TINYDIR_FUNC +int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file); +_TINYDIR_FUNC +int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i); +_TINYDIR_FUNC +int tinydir_open_subdir_n(tinydir_dir *dir, size_t i); + +_TINYDIR_FUNC +int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path); +_TINYDIR_FUNC +void _tinydir_get_ext(tinydir_file *file); +_TINYDIR_FUNC +int _tinydir_file_cmp(const void *a, const void *b); +#ifndef _MSC_VER +#ifndef _TINYDIR_USE_READDIR +_TINYDIR_FUNC +size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp); +#endif +#endif + + +/* definitions*/ + +_TINYDIR_FUNC +int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path) +{ +#ifndef _MSC_VER +#ifndef _TINYDIR_USE_READDIR + int error; + int size; /* using int size */ +#endif +#else + _tinydir_char_t path_buf[_TINYDIR_PATH_MAX]; +#endif + _tinydir_char_t *pathp; + + if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0) + { + errno = EINVAL; + return -1; + } + if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) + { + errno = ENAMETOOLONG; + return -1; + } + + /* initialise dir */ + dir->_files = NULL; +#ifdef _MSC_VER + dir->_h = INVALID_HANDLE_VALUE; +#else + dir->_d = NULL; +#ifndef _TINYDIR_USE_READDIR + dir->_ep = NULL; +#endif +#endif + tinydir_close(dir); + + _tinydir_strcpy(dir->path, path); + /* Remove trailing slashes */ + pathp = &dir->path[_tinydir_strlen(dir->path) - 1]; + while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/'))) + { + *pathp = TINYDIR_STRING('\0'); + pathp++; + } +#ifdef _MSC_VER + _tinydir_strcpy(path_buf, dir->path); + _tinydir_strcat(path_buf, TINYDIR_STRING("\\*")); + dir->_h = FindFirstFile(path_buf, &dir->_f); + if (dir->_h == INVALID_HANDLE_VALUE) + { + errno = ENOENT; +#else + dir->_d = _tinydir_opendir(path); + if (dir->_d == NULL) + { +#endif + goto bail; + } + + /* read first file */ + dir->has_next = 1; +#ifndef _MSC_VER +#ifdef _TINYDIR_USE_READDIR + dir->_e = _tinydir_readdir(dir->_d); +#else + /* allocate dirent buffer for readdir_r */ + size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */ + if (size == -1) return -1; + dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size); + if (dir->_ep == NULL) return -1; + + error = readdir_r(dir->_d, dir->_ep, &dir->_e); + if (error != 0) return -1; +#endif + if (dir->_e == NULL) + { + dir->has_next = 0; + } +#endif + + return 0; + +bail: + tinydir_close(dir); + return -1; +} + +_TINYDIR_FUNC +int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path) +{ + /* Count the number of files first, to pre-allocate the files array */ + size_t n_files = 0; + if (tinydir_open(dir, path) == -1) + { + return -1; + } + while (dir->has_next) + { + n_files++; + if (tinydir_next(dir) == -1) + { + goto bail; + } + } + tinydir_close(dir); + + if (tinydir_open(dir, path) == -1) + { + return -1; + } + + dir->n_files = 0; + dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files); + if (dir->_files == NULL) + { + goto bail; + } + while (dir->has_next) + { + tinydir_file *p_file; + dir->n_files++; + + p_file = &dir->_files[dir->n_files - 1]; + if (tinydir_readfile(dir, p_file) == -1) + { + goto bail; + } + + if (tinydir_next(dir) == -1) + { + goto bail; + } + + /* Just in case the number of files has changed between the first and + second reads, terminate without writing into unallocated memory */ + if (dir->n_files == n_files) + { + break; + } + } + + qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp); + + return 0; + +bail: + tinydir_close(dir); + return -1; +} + +_TINYDIR_FUNC +void tinydir_close(tinydir_dir *dir) +{ + if (dir == NULL) + { + return; + } + + memset(dir->path, 0, sizeof(dir->path)); + dir->has_next = 0; + dir->n_files = 0; + _TINYDIR_FREE(dir->_files); + dir->_files = NULL; +#ifdef _MSC_VER + if (dir->_h != INVALID_HANDLE_VALUE) + { + FindClose(dir->_h); + } + dir->_h = INVALID_HANDLE_VALUE; +#else + if (dir->_d) + { + _tinydir_closedir(dir->_d); + } + dir->_d = NULL; + dir->_e = NULL; +#ifndef _TINYDIR_USE_READDIR + _TINYDIR_FREE(dir->_ep); + dir->_ep = NULL; +#endif +#endif +} + +_TINYDIR_FUNC +int tinydir_next(tinydir_dir *dir) +{ + if (dir == NULL) + { + errno = EINVAL; + return -1; + } + if (!dir->has_next) + { + errno = ENOENT; + return -1; + } + +#ifdef _MSC_VER + if (FindNextFile(dir->_h, &dir->_f) == 0) +#else +#ifdef _TINYDIR_USE_READDIR + dir->_e = _tinydir_readdir(dir->_d); +#else + if (dir->_ep == NULL) + { + return -1; + } + if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0) + { + return -1; + } +#endif + if (dir->_e == NULL) +#endif + { + dir->has_next = 0; +#ifdef _MSC_VER + if (GetLastError() != ERROR_SUCCESS && + GetLastError() != ERROR_NO_MORE_FILES) + { + tinydir_close(dir); + errno = EIO; + return -1; + } +#endif + } + + return 0; +} + +_TINYDIR_FUNC +int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file) +{ + if (dir == NULL || file == NULL) + { + errno = EINVAL; + return -1; + } +#ifdef _MSC_VER + if (dir->_h == INVALID_HANDLE_VALUE) +#else + if (dir->_e == NULL) +#endif + { + errno = ENOENT; + return -1; + } + if (_tinydir_strlen(dir->path) + + _tinydir_strlen( +#ifdef _MSC_VER + dir->_f.cFileName +#else + dir->_e->d_name +#endif + ) + 1 + _TINYDIR_PATH_EXTRA >= + _TINYDIR_PATH_MAX) + { + /* the path for the file will be too long */ + errno = ENAMETOOLONG; + return -1; + } + if (_tinydir_strlen( +#ifdef _MSC_VER + dir->_f.cFileName +#else + dir->_e->d_name +#endif + ) >= _TINYDIR_FILENAME_MAX) + { + errno = ENAMETOOLONG; + return -1; + } + + _tinydir_strcpy(file->path, dir->path); + _tinydir_strcat(file->path, TINYDIR_STRING("/")); + _tinydir_strcpy(file->name, +#ifdef _MSC_VER + dir->_f.cFileName +#else + dir->_e->d_name +#endif + ); + _tinydir_strcat(file->path, file->name); +#ifndef _MSC_VER +#ifdef __MINGW32__ + if (_tstat( +#else + if (stat( +#endif + file->path, &file->_s) == -1) + { + return -1; + } +#endif + _tinydir_get_ext(file); + + file->is_dir = +#ifdef _MSC_VER + !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY); +#else + S_ISDIR(file->_s.st_mode); +#endif + file->is_reg = +#ifdef _MSC_VER + !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) || + ( + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) && + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) && +#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) && +#endif +#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) && +#endif + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) && + !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY)); +#else + S_ISREG(file->_s.st_mode); +#endif + + return 0; +} + +_TINYDIR_FUNC +int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i) +{ + if (dir == NULL || file == NULL) + { + errno = EINVAL; + return -1; + } + if (i >= dir->n_files) + { + errno = ENOENT; + return -1; + } + + memcpy(file, &dir->_files[i], sizeof(tinydir_file)); + _tinydir_get_ext(file); + + return 0; +} + +_TINYDIR_FUNC +int tinydir_open_subdir_n(tinydir_dir *dir, size_t i) +{ + _tinydir_char_t path[_TINYDIR_PATH_MAX]; + if (dir == NULL) + { + errno = EINVAL; + return -1; + } + if (i >= dir->n_files || !dir->_files[i].is_dir) + { + errno = ENOENT; + return -1; + } + + _tinydir_strcpy(path, dir->_files[i].path); + tinydir_close(dir); + if (tinydir_open_sorted(dir, path) == -1) + { + return -1; + } + + return 0; +} + +/* Open a single file given its path */ +_TINYDIR_FUNC +int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path) +{ + tinydir_dir dir; + int result = 0; + int found = 0; + _tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX]; + _tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX]; + _tinydir_char_t *dir_name; + _tinydir_char_t *base_name; +#if (defined _MSC_VER || defined __MINGW32__) + _tinydir_char_t drive_buf[_TINYDIR_DRIVE_MAX]; + _tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX]; +#endif + + if (file == NULL || path == NULL || _tinydir_strlen(path) == 0) + { + errno = EINVAL; + return -1; + } + if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX) + { + errno = ENAMETOOLONG; + return -1; + } + + /* Get the parent path */ +#if (defined _MSC_VER || defined __MINGW32__) +#if ((defined _MSC_VER) && (_MSC_VER >= 1400)) + _tsplitpath_s( + path, + drive_buf, _TINYDIR_DRIVE_MAX, + dir_name_buf, _TINYDIR_FILENAME_MAX, + file_name_buf, _TINYDIR_FILENAME_MAX, + ext_buf, _TINYDIR_FILENAME_MAX); +#else + _tsplitpath( + path, + drive_buf, + dir_name_buf, + file_name_buf, + ext_buf); +#endif + +/* _splitpath_s not work fine with only filename and widechar support */ +#ifdef _UNICODE + if (drive_buf[0] == L'\xFEFE') + drive_buf[0] = '\0'; + if (dir_name_buf[0] == L'\xFEFE') + dir_name_buf[0] = '\0'; +#endif + + if (errno) + { + errno = EINVAL; + return -1; + } + /* Emulate the behavior of dirname by returning "." for dir name if it's + empty */ + if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0') + { + _tinydir_strcpy(dir_name_buf, TINYDIR_STRING(".")); + } + /* Concatenate the drive letter and dir name to form full dir name */ + _tinydir_strcat(drive_buf, dir_name_buf); + dir_name = drive_buf; + /* Concatenate the file name and extension to form base name */ + _tinydir_strcat(file_name_buf, ext_buf); + base_name = file_name_buf; +#else + _tinydir_strcpy(dir_name_buf, path); + dir_name = dirname(dir_name_buf); + _tinydir_strcpy(file_name_buf, path); + base_name =basename(file_name_buf); +#endif + + /* Open the parent directory */ + if (tinydir_open(&dir, dir_name) == -1) + { + return -1; + } + + /* Read through the parent directory and look for the file */ + while (dir.has_next) + { + if (tinydir_readfile(&dir, file) == -1) + { + result = -1; + goto bail; + } + if (_tinydir_strcmp(file->name, base_name) == 0) + { + /* File found */ + found = 1; + break; + } + tinydir_next(&dir); + } + if (!found) + { + result = -1; + errno = ENOENT; + } + +bail: + tinydir_close(&dir); + return result; +} + +_TINYDIR_FUNC +void _tinydir_get_ext(tinydir_file *file) +{ + _tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.')); + if (period == NULL) + { + file->extension = &(file->name[_tinydir_strlen(file->name)]); + } + else + { + file->extension = period + 1; + } +} + +_TINYDIR_FUNC +int _tinydir_file_cmp(const void *a, const void *b) +{ + const tinydir_file *fa = (const tinydir_file *)a; + const tinydir_file *fb = (const tinydir_file *)b; + if (fa->is_dir != fb->is_dir) + { + return -(fa->is_dir - fb->is_dir); + } + return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX); +} + +#ifndef _MSC_VER +#ifndef _TINYDIR_USE_READDIR +/* +The following authored by Ben Hutchings +from https://womble.decadent.org.uk/readdir_r-advisory.html +*/ +/* Calculate the required buffer size (in bytes) for directory * +* entries read from the given directory handle. Return -1 if this * +* this cannot be done. * +* * +* This code does not trust values of NAME_MAX that are less than * +* 255, since some systems (including at least HP-UX) incorrectly * +* define it to be a smaller value. */ +_TINYDIR_FUNC +size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp) +{ + long name_max; + size_t name_end; + /* parameter may be unused */ + (void)dirp; + +#if defined _TINYDIR_USE_FPATHCONF + name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX); + if (name_max == -1) +#if defined(NAME_MAX) + name_max = (NAME_MAX > 255) ? NAME_MAX : 255; +#else + return (size_t)(-1); +#endif +#elif defined(NAME_MAX) + name_max = (NAME_MAX > 255) ? NAME_MAX : 255; +#else +#error "buffer size for readdir_r cannot be determined" +#endif + name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1; + return (name_end > sizeof(struct _tinydir_dirent) ? + name_end : sizeof(struct _tinydir_dirent)); +} +#endif +#endif + +#ifdef __cplusplus +} +#endif + +# if defined (_MSC_VER) +# pragma warning(pop) +# endif + +#endif diff --git a/utils.cc b/utils.cc new file mode 100644 index 00000000..8a5732e6 --- /dev/null +++ b/utils.cc @@ -0,0 +1,68 @@ +#include "utils.h" + +#include + +#include "tinydir.h" + +std::vector GetFilesInFolder(std::string folder) { + std::vector result; + + tinydir_dir dir; + if (tinydir_open(&dir, folder.c_str()) == -1) { + perror("Error opening file"); + goto bail; + } + + while (dir.has_next) { + tinydir_file file; + if (tinydir_readfile(&dir, &file) == -1) { + perror("Error getting file"); + goto bail; + } + + std::string full_path = folder + "/" + file.name; + if (file.is_dir) { + if (strcmp(file.name, ".") != 0 && strcmp(file.name, "..") != 0) { + for (std::string nested_file : GetFilesInFolder(full_path)) + result.push_back(nested_file); + } + } + else { + result.push_back(full_path); + } + + if (tinydir_next(&dir) == -1) { + perror("Error getting next file"); + goto bail; + } + } + +bail: + tinydir_close(&dir); + return result; +} + +std::vector ReadLines(std::string filename) { + std::vector result; + + std::ifstream input(filename); + for (std::string line; getline(input, line); ) + result.push_back(line); + + return result; +} + +void ParseTestExpectation(std::string filename, std::vector* expected_output) { + bool in_output = false; + + for (std::string line : ReadLines(filename)) { + if (line == "*/") + break; + + if (in_output) + expected_output->push_back(line); + + if (line == "OUTPUT:") + in_output = true; + } +} diff --git a/utils.h b/utils.h new file mode 100644 index 00000000..971d8ca5 --- /dev/null +++ b/utils.h @@ -0,0 +1,8 @@ +#pragma once + +#include +#include + +std::vector GetFilesInFolder(std::string folder); +std::vector ReadLines(std::string filename); +void ParseTestExpectation(std::string filename, std::vector* expected_output); \ No newline at end of file