mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-24 16:45:07 +00:00
Update
This commit is contained in:
parent
5dbde940b6
commit
0dc27bd3ac
@ -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));
|
||||
}
|
||||
|
@ -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<std::string> clang::CompileCommand::get_command_as_args() {
|
||||
unsigned N = clang_CompileCommand_getNumArgs(cx_command);
|
||||
std::vector<std::string> 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;
|
||||
}
|
||||
|
@ -15,13 +15,13 @@ unsigned clang::CompletionString::get_num_chunks() {
|
||||
std::vector<clang::CompletionChunk> clang::CompletionString::get_chunks() {
|
||||
std::vector<CompletionChunk> res;
|
||||
for (unsigned i = 0; i < get_num_chunks(); i++) {
|
||||
res.emplace_back(to_string(clang_getCompletionChunkText(cx_completion_sting, i)), static_cast<CompletionChunkKind> (clang_getCompletionChunkKind(cx_completion_sting, i)));
|
||||
res.emplace_back(ToString(clang_getCompletionChunkText(cx_completion_sting, i)), static_cast<CompletionChunkKind> (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) :
|
||||
|
@ -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> 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 "";
|
||||
}
|
||||
|
@ -3,49 +3,76 @@
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <clang-c/Index.h>
|
||||
#include <type_traits>
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
#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<Cursor> 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<Cursor> get_arguments() const;
|
||||
bool is_valid_kind() const;
|
||||
std::string get_type_description() const;
|
||||
std::string get_comments() const;
|
||||
|
||||
template<typename TClientData>
|
||||
using Visitor = VisiterResult(*)(Cursor cursor, Cursor parent, TClientData* client_data);
|
||||
|
||||
enum class VisitResult {
|
||||
Completed, EndedEarly
|
||||
};
|
||||
|
||||
template<typename TClientData>
|
||||
VisitResult VisitChildren(Visitor<TClientData> visitor, TClientData* client_data) const {
|
||||
if (clang_visitChildren(cx_cursor, reinterpret_cast<CXCursorVisitor>(visitor), client_data) == 0)
|
||||
return VisitResult::Completed;
|
||||
return VisitResult::EndedEarly;
|
||||
}
|
||||
|
||||
CXCursor cx_cursor;
|
||||
};
|
||||
} // namespace clang
|
||||
|
||||
#endif // CURSOR_H_
|
||||
|
@ -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<num_fix_its;c++) {
|
||||
CXSourceRange fix_it_range;
|
||||
auto source=to_string(clang_getDiagnosticFixIt(cx_diagnostic, c, &fix_it_range));
|
||||
auto source=ToString(clang_getDiagnosticFixIt(cx_diagnostic, c, &fix_it_range));
|
||||
fix_its.emplace_back(source, SourceRange(fix_it_range).get_offsets());
|
||||
}
|
||||
}
|
||||
|
@ -6,27 +6,28 @@
|
||||
#include "SourceRange.h"
|
||||
|
||||
namespace clang {
|
||||
class Diagnostic {
|
||||
friend class TranslationUnit;
|
||||
Diagnostic(CXTranslationUnit& cx_tu, CXDiagnostic& cx_diagnostic);
|
||||
|
||||
class Diagnostic {
|
||||
friend class TranslationUnit;
|
||||
Diagnostic(CXTranslationUnit& cx_tu, CXDiagnostic& cx_diagnostic);
|
||||
public:
|
||||
class FixIt {
|
||||
public:
|
||||
class FixIt {
|
||||
public:
|
||||
FixIt(const std::string &source, const std::pair<clang::Offset, clang::Offset> &offsets):
|
||||
source(source), offsets(offsets) {}
|
||||
std::string source;
|
||||
std::pair<clang::Offset, clang::Offset> 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<clang::Offset, clang::Offset> &offsets) :
|
||||
source(source), offsets(offsets) {}
|
||||
std::string source;
|
||||
std::pair<clang::Offset, clang::Offset> offsets;
|
||||
std::vector<FixIt> 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<clang::Offset, clang::Offset> offsets;
|
||||
std::vector<FixIt> fix_its;
|
||||
};
|
||||
|
||||
}
|
||||
#endif // DIAGNOSTIC_H_
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -5,45 +5,44 @@
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include <iostream> //TODO: remove
|
||||
using namespace std; //TODO: remove
|
||||
namespace clang {
|
||||
|
||||
clang::TranslationUnit::TranslationUnit(Index &index, const std::string &file_path,
|
||||
const std::vector<std::string> &command_line_args,
|
||||
const std::string &buffer, unsigned flags) {
|
||||
TranslationUnit::TranslationUnit(Index &index, const std::string &file_path,
|
||||
const std::vector<std::string> &command_line_args,
|
||||
const std::string &buffer, unsigned flags) {
|
||||
std::vector<const char*> 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<std::string> &command_line_args,
|
||||
unsigned flags) {
|
||||
TranslationUnit::TranslationUnit(Index &index, const std::string &file_path,
|
||||
const std::vector<std::string> &command_line_args,
|
||||
unsigned flags) {
|
||||
std::vector<const char*> 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<std::string> &command_line_args,
|
||||
const std::map<std::string, std::string> &buffers, unsigned flags) {
|
||||
void TranslationUnit::parse(Index &index, const std::string &file_path,
|
||||
const std::vector<std::string> &command_line_args,
|
||||
const std::map<std::string, std::string> &buffers, unsigned flags) {
|
||||
std::vector<CXUnsavedFile> 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<const char*> 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::Diagnostic> clang::TranslationUnit::get_diagnostics() {
|
||||
std::vector<Diagnostic> TranslationUnit::get_diagnostics() {
|
||||
std::vector<Diagnostic> diagnostics;
|
||||
for(unsigned c=0;c<clang_getNumDiagnostics(cx_tu);c++) {
|
||||
CXDiagnostic clang_diagnostic=clang_getDiagnostic(cx_tu, c);
|
||||
for (unsigned c = 0; c < clang_getNumDiagnostics(cx_tu); c++) {
|
||||
CXDiagnostic clang_diagnostic = clang_getDiagnostic(cx_tu, c);
|
||||
diagnostics.emplace_back(Diagnostic(cx_tu, clang_diagnostic));
|
||||
clang_disposeDiagnostic(clang_diagnostic);
|
||||
}
|
||||
return diagnostics;
|
||||
}
|
||||
|
||||
std::unique_ptr<clang::Tokens> clang::TranslationUnit::get_tokens(unsigned start_offset, unsigned end_offset) {
|
||||
auto path=clang::to_string(clang_getTranslationUnitSpelling(cx_tu));
|
||||
std::unique_ptr<Tokens> 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<Tokens>(new Tokens(cx_tu, range));
|
||||
}
|
||||
|
||||
std::unique_ptr<clang::Tokens> 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<Tokens> 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<Tokens>(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));
|
||||
}
|
||||
|
||||
}
|
@ -44,6 +44,8 @@ namespace clang {
|
||||
std::unique_ptr<Tokens> 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);
|
||||
|
||||
|
@ -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 "<unknown kind>";
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
#ifndef UTILITY_H_
|
||||
#define UTILITY_H_
|
||||
#pragma once
|
||||
|
||||
#include <clang-c/Index.h>
|
||||
#include <string>
|
||||
|
||||
namespace clang {
|
||||
std::string to_string(CXString cx_string);
|
||||
std::string to_string(CXCursorKind cursor_kind);
|
||||
}
|
||||
|
||||
#endif // UTILITY_H_
|
||||
std::string ToString(CXString cx_string);
|
||||
std::string ToString(CXCursorKind cursor_kind);
|
||||
|
||||
} // namespace clang
|
548
main.cpp
548
main.cpp
@ -1,85 +1,513 @@
|
||||
#include <optional>
|
||||
#include <iostream>
|
||||
#include <cstdint>
|
||||
#include <cassert>
|
||||
#include <fstream>
|
||||
|
||||
#include "libclangmm\clangmm.h"
|
||||
#include "libclangmm\Utility.h"
|
||||
|
||||
#include "utils.h"
|
||||
//#include <clang-c\Index.h>
|
||||
|
||||
CXChildVisitResult visitPrint(CXCursor cursor0, CXCursor parent, CXClientData param) {
|
||||
int* level = static_cast<int*>(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<typename T>
|
||||
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<TypeDef>;
|
||||
using FuncId = Id<FuncDef>;
|
||||
using VarId = Id<VarDef>;
|
||||
|
||||
return VisitResult.Continu
|
||||
*/
|
||||
|
||||
template<typename T>
|
||||
struct Ref {
|
||||
Id<T> id;
|
||||
clang::SourceLocation loc;
|
||||
};
|
||||
using TypeRef = Ref<TypeDef>;
|
||||
using FuncRef = Ref<FuncDef>;
|
||||
using VarRef = Ref<VarDef>;
|
||||
|
||||
|
||||
struct TypeDef {
|
||||
TypeDef(TypeId id);
|
||||
|
||||
// General metadata.
|
||||
TypeId id;
|
||||
std::string usr;
|
||||
std::string shortName;
|
||||
std::string qualifiedName;
|
||||
std::optional<clang::SourceLocation> definition;
|
||||
|
||||
// Immediate parent and immediate derived types.
|
||||
std::vector<TypeId> parents;
|
||||
std::vector<TypeId> derived;
|
||||
|
||||
// Types, functions, and variables defined in this type.
|
||||
std::vector<TypeId> types;
|
||||
std::vector<FuncId> funcs;
|
||||
std::vector<VarId> vars;
|
||||
|
||||
// Usages.
|
||||
std::vector<clang::SourceLocation> 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<clang::SourceLocation> declaration;
|
||||
std::optional<clang::SourceLocation> definition;
|
||||
|
||||
// Type which declares this one (ie, it is a method)
|
||||
std::optional<TypeId> declaringType;
|
||||
// Method this method overrides.
|
||||
std::optional<FuncId> baseFunc;
|
||||
// Methods which directly override this one.
|
||||
std::vector<FuncId> derived;
|
||||
|
||||
// Local variables defined in this function.
|
||||
std::vector<VarId> locals;
|
||||
|
||||
// Functions which call this one.
|
||||
std::vector<FuncRef> callers;
|
||||
// Functions that this function calls.
|
||||
std::vector<FuncRef> callees;
|
||||
|
||||
// Usages.
|
||||
std::vector<clang::SourceLocation> 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<clang::SourceLocation> declaration;
|
||||
std::vector<clang::SourceLocation> initializations;
|
||||
|
||||
// Type of the variable.
|
||||
std::optional<TypeId> variableType;
|
||||
|
||||
// Type which declares this one (ie, it is a method)
|
||||
std::optional<TypeId> declaringType;
|
||||
|
||||
// Usages.
|
||||
std::vector<clang::SourceLocation> 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<std::string, TypeId> usrToTypeId;
|
||||
std::unordered_map<std::string, FuncId> usrToFuncId;
|
||||
std::unordered_map<std::string, VarId> usrToVarId;
|
||||
|
||||
std::vector<TypeDef> types;
|
||||
std::vector<FuncDef> funcs;
|
||||
std::vector<VarDef> 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<std::string> 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<std::string> ParsingDatabase::ToString() {
|
||||
std::vector<std::string> 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<TypeDef> types;
|
||||
std::vector<FuncDef> funcs;
|
||||
std::vector<VarDef> vars;
|
||||
};
|
||||
|
||||
|
||||
|
||||
struct Database {
|
||||
std::unordered_map<std::string, TypeId> usrToTypeId;
|
||||
std::unordered_map<std::string, FuncId> usrToFuncId;
|
||||
std::unordered_map<std::string, VarId> usrToVarId;
|
||||
|
||||
std::vector<FileDef> 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<std::string> 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<TypeId> 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<std::string> 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<std::string> &command_line_args,
|
||||
const std::string &buffer,
|
||||
unsigned flags = DefaultFlags());
|
||||
TranslationUnit(Index &index,
|
||||
const std::string &file_path,
|
||||
const std::vector<std::string> &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<typename T>
|
||||
bool AreEqual(const std::vector<T>& a, const std::vector<T>& 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<std::string>& 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<std::string> expected_output;
|
||||
ParseTestExpectation(path, &expected_output);
|
||||
|
||||
std::cout << "[START] " << path << std::endl;
|
||||
|
||||
ParsingDatabase db = Parse(path);
|
||||
std::vector<std::string> 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;
|
||||
}
|
9
tests/function_declaration.cc
Normal file
9
tests/function_declaration.cc
Normal file
@ -0,0 +1,9 @@
|
||||
void foo(int a, int b);
|
||||
|
||||
/*
|
||||
OUTPUT:
|
||||
Types:
|
||||
Funcs:
|
||||
foo
|
||||
Vars:
|
||||
*/
|
11
tests/function_declaration_in_namespace.cc
Normal file
11
tests/function_declaration_in_namespace.cc
Normal file
@ -0,0 +1,11 @@
|
||||
namespace hello {
|
||||
void foo(int a, int b);
|
||||
}
|
||||
|
||||
/*
|
||||
OUTPUT:
|
||||
Types:
|
||||
Funcs:
|
||||
hello::foo
|
||||
Vars:
|
||||
*/
|
9
tests/function_definition.cc
Normal file
9
tests/function_definition.cc
Normal file
@ -0,0 +1,9 @@
|
||||
void foo(int a, int b) {}
|
||||
|
||||
/*
|
||||
OUTPUT:
|
||||
Types:
|
||||
Funcs:
|
||||
foo
|
||||
Vars:
|
||||
*/
|
11
tests/function_definition_in_namespace.cc
Normal file
11
tests/function_definition_in_namespace.cc
Normal file
@ -0,0 +1,11 @@
|
||||
namespace hello {
|
||||
void foo(int a, int b) {}
|
||||
}
|
||||
|
||||
/*
|
||||
OUTPUT:
|
||||
Types:
|
||||
Funcs:
|
||||
hello::foo
|
||||
Vars:
|
||||
*/
|
11
tests/method_declaration.cc
Normal file
11
tests/method_declaration.cc
Normal file
@ -0,0 +1,11 @@
|
||||
class Foo {
|
||||
void foo();
|
||||
};
|
||||
|
||||
/*
|
||||
OUTPUT:
|
||||
Types:
|
||||
Funcs:
|
||||
foo
|
||||
Vars:
|
||||
*/
|
13
tests/method_declaration_in_namespace.cc
Normal file
13
tests/method_declaration_in_namespace.cc
Normal file
@ -0,0 +1,13 @@
|
||||
namespace hello {
|
||||
class Foo {
|
||||
void foo();
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
OUTPUT:
|
||||
Types:
|
||||
Funcs:
|
||||
hello::Foo::foo
|
||||
Vars:
|
||||
*/
|
13
tests/method_definition.cc
Normal file
13
tests/method_definition.cc
Normal file
@ -0,0 +1,13 @@
|
||||
class Foo {
|
||||
void foo();
|
||||
};
|
||||
|
||||
void Foo::foo() {}
|
||||
|
||||
/*
|
||||
OUTPUT:
|
||||
Types:
|
||||
Funcs:
|
||||
foo
|
||||
Vars:
|
||||
*/
|
15
tests/method_definition_in_namespace.cc
Normal file
15
tests/method_definition_in_namespace.cc
Normal file
@ -0,0 +1,15 @@
|
||||
namespace hello {
|
||||
class Foo {
|
||||
void foo();
|
||||
};
|
||||
|
||||
void Foo::foo() {}
|
||||
}
|
||||
|
||||
/*
|
||||
OUTPUT:
|
||||
Types:
|
||||
Funcs:
|
||||
hello::Foo::foo
|
||||
Vars:
|
||||
*/
|
11
tests/method_inline_declaration.cc
Normal file
11
tests/method_inline_declaration.cc
Normal file
@ -0,0 +1,11 @@
|
||||
class Foo {
|
||||
void foo() {}
|
||||
};
|
||||
|
||||
/*
|
||||
OUTPUT:
|
||||
Types:
|
||||
Funcs:
|
||||
foo
|
||||
Vars:
|
||||
*/
|
13
tests/method_inline_declaration_in_namespace.cc
Normal file
13
tests/method_inline_declaration_in_namespace.cc
Normal file
@ -0,0 +1,13 @@
|
||||
namespace hello {
|
||||
class Foo {
|
||||
void foo() {}
|
||||
};
|
||||
}
|
||||
|
||||
/*
|
||||
OUTPUT:
|
||||
Types:
|
||||
Funcs:
|
||||
hello::Foo::foo
|
||||
Vars:
|
||||
*/
|
804
tinydir.h
Normal file
804
tinydir.h
Normal file
@ -0,0 +1,804 @@
|
||||
/*
|
||||
Copyright (c) 2013-2016, tinydir authors:
|
||||
- Cong Xu
|
||||
- Lautis Sun
|
||||
- Baudouin Feildel
|
||||
- Andargor <andargor@yahoo.com>
|
||||
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 <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#ifdef _MSC_VER
|
||||
# define WIN32_LEAN_AND_MEAN
|
||||
# include <windows.h>
|
||||
# include <tchar.h>
|
||||
# pragma warning(push)
|
||||
# pragma warning (disable : 4996)
|
||||
#else
|
||||
# include <dirent.h>
|
||||
# include <libgen.h>
|
||||
# include <sys/stat.h>
|
||||
# include <stddef.h>
|
||||
#endif
|
||||
#ifdef __MINGW32__
|
||||
# include <tchar.h>
|
||||
#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 <windows.h>
|
||||
#define _TINYDIR_PATH_MAX MAX_PATH
|
||||
#elif defined __linux__
|
||||
#include <linux/limits.h>
|
||||
#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 <unistd.h>
|
||||
#endif
|
||||
#if _BSD_SOURCE || _SVID_SOURCE || \
|
||||
(_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
|
||||
# define _TINYDIR_HAS_DIRFD
|
||||
# include <sys/types.h>
|
||||
#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 <ben@decadent.org.uk>
|
||||
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
|
68
utils.cc
Normal file
68
utils.cc
Normal file
@ -0,0 +1,68 @@
|
||||
#include "utils.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
#include "tinydir.h"
|
||||
|
||||
std::vector<std::string> GetFilesInFolder(std::string folder) {
|
||||
std::vector<std::string> 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<std::string> ReadLines(std::string filename) {
|
||||
std::vector<std::string> 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<std::string>* 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;
|
||||
}
|
||||
}
|
8
utils.h
Normal file
8
utils.h
Normal file
@ -0,0 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
std::vector<std::string> GetFilesInFolder(std::string folder);
|
||||
std::vector<std::string> ReadLines(std::string filename);
|
||||
void ParseTestExpectation(std::string filename, std::vector<std::string>* expected_output);
|
Loading…
Reference in New Issue
Block a user