diff --git a/cmake/Modules/FindLibClang.cmake b/cmake/Modules/FindLibClang.cmake index 8a4ed46..3d15636 100644 --- a/cmake/Modules/FindLibClang.cmake +++ b/cmake/Modules/FindLibClang.cmake @@ -14,7 +14,8 @@ # Known LLVM release numbers. # most recent versions come first -set(LIBCLANG_KNOWN_LLVM_VERSIONS 3.6 +set(LIBCLANG_KNOWN_LLVM_VERSIONS 3.6.1 + 3.6 3.5.1 3.5.0 #Arch Linux 3.5 #LLVM Debian/Ubuntu packages from http://llvm.org/apt/ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6c74fa5..1a1ae1c 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,4 +1,4 @@ -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O3") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -O3 -Wall -Wno-reorder") set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_HOME_DIRECTORY}/cmake/Modules/") @@ -25,6 +25,7 @@ set(header_files Token.h Tokens.h TranslationUnit.h + Diagnostic.h ) set(cc_files CodeCompleteResults.cc @@ -39,6 +40,7 @@ set(cc_files Token.cc Tokens.cc TranslationUnit.cc + Diagnostic.cc ) add_library(${project_name} SHARED ${header_files} ${cc_files}) diff --git a/src/CodeCompleteResults.cc b/src/CodeCompleteResults.cc index bb77346..6a281e8 100644 --- a/src/CodeCompleteResults.cc +++ b/src/CodeCompleteResults.cc @@ -22,10 +22,15 @@ CodeCompleteResults(clang::TranslationUnit *tu, column, files.data(), files.size(), - clang_defaultCodeCompleteOptions()); + clang_defaultCodeCompleteOptions()|CXCodeComplete_IncludeBriefComments); clang_sortCodeCompletionResults(results_->Results, results_->NumResults); } +clang::CodeCompleteResults::~CodeCompleteResults() { + delete[] results_->Results; + delete results_; +} + int clang::CodeCompleteResults:: size() { return results_->NumResults; diff --git a/src/CodeCompleteResults.h b/src/CodeCompleteResults.h index ef05d4c..8446a74 100644 --- a/src/CodeCompleteResults.h +++ b/src/CodeCompleteResults.h @@ -13,6 +13,7 @@ namespace clang { const std::map &buffers, int line_num, int column); + ~CodeCompleteResults(); CompletionString get(int index); int size(); diff --git a/src/CompletionString.cc b/src/CompletionString.cc index 06bc9cb..07dd5bc 100644 --- a/src/CompletionString.cc +++ b/src/CompletionString.cc @@ -26,6 +26,16 @@ get_chunks() { return res; } +std::string clang::CompletionString::get_brief_comments() { + std::string brief_comments; + auto cxstr=clang_getCompletionBriefComment(str_); + if(cxstr.data!=NULL) { + brief_comments=clang_getCString(cxstr); + clang_disposeString(cxstr); + } + return brief_comments; +} + clang::CompletionChunk:: CompletionChunk(std::string chunk, clang::CompletionChunkKind kind) : - chunk_(chunk), kind_(kind) { } + chunk(chunk), kind(kind) { } diff --git a/src/CompletionString.h b/src/CompletionString.h index 040c87a..0018bc3 100644 --- a/src/CompletionString.h +++ b/src/CompletionString.h @@ -21,16 +21,14 @@ namespace clang { class CompletionChunk { public: CompletionChunk(std::string chunk, CompletionChunkKind kind); - const std::string& chunk() const { return chunk_; } - const CompletionChunkKind& kind() const { return kind_; } - private: - std::string chunk_; - CompletionChunkKind kind_; + std::string chunk; + CompletionChunkKind kind; }; class CompletionString { public: std::vector get_chunks(); + std::string get_brief_comments(); int get_num_chunks(); private: explicit CompletionString(const CXCompletionString &str); diff --git a/src/Cursor.h b/src/Cursor.h index f7cd903..5740226 100644 --- a/src/Cursor.h +++ b/src/Cursor.h @@ -174,12 +174,14 @@ namespace clang { class Cursor { public: + Cursor() {} Cursor(TranslationUnit *tu, SourceLocation *source_location); const CursorKind kind(); private: CXCursor cursor_; friend SourceRange; friend SourceLocation; + friend Tokens; }; } // namespace clang #endif // CURSOR_H_ diff --git a/src/Diagnostic.cc b/src/Diagnostic.cc new file mode 100644 index 0000000..5712527 --- /dev/null +++ b/src/Diagnostic.cc @@ -0,0 +1,46 @@ +#include "Diagnostic.h" +#include "SourceLocation.h" +#include "Tokens.h" + +clang::Diagnostic::Diagnostic(clang::TranslationUnit& tu, CXDiagnostic& clang_diagnostic) { + severity=clang_getDiagnosticSeverity(clang_diagnostic); + severity_spelling=get_severity_spelling(severity); + spelling=clang_getCString(clang_getDiagnosticSpelling(clang_diagnostic)); + clang::SourceLocation location(clang_getDiagnosticLocation(clang_diagnostic)); + std::string tmp_path; + unsigned line, column, offset; + location.get_location_info(&tmp_path, &line, &column, &offset); + path=tmp_path; + start_location.line=line; + start_location.column=column; + start_location.offset=offset; + + clang::SourceRange range(&location, &location); + clang::Tokens tokens(&tu, &range); + if(tokens.size()==1) { + auto& token=tokens[0]; + clang::SourceRange range=token.get_source_range(&tu); + clang::SourceLocation location(&range, false); + location.get_location_info(NULL, &line, &column, &offset); + end_location.line=line; + end_location.column=column; + end_location.offset=offset; + } +} + +const std::string clang::Diagnostic::get_severity_spelling(unsigned severity) { + switch(severity) { + case CXDiagnostic_Ignored: + return "Ignored"; + case CXDiagnostic_Note: + return "Note"; + case CXDiagnostic_Warning: + return "Warning"; + case CXDiagnostic_Error: + return "Error"; + case CXDiagnostic_Fatal: + return "Fatal"; + default: + return ""; + } +} \ No newline at end of file diff --git a/src/Diagnostic.h b/src/Diagnostic.h new file mode 100644 index 0000000..c858d33 --- /dev/null +++ b/src/Diagnostic.h @@ -0,0 +1,28 @@ +#ifndef DIAGNOSTIC_H_ +#define DIAGNOSTIC_H_ +#include +#include +#include +#include "TranslationUnit.h" + +namespace clang { + class Diagnostic { + public: + class LocationData { + public: + unsigned line, column, offset; + }; + + Diagnostic(clang::TranslationUnit& tu, CXDiagnostic& clang_diagnostic); + + static const std::string get_severity_spelling(unsigned severity); + + unsigned severity; + std::string severity_spelling; + std::string spelling; + std::string path; + LocationData start_location, end_location; + }; +} + +#endif // DIAGNOSTIC_H_ \ No newline at end of file diff --git a/src/SourceLocation.cc b/src/SourceLocation.cc index 36564df..c68d32f 100644 --- a/src/SourceLocation.cc +++ b/src/SourceLocation.cc @@ -52,7 +52,7 @@ get_location_info(std::string* path, unsigned *offset) { CXFile file; clang_getExpansionLocation(location_, &file, line, column, offset); - if (path != NULL) { + if (path != NULL && file!=NULL) { path->operator=(((clang_getCString((clang_getFileName(file)))))); } } diff --git a/src/SourceLocation.h b/src/SourceLocation.h index e6cd3b0..286ddbb 100644 --- a/src/SourceLocation.h +++ b/src/SourceLocation.h @@ -21,6 +21,8 @@ namespace clang { const std::string &filepath, int offset); + SourceLocation(CXSourceLocation location) {location_=location;} + explicit SourceLocation(Cursor *cursor); void get_location_info(std::string* path, diff --git a/src/SourceRange.cc b/src/SourceRange.cc index d6edc89..87581d4 100644 --- a/src/SourceRange.cc +++ b/src/SourceRange.cc @@ -10,8 +10,6 @@ SourceRange(clang::SourceLocation *start, clang::SourceLocation *end) { range_ = clang_getRange(start->location_, end->location_); } -clang::SourceRange::~SourceRange() { } - clang::SourceRange::SourceRange(Cursor *cursor) { range_ = clang_getCursorExtent(cursor->cursor_); } diff --git a/src/SourceRange.h b/src/SourceRange.h index 8cc9afe..d1f772b 100644 --- a/src/SourceRange.h +++ b/src/SourceRange.h @@ -7,16 +7,17 @@ namespace clang { class SourceRange { public: + SourceRange() {} SourceRange(TranslationUnit *tu, Token *token); SourceRange(SourceLocation *start, SourceLocation *end); explicit SourceRange(Cursor *cursor); - ~SourceRange(); private: CXSourceRange range_; friend Tokens; friend SourceLocation; + friend Diagnostic; }; } // namespace clang #endif // SOURCERANGE_H_ diff --git a/src/Token.h b/src/Token.h index 6ff1ffc..dd8efe4 100644 --- a/src/Token.h +++ b/src/Token.h @@ -19,13 +19,13 @@ namespace clang { std::string get_token_spelling(TranslationUnit *tu); SourceLocation get_source_location(TranslationUnit *tu); SourceRange get_source_range(TranslationUnit *tu); - + std::string type; private: explicit Token(const CXToken &token); friend SourceRange; friend SourceLocation; friend Tokens; - CXToken token_; + const CXToken& token_; }; } // namespace clang #endif // TOKEN_H_ diff --git a/src/Tokens.cc b/src/Tokens.cc index 0b709b5..571d0db 100644 --- a/src/Tokens.cc +++ b/src/Tokens.cc @@ -1,21 +1,136 @@ #include "Tokens.h" -clang::Tokens::Tokens(clang::TranslationUnit *tu, clang::SourceRange *range) { +#include +using namespace std; + +clang::Tokens::Tokens(clang::TranslationUnit *tu, clang::SourceRange *range): tu(*tu) { clang_tokenize(tu->tu_, range->range_, &tokens_, &num_tokens_); for (int i = 0; i < num_tokens_; i++) { - tks.push_back(clang::Token(tokens_[i])); + push_back(clang::Token(tokens_[i])); } } clang::Tokens::~Tokens() { - // instead of using clang_disposeTokens() the implementation - // of the latter method is just free(token_) the same as - // delete(tokens_) eliminating the need of tu* - delete tokens_; + clang_disposeTokens(tu.tu_, tokens_, size()); } -std::vector& clang::Tokens::tokens() { - return tks; +void clang::Tokens::update_types(clang::TranslationUnit *tu) { + clang_cursors.clear(); + clang_cursors.reserve(size()); + clang_annotateTokens(tu->tu_, tokens_, size(), clang_cursors.data()); + + for(size_t c=0;c=4 && spelling.substr(0, 4)=="auto")) { + auto_end=spelling.substr(4); + auto type=clang_getCanonicalType(clang_getCursorType(clang_cursors[c])); + auto cxstr=clang_getTypeSpelling(type); + spelling=clang_getCString(cxstr); + clang_disposeString(cxstr); + if(spelling.find(" ")==std::string::npos) + spelling+=auto_end; + } + + (*this)[c].type=spelling; + //std::cout << clang_getCString(clang_getTypeSpelling(type)) << ": " << type.kind << endl; + ////auto cursor=clang_getTypeDeclaration(type); + ////tks[c].type=clang_getCString(clang_getCursorSpelling(cursor)); + ////auto type=clang_getCursorType(referenced); + + } + //Testing: + /*if(tks[c].get_token_spelling(tu)=="text_view") { + cout << tks[c].get_token_spelling(tu) << endl; + auto kind=clang_getCursorKind(cursors[c].cursor_); + cout << " " << kind << endl; + cout << " Decl: " << clang_isDeclaration(kind) << endl; + cout << " Attr: " << clang_isAttribute(kind) << endl; + cout << " Ref: " << clang_isReference(kind) << endl; + cout << " Expr: " << clang_isExpression(kind) << endl; + auto referenced=clang_getCursorReferenced(cursors[c].cursor_); + if(!clang_Cursor_isNull(referenced)) { + cout << " " << clang_getCursorKind(referenced) << endl; + + clang::Cursor referenced_cursor; + referenced_cursor.cursor_=referenced; + auto range=clang::SourceRange(&referenced_cursor); + + auto location=clang::SourceLocation(&range, true); + std::string path; + unsigned line, column, offset; + location.get_location_info(&path, &line, &column, &offset); + cout << " start: " << path << ", " << line << ", " << column << endl; + + location=clang::SourceLocation(&range, false); + location.get_location_info(&path, &line, &column, &offset); + cout << " start: " << path << ", " << line << ", " << column << endl; + + auto type=clang_getCursorType(referenced); + cout << " " << clang_getCString(clang_getTypeSpelling(type)) << endl; + } + }*/ + } } + +std::string clang::Tokens::get_brief_comments(size_t cursor_id) { + std::string comment_string; + auto referenced=clang_getCursorReferenced(clang_cursors[cursor_id]); + auto comment=clang_Cursor_getParsedComment(referenced); + if(clang_Comment_getKind(comment)==CXComment_FullComment) { + size_t para_c=0; + for(unsigned c=0;c=2) + break; + for(unsigned c=0;c0) + comment_string.pop_back(); + if(clang_InlineCommandComment_getNumArgs(grandchild_comment)==0) + comment_string+=clang_getCString(cxstr); + clang_disposeString(cxstr); + for(unsigned arg_c=0;arg_c0) + comment_string+=" "; + comment_string+=clang_getCString(cxstr); + clang_disposeString(cxstr); + } + } + } + } + } + /*cout << " " << clang_Comment_getKind(child_comment) << ", children: " << clang_Comment_getNumChildren(child_comment) << endl; + auto cxstr=clang_FullComment_getAsHTML(child_comment); + cout << " " << clang_getCString(cxstr) << endl; + clang_disposeString(cxstr);*/ + } + while(comment_string.size()>0 && (comment_string.back()=='\n' || comment_string.back()==' ')) + comment_string.pop_back(); + } + + return comment_string; +} \ No newline at end of file diff --git a/src/Tokens.h b/src/Tokens.h index 344b0a0..8696337 100644 --- a/src/Tokens.h +++ b/src/Tokens.h @@ -5,15 +5,17 @@ #include "Token.h" namespace clang { - class Tokens { + class Tokens : public std::vector { public: Tokens(TranslationUnit *tu, SourceRange *range); ~Tokens(); - std::vector& tokens(); - protected: - std::vector tks; + void update_types(clang::TranslationUnit *tu); + std::string get_brief_comments(size_t cursor_id); + private: CXToken *tokens_; unsigned num_tokens_; + std::vector clang_cursors; + TranslationUnit& tu; }; } // namespace clang #endif // TOKENS_H_ diff --git a/src/TranslationUnit.cc b/src/TranslationUnit.cc index a91fa6d..de1c2aa 100644 --- a/src/TranslationUnit.cc +++ b/src/TranslationUnit.cc @@ -1,4 +1,8 @@ #include "TranslationUnit.h" +#include "SourceLocation.h" +#include "Tokens.h" +#include +#include clang::TranslationUnit:: ~TranslationUnit() { @@ -15,27 +19,24 @@ clang::TranslationUnit:: TranslationUnit(Index *index, const std::string &filepath, const std::vector &command_line_args) { - std::vector args; - for(auto &a: command_line_args) { - args.push_back(a.c_str()); - } - tu_ = clang_createTranslationUnitFromSourceFile(index->index_, - filepath.c_str(), - args.size(), - args.data(), - 0, - NULL); + std::map buffers; + std::ifstream ifs(filepath, std::ifstream::in); + std::stringstream ss; + ss << ifs.rdbuf(); + buffers[filepath]=ss.str(); + parse(index, filepath, command_line_args, buffers); } clang::TranslationUnit:: TranslationUnit(Index *index, const std::string &filepath) { - tu_ = clang_createTranslationUnitFromSourceFile(index->index_, - filepath.c_str(), - 0, - NULL, - 0, - NULL); + std::vector command_line_args; + std::map buffers; + std::ifstream ifs(filepath, std::ifstream::in); + std::stringstream ss; + ss << ifs.rdbuf(); + buffers[filepath]=ss.str(); + parse(index, filepath, command_line_args, buffers); } clang::TranslationUnit:: @@ -44,6 +45,14 @@ TranslationUnit(clang::Index *index, const std::vector &command_line_args, const std::map &buffers, unsigned flags) { + parse(index, filepath, command_line_args, buffers, flags); +} + +void clang::TranslationUnit::parse(Index *index, + const std::string &filepath, + const std::vector &command_line_args, + const std::map &buffers, + unsigned flags) { std::vector files; for (auto &buffer : buffers) { CXUnsavedFile file; @@ -57,13 +66,13 @@ TranslationUnit(clang::Index *index, args.push_back(a.c_str()); } tu_ = - clang_parseTranslationUnit(index->index_, - filepath.c_str(), - args.data(), - args.size(), - files.data(), - files.size(), - flags); + clang_parseTranslationUnit(index->index_, + filepath.c_str(), + args.data(), + args.size(), + files.data(), + files.size(), + flags); } int clang::TranslationUnit:: @@ -85,5 +94,14 @@ ReparseTranslationUnit(const std::string &file_path, } unsigned clang::TranslationUnit::DefaultFlags() { - return CXTranslationUnit_CacheCompletionResults | CXTranslationUnit_PrecompiledPreamble | CXTranslationUnit_Incomplete; + return CXTranslationUnit_CacheCompletionResults | CXTranslationUnit_PrecompiledPreamble | CXTranslationUnit_Incomplete | CXTranslationUnit_IncludeBriefCommentsInCodeCompletion; +} + +void clang::TranslationUnit::update_diagnostics() { + diagnostics.clear(); + for(unsigned c=0;c #include #include "Index.h" +#include "Diagnostic.h" namespace clang { class Token; @@ -14,6 +15,7 @@ namespace clang { class SourceRange; class Cursor; class CodeCompleteResults; + class Diagnostic; class TranslationUnit { public: @@ -34,7 +36,15 @@ namespace clang { &buffers, unsigned flags=DefaultFlags()); static unsigned DefaultFlags(); + void update_diagnostics(); + + std::vector diagnostics; private: + void parse(Index *index, + const std::string &filepath, + const std::vector &command_line_args, + const std::map &buffers, + unsigned flags=DefaultFlags()); friend Token; friend Tokens; friend SourceLocation; diff --git a/src/clangmm.h b/src/clangmm.h index d5b11c5..3ffe3ae 100644 --- a/src/clangmm.h +++ b/src/clangmm.h @@ -12,4 +12,5 @@ #include "CompletionString.h" #include "Index.h" #include "Cursor.h" +#include "Diagnostic.h" #endif // CLANGMM_H_ diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 01899af..de61c79 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,6 +1,6 @@ set(project_tests ${project_name}_tests) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_HOME_DIRECTORY}/cmake/Modules/") @@ -23,6 +23,7 @@ add_executable(${project_tests} Cursor_H_Test.cc Token_H_Test.cc SourceLocation_H_Test.cc + Diagnostics_Test.cc ) include_directories(${LIBCLANG_INCLUDE_DIRS} ${Boost_INCLUDE_DIRS} "${CMAKE_SOURCE_DIR}/src") diff --git a/tests/CodeCompleteResults_H_Test.cc b/tests/CodeCompleteResults_H_Test.cc index 32bcd76..c696727 100644 --- a/tests/CodeCompleteResults_H_Test.cc +++ b/tests/CodeCompleteResults_H_Test.cc @@ -32,7 +32,7 @@ BOOST_AUTO_TEST_CASE(code_complete_results) { bool substr_found=false; for(int c=0;c +#include "clangmm.h" +#include +#include + +using namespace std; + +BOOST_AUTO_TEST_CASE(diagnostics_test) { + std::string path("./case/main_error.cpp"); + + clang::Index index(0, 0); + + std::map map_buffers; + ifstream ifs(path, ifstream::in); + stringstream ss; + ss << ifs.rdbuf(); + + map_buffers["./case/main_error.cpp"]=ss.str(); + + std::vector args; + clang::TranslationUnit tu(&index, path, args, map_buffers); + + tu.update_diagnostics(); + BOOST_CHECK(tu.diagnostics.size()==1); + BOOST_CHECK(tu.diagnostics[0].spelling=="use of undeclared identifier 'undeclared_variable'"); + BOOST_CHECK(tu.diagnostics[0].path=="./case/main_error.cpp"); + BOOST_CHECK(tu.diagnostics[0].severity==3); + BOOST_CHECK(tu.diagnostics[0].start_location.line==5); + BOOST_CHECK(tu.diagnostics[0].end_location.line==5); + BOOST_CHECK(tu.diagnostics[0].start_location.column==16); + BOOST_CHECK(tu.diagnostics[0].end_location.column==35); +} diff --git a/tests/SourceLocation_H_Test.cc b/tests/SourceLocation_H_Test.cc index 776fcf7..308fe6a 100644 --- a/tests/SourceLocation_H_Test.cc +++ b/tests/SourceLocation_H_Test.cc @@ -13,7 +13,7 @@ BOOST_AUTO_TEST_CASE(source_location) { clang::SourceRange range(&start, &end); clang::Tokens tokens(&tu, &range); - clang::SourceRange token_range = tokens.tokens()[28].get_source_range(&tu); + clang::SourceRange token_range = tokens[28].get_source_range(&tu); unsigned token_start_line, token_start_column, token_start_offset, token_end_line, token_end_column, token_end_offset; diff --git a/tests/Token_H_Test.cc b/tests/Token_H_Test.cc index 0b166e2..8befb20 100644 --- a/tests/Token_H_Test.cc +++ b/tests/Token_H_Test.cc @@ -15,9 +15,9 @@ BOOST_AUTO_TEST_CASE(token) { clang::Tokens tokens(&tu, &range); - BOOST_CHECK(tokens.tokens().size() == 32); - BOOST_CHECK(tokens.tokens()[1].kind() == clang::TokenKind::Token_Identifier); + BOOST_CHECK(tokens.size() == 32); + BOOST_CHECK(tokens[1].kind() == clang::TokenKind::Token_Identifier); - std::string str = tokens.tokens()[28].get_token_spelling(&tu); + std::string str = tokens[28].get_token_spelling(&tu); BOOST_CHECK(str == "return"); } diff --git a/tests/case/main_error.cpp b/tests/case/main_error.cpp new file mode 100644 index 0000000..21f9c23 --- /dev/null +++ b/tests/case/main_error.cpp @@ -0,0 +1,7 @@ +#include + +int main(int argc, char *argv[]) { + std::cout << "Hello, World!" << std::endl; + std::cout << undeclared_variable << std::endl; + return 0; +}