mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-22 07:35:08 +00:00
Use Clang C++ for completion and diagnostics
This commit is contained in:
parent
d1c90ec85a
commit
c4e22bde23
@ -5,6 +5,8 @@
|
|||||||
#include "log.hh"
|
#include "log.hh"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
|
#include "clang/Frontend/CompilerInstance.h"
|
||||||
|
#include "clang/Frontend/FrontendDiagnostic.h"
|
||||||
#include <clang/Sema/CodeCompleteConsumer.h>
|
#include <clang/Sema/CodeCompleteConsumer.h>
|
||||||
#include <llvm/ADT/Twine.h>
|
#include <llvm/ADT/Twine.h>
|
||||||
#include <llvm/Support/Threading.h>
|
#include <llvm/Support/Threading.h>
|
||||||
@ -16,25 +18,15 @@ using namespace llvm;
|
|||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
unsigned Flags() {
|
|
||||||
// TODO: use clang_defaultEditingTranslationUnitOptions()?
|
|
||||||
return CXTranslationUnit_Incomplete | CXTranslationUnit_KeepGoing |
|
|
||||||
CXTranslationUnit_CacheCompletionResults |
|
|
||||||
CXTranslationUnit_PrecompiledPreamble |
|
|
||||||
CXTranslationUnit_IncludeBriefCommentsInCodeCompletion |
|
|
||||||
CXTranslationUnit_DetailedPreprocessingRecord |
|
|
||||||
CXTranslationUnit_CreatePreambleOnFirstParse;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string StripFileType(const std::string& path) {
|
std::string StripFileType(const std::string& path) {
|
||||||
SmallString<128> Ret;
|
SmallString<128> Ret;
|
||||||
sys::path::append(Ret, sys::path::parent_path(path), sys::path::stem(path));
|
sys::path::append(Ret, sys::path::parent_path(path), sys::path::stem(path));
|
||||||
return Ret.str();
|
return Ret.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned GetCompletionPriority(const CodeCompletionString& CCS,
|
unsigned GetCompletionPriority(const CodeCompletionString &CCS,
|
||||||
CXCursorKind result_kind,
|
CXCursorKind result_kind,
|
||||||
const std::optional<std::string>& typedText) {
|
const std::optional<std::string> &typedText) {
|
||||||
unsigned priority = CCS.getPriority();
|
unsigned priority = CCS.getPriority();
|
||||||
if (CCS.getAvailability() != CXAvailability_Available ||
|
if (CCS.getAvailability() != CXAvailability_Available ||
|
||||||
result_kind == CXCursor_Destructor ||
|
result_kind == CXCursor_Destructor ||
|
||||||
@ -196,35 +188,29 @@ void BuildCompletionItemTexts(std::vector<lsCompletionItem> &out,
|
|||||||
// clang-format on
|
// clang-format on
|
||||||
}
|
}
|
||||||
|
|
||||||
for (auto i = out_first; i < out.size(); ++i)
|
if (Kind != CodeCompletionString::CK_Informative)
|
||||||
out[i].label += text;
|
for (auto i = out_first; i < out.size(); ++i) {
|
||||||
|
out[i].label += text;
|
||||||
|
if (!include_snippets && !out[i].parameters_.empty())
|
||||||
|
continue;
|
||||||
|
|
||||||
if (Kind == CodeCompletionString::CK_Informative)
|
if (Kind == CodeCompletionString::CK_Placeholder) {
|
||||||
continue;
|
out[i].insertText +=
|
||||||
|
|
||||||
for (auto i = out_first; i < out.size(); ++i) {
|
|
||||||
if (!include_snippets && !out[i].parameters_.empty())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (Kind == CodeCompletionString::CK_Placeholder) {
|
|
||||||
out[i].insertText +=
|
|
||||||
"${" + std::to_string(out[i].parameters_.size()) + ":" + text + "}";
|
"${" + std::to_string(out[i].parameters_.size()) + ":" + text + "}";
|
||||||
out[i].insertTextFormat = lsInsertTextFormat::Snippet;
|
out[i].insertTextFormat = lsInsertTextFormat::Snippet;
|
||||||
} else {
|
} else {
|
||||||
out[i].insertText += text;
|
out[i].insertText += text;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result_type.size())
|
||||||
|
for (auto i = out_first; i < out.size(); ++i) {
|
||||||
|
// ' : ' for variables,
|
||||||
|
// ' -> ' (trailing return type-like) for functions
|
||||||
|
out[i].label += (out[i].label == out[i].filterText ? " : " : " -> ");
|
||||||
|
out[i].label += result_type;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (result_type.empty())
|
|
||||||
return;
|
|
||||||
|
|
||||||
for (auto i = out_first; i < out.size(); ++i) {
|
|
||||||
// ' : ' for variables,
|
|
||||||
// ' -> ' (trailing return type-like) for functions
|
|
||||||
out[i].label += (out[i].label == out[i].filterText ? " : " : " -> ");
|
|
||||||
out[i].label += result_type;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// |do_insert|: if |!do_insert|, do not append strings to |insert| after
|
// |do_insert|: if |!do_insert|, do not append strings to |insert| after
|
||||||
@ -298,56 +284,87 @@ void BuildDetailString(const CodeCompletionString &CCS, lsCompletionItem &item,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class CaptureCompletionResults : public CodeCompleteConsumer {
|
||||||
|
std::shared_ptr<clang::GlobalCodeCompletionAllocator> Alloc;
|
||||||
|
CodeCompletionTUInfo CCTUInfo;
|
||||||
|
|
||||||
|
public:
|
||||||
|
std::vector<lsCompletionItem> ls_items;
|
||||||
|
|
||||||
|
CaptureCompletionResults(const CodeCompleteOptions &Opts)
|
||||||
|
: CodeCompleteConsumer(Opts, false),
|
||||||
|
Alloc(std::make_shared<clang::GlobalCodeCompletionAllocator>()),
|
||||||
|
CCTUInfo(Alloc) {}
|
||||||
|
|
||||||
|
void ProcessCodeCompleteResults(Sema &S,
|
||||||
|
CodeCompletionContext Context,
|
||||||
|
CodeCompletionResult *Results,
|
||||||
|
unsigned NumResults) override {
|
||||||
|
ls_items.reserve(NumResults);
|
||||||
|
for (unsigned i = 0; i != NumResults; i++) {
|
||||||
|
CodeCompletionString *CCS = Results[i].CreateCodeCompletionString(
|
||||||
|
S, Context, getAllocator(), getCodeCompletionTUInfo(),
|
||||||
|
includeBriefComments());
|
||||||
|
if (CCS->getAvailability() == CXAvailability_NotAvailable)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
lsCompletionItem ls_item;
|
||||||
|
ls_item.kind = GetCompletionKind(Results[i].CursorKind);
|
||||||
|
if (const char* brief = CCS->getBriefComment())
|
||||||
|
ls_item.documentation = brief;
|
||||||
|
|
||||||
|
// label/detail/filterText/insertText/priority
|
||||||
|
if (g_config->completion.detailedLabel) {
|
||||||
|
ls_item.detail = CCS->getParentContextName().str();
|
||||||
|
|
||||||
|
size_t first_idx = ls_items.size();
|
||||||
|
ls_items.push_back(ls_item);
|
||||||
|
BuildCompletionItemTexts(ls_items, *CCS,
|
||||||
|
g_config->client.snippetSupport);
|
||||||
|
|
||||||
|
for (size_t j = first_idx; j < ls_items.size(); j++) {
|
||||||
|
if (g_config->client.snippetSupport &&
|
||||||
|
ls_items[j].insertTextFormat == lsInsertTextFormat::Snippet)
|
||||||
|
ls_items[j].insertText += "$0";
|
||||||
|
ls_items[j].priority_ = GetCompletionPriority(
|
||||||
|
*CCS, Results[i].CursorKind, ls_items[i].filterText);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
bool do_insert = true;
|
||||||
|
int angle_stack = 0;
|
||||||
|
BuildDetailString(*CCS, ls_item, do_insert,
|
||||||
|
&ls_item.parameters_,
|
||||||
|
g_config->client.snippetSupport, angle_stack);
|
||||||
|
if (g_config->client.snippetSupport &&
|
||||||
|
ls_item.insertTextFormat == lsInsertTextFormat::Snippet)
|
||||||
|
ls_item.insertText += "$0";
|
||||||
|
ls_item.priority_ =
|
||||||
|
GetCompletionPriority(*CCS, Results[i].CursorKind, ls_item.label);
|
||||||
|
ls_items.push_back(ls_item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CodeCompletionAllocator &getAllocator() override { return *Alloc; }
|
||||||
|
|
||||||
|
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo;}
|
||||||
|
};
|
||||||
|
|
||||||
void TryEnsureDocumentParsed(ClangCompleteManager* manager,
|
void TryEnsureDocumentParsed(ClangCompleteManager* manager,
|
||||||
std::shared_ptr<CompletionSession> session,
|
std::shared_ptr<CompletionSession> session,
|
||||||
std::unique_ptr<ClangTranslationUnit>* tu,
|
std::unique_ptr<ClangTranslationUnit>* tu,
|
||||||
ClangIndex* index,
|
|
||||||
bool emit_diag) {
|
bool emit_diag) {
|
||||||
// Nothing to do. We already have a translation unit.
|
// Nothing to do. We already have a translation unit.
|
||||||
if (*tu)
|
if (*tu)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::vector<std::string> args = session->file.args;
|
const auto &args = session->file.args;
|
||||||
|
|
||||||
// -fspell-checking enables FixIts for, ie, misspelled types.
|
|
||||||
if (!std::count(args.begin(), args.end(), "-fno-spell-checking") &&
|
|
||||||
!std::count(args.begin(), args.end(), "-fspell-checking")) {
|
|
||||||
args.push_back("-fspell-checking");
|
|
||||||
}
|
|
||||||
|
|
||||||
WorkingFiles::Snapshot snapshot = session->working_files->AsSnapshot(
|
WorkingFiles::Snapshot snapshot = session->working_files->AsSnapshot(
|
||||||
{StripFileType(session->file.filename)});
|
{StripFileType(session->file.filename)});
|
||||||
std::vector<CXUnsavedFile> unsaved = snapshot.AsUnsavedFiles();
|
|
||||||
|
|
||||||
LOG_S(INFO) << "Creating completion session with arguments "
|
LOG_S(INFO) << "Creating completion session with arguments "
|
||||||
<< StringJoin(args, " ");
|
<< StringJoin(args, " ");
|
||||||
*tu = ClangTranslationUnit::Create(index, session->file.filename, args,
|
*tu = ClangTranslationUnit::Create(session->file.filename, args, snapshot);
|
||||||
unsaved, Flags());
|
|
||||||
|
|
||||||
// Build diagnostics.
|
|
||||||
if (g_config->diagnostics.onParse && *tu) {
|
|
||||||
// If we're emitting diagnostics, do an immediate reparse, otherwise we will
|
|
||||||
// emit stale/bad diagnostics.
|
|
||||||
*tu = ClangTranslationUnit::Reparse(std::move(*tu), unsaved);
|
|
||||||
if (!*tu) {
|
|
||||||
LOG_S(ERROR) << "Reparsing translation unit for diagnostics failed for "
|
|
||||||
<< session->file.filename;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<lsDiagnostic> ls_diagnostics;
|
|
||||||
unsigned num_diagnostics = clang_getNumDiagnostics((*tu)->cx_tu);
|
|
||||||
for (unsigned i = 0; i < num_diagnostics; ++i) {
|
|
||||||
std::optional<lsDiagnostic> diagnostic = BuildAndDisposeDiagnostic(
|
|
||||||
clang_getDiagnostic((*tu)->cx_tu, i), session->file.filename);
|
|
||||||
// Filter messages like "too many errors emitted, stopping now
|
|
||||||
// [-ferror-limit=]" which has line = 0 and got subtracted by 1 after
|
|
||||||
// conversion to lsDiagnostic
|
|
||||||
if (diagnostic && diagnostic->range.start.line >= 0)
|
|
||||||
ls_diagnostics.push_back(*diagnostic);
|
|
||||||
}
|
|
||||||
manager->on_diagnostic_(session->file.filename, ls_diagnostics);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompletionPreloadMain(ClangCompleteManager* completion_manager) {
|
void CompletionPreloadMain(ClangCompleteManager* completion_manager) {
|
||||||
@ -374,8 +391,7 @@ void CompletionPreloadMain(ClangCompleteManager* completion_manager) {
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
std::unique_ptr<ClangTranslationUnit> parsing;
|
std::unique_ptr<ClangTranslationUnit> parsing;
|
||||||
TryEnsureDocumentParsed(completion_manager, session, &parsing, &tu->index,
|
TryEnsureDocumentParsed(completion_manager, session, &parsing, true);
|
||||||
true);
|
|
||||||
|
|
||||||
// Activate new translation unit.
|
// Activate new translation unit.
|
||||||
std::lock_guard<std::mutex> lock(tu->lock);
|
std::lock_guard<std::mutex> lock(tu->lock);
|
||||||
@ -404,84 +420,43 @@ void CompletionQueryMain(ClangCompleteManager* completion_manager) {
|
|||||||
true /*create_if_needed*/);
|
true /*create_if_needed*/);
|
||||||
|
|
||||||
std::lock_guard<std::mutex> lock(session->completion.lock);
|
std::lock_guard<std::mutex> lock(session->completion.lock);
|
||||||
TryEnsureDocumentParsed(completion_manager, session, &session->completion.tu,
|
TryEnsureDocumentParsed(completion_manager, session, &session->completion.tu, false);
|
||||||
&session->completion.index, false);
|
|
||||||
|
|
||||||
// It is possible we failed to create the document despite
|
// It is possible we failed to create the document despite
|
||||||
// |TryEnsureDocumentParsed|.
|
// |TryEnsureDocumentParsed|.
|
||||||
if (!session->completion.tu)
|
if (ClangTranslationUnit* tu = session->completion.tu.get()) {
|
||||||
continue;
|
WorkingFiles::Snapshot snapshot =
|
||||||
|
completion_manager->working_files_->AsSnapshot({StripFileType(path)});
|
||||||
|
IntrusiveRefCntPtr<FileManager> FileMgr(&tu->Unit->getFileManager());
|
||||||
|
IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
|
||||||
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diag(new DiagnosticsEngine(
|
||||||
|
IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts));
|
||||||
|
// StoreDiags Diags;
|
||||||
|
// IntrusiveRefCntPtr<DiagnosticsEngine> DiagE =
|
||||||
|
// CompilerInstance::createDiagnostics(DiagOpts.get(), &Diags, false);
|
||||||
|
|
||||||
WorkingFiles::Snapshot snapshot =
|
IntrusiveRefCntPtr<SourceManager> SrcMgr(
|
||||||
completion_manager->working_files_->AsSnapshot({StripFileType(path)});
|
new SourceManager(*Diag, *FileMgr));
|
||||||
std::vector<CXUnsavedFile> unsaved = snapshot.AsUnsavedFiles();
|
std::vector<ASTUnit::RemappedFile> Remapped = GetRemapped(snapshot);
|
||||||
|
SmallVector<StoredDiagnostic, 8> Diagnostics;
|
||||||
|
SmallVector<const llvm::MemoryBuffer *, 1> TemporaryBuffers;
|
||||||
|
|
||||||
unsigned const kCompleteOptions =
|
CodeCompleteOptions Opts;
|
||||||
CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeBriefComments;
|
LangOptions LangOpts;
|
||||||
CXCodeCompleteResults* cx_results = clang_codeCompleteAt(
|
Opts.IncludeBriefComments = true;
|
||||||
session->completion.tu->cx_tu, session->file.filename.c_str(),
|
Opts.LoadExternal = false;
|
||||||
request->position.line + 1, request->position.character + 1,
|
Opts.IncludeFixIts = true;
|
||||||
unsaved.data(), (unsigned)unsaved.size(), kCompleteOptions);
|
CaptureCompletionResults capture(Opts);
|
||||||
if (!cx_results) {
|
tu->Unit->CodeComplete(session->file.filename, request->position.line + 1,
|
||||||
request->on_complete({}, false /*is_cached_result*/);
|
request->position.character + 1, Remapped,
|
||||||
continue;
|
/*IncludeMacros=*/true,
|
||||||
|
/*IncludeCodePatterns=*/false,
|
||||||
|
/*IncludeBriefComments=*/true, capture, tu->PCHCO,
|
||||||
|
*Diag, LangOpts, *SrcMgr, *FileMgr, Diagnostics,
|
||||||
|
TemporaryBuffers);
|
||||||
|
request->on_complete(capture.ls_items, false /*is_cached_result*/);
|
||||||
|
// completion_manager->on_diagnostic_(session->file.filename, Diags.take());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<lsCompletionItem> ls_result;
|
|
||||||
// this is a guess but can be larger in case of std::optional
|
|
||||||
// parameters, as they may be expanded into multiple items
|
|
||||||
ls_result.reserve(cx_results->NumResults);
|
|
||||||
|
|
||||||
for (unsigned i = 0; i < cx_results->NumResults; ++i) {
|
|
||||||
CXCompletionResult& result = cx_results->Results[i];
|
|
||||||
auto CCS = (CodeCompletionString *)result.CompletionString;
|
|
||||||
if (CCS->getAvailability() == CXAvailability_NotAvailable)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
lsCompletionItem ls_item;
|
|
||||||
ls_item.kind = GetCompletionKind(result.CursorKind);
|
|
||||||
if (const char* brief = CCS->getBriefComment())
|
|
||||||
ls_item.documentation = brief;
|
|
||||||
|
|
||||||
// label/detail/filterText/insertText/priority
|
|
||||||
if (g_config->completion.detailedLabel) {
|
|
||||||
ls_item.detail = CCS->getParentContextName().str();
|
|
||||||
|
|
||||||
auto first_idx = ls_result.size();
|
|
||||||
ls_result.push_back(ls_item);
|
|
||||||
|
|
||||||
// label/filterText/insertText
|
|
||||||
BuildCompletionItemTexts(ls_result, *CCS,
|
|
||||||
g_config->client.snippetSupport);
|
|
||||||
|
|
||||||
for (auto i = first_idx; i < ls_result.size(); ++i) {
|
|
||||||
if (g_config->client.snippetSupport &&
|
|
||||||
ls_result[i].insertTextFormat == lsInsertTextFormat::Snippet) {
|
|
||||||
ls_result[i].insertText += "$0";
|
|
||||||
}
|
|
||||||
|
|
||||||
ls_result[i].priority_ = GetCompletionPriority(
|
|
||||||
*CCS, result.CursorKind, ls_result[i].filterText);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
bool do_insert = true;
|
|
||||||
int angle_stack = 0;
|
|
||||||
BuildDetailString(*CCS, ls_item, do_insert,
|
|
||||||
&ls_item.parameters_,
|
|
||||||
g_config->client.snippetSupport, angle_stack);
|
|
||||||
if (g_config->client.snippetSupport &&
|
|
||||||
ls_item.insertTextFormat == lsInsertTextFormat::Snippet)
|
|
||||||
ls_item.insertText += "$0";
|
|
||||||
ls_item.priority_ =
|
|
||||||
GetCompletionPriority(*CCS, result.CursorKind, ls_item.label);
|
|
||||||
ls_result.push_back(ls_item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
request->on_complete(ls_result, false /*is_cached_result*/);
|
|
||||||
|
|
||||||
// Make sure |ls_results| is destroyed before clearing |cx_results|.
|
|
||||||
clang_disposeCodeCompleteResults(cx_results);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -500,44 +475,72 @@ void DiagnosticQueryMain(ClangCompleteManager* completion_manager) {
|
|||||||
|
|
||||||
// At this point, we must have a translation unit. Block until we have one.
|
// At this point, we must have a translation unit. Block until we have one.
|
||||||
std::lock_guard<std::mutex> lock(session->diagnostics.lock);
|
std::lock_guard<std::mutex> lock(session->diagnostics.lock);
|
||||||
TryEnsureDocumentParsed(
|
TryEnsureDocumentParsed(completion_manager, session,
|
||||||
completion_manager, session, &session->diagnostics.tu,
|
&session->diagnostics.tu,
|
||||||
&session->diagnostics.index, false /*emit_diagnostics*/);
|
false /*emit_diagnostics*/);
|
||||||
|
|
||||||
// It is possible we failed to create the document despite
|
// It is possible we failed to create the document despite
|
||||||
// |TryEnsureDocumentParsed|.
|
// |TryEnsureDocumentParsed|.
|
||||||
if (!session->diagnostics.tu)
|
ClangTranslationUnit* tu = session->diagnostics.tu.get();
|
||||||
|
if (!tu)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
WorkingFiles::Snapshot snapshot =
|
WorkingFiles::Snapshot snapshot =
|
||||||
completion_manager->working_files_->AsSnapshot({StripFileType(path)});
|
completion_manager->working_files_->AsSnapshot({StripFileType(path)});
|
||||||
std::vector<CXUnsavedFile> unsaved = snapshot.AsUnsavedFiles();
|
llvm::CrashRecoveryContext CRC;
|
||||||
|
if (tu->Reparse(CRC, snapshot)) {
|
||||||
// Emit diagnostics.
|
|
||||||
session->diagnostics.tu = ClangTranslationUnit::Reparse(
|
|
||||||
std::move(session->diagnostics.tu), unsaved);
|
|
||||||
if (!session->diagnostics.tu) {
|
|
||||||
LOG_S(ERROR) << "Reparsing translation unit for diagnostics failed for "
|
LOG_S(ERROR) << "Reparsing translation unit for diagnostics failed for "
|
||||||
<< path;
|
<< path;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t num_diagnostics =
|
auto &SM = tu->Unit->getSourceManager();
|
||||||
clang_getNumDiagnostics(session->diagnostics.tu->cx_tu);
|
auto &LangOpts = tu->Unit->getLangOpts();
|
||||||
std::vector<lsDiagnostic> ls_diagnostics;
|
std::vector<lsDiagnostic> ls_diags;
|
||||||
ls_diagnostics.reserve(num_diagnostics);
|
for (ASTUnit::stored_diag_iterator I = tu->Unit->stored_diag_begin(),
|
||||||
for (unsigned i = 0; i < num_diagnostics; ++i) {
|
E = tu->Unit->stored_diag_end();
|
||||||
CXDiagnostic cx_diag =
|
I != E; ++I) {
|
||||||
clang_getDiagnostic(session->diagnostics.tu->cx_tu, i);
|
FullSourceLoc FLoc = I->getLocation();
|
||||||
std::optional<lsDiagnostic> diagnostic =
|
SourceRange R;
|
||||||
BuildAndDisposeDiagnostic(cx_diag, path);
|
for (const auto &CR : I->getRanges()) {
|
||||||
// Filter messages like "too many errors emitted, stopping now
|
auto RT = Lexer::makeFileCharRange(CR, SM, LangOpts);
|
||||||
// [-ferror-limit=]" which has line = 0 and got subtracted by 1 after
|
if (SM.isPointWithin(FLoc, RT.getBegin(), RT.getEnd())) {
|
||||||
// conversion to lsDiagnostic
|
R = CR.getAsRange();
|
||||||
if (diagnostic && diagnostic->range.start.line >= 0)
|
break;
|
||||||
ls_diagnostics.push_back(*diagnostic);
|
}
|
||||||
|
}
|
||||||
|
Range r = R.isValid() ? FromCharRange(SM, LangOpts, R)
|
||||||
|
: FromTokenRange(SM, LangOpts, {FLoc, FLoc});
|
||||||
|
lsDiagnostic ls_diag;
|
||||||
|
ls_diag.range =
|
||||||
|
lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}};
|
||||||
|
switch (I->getLevel()) {
|
||||||
|
case DiagnosticsEngine::Ignored:
|
||||||
|
// llvm_unreachable
|
||||||
|
break;
|
||||||
|
case DiagnosticsEngine::Note:
|
||||||
|
case DiagnosticsEngine::Remark:
|
||||||
|
ls_diag.severity = lsDiagnosticSeverity::Information;
|
||||||
|
break;
|
||||||
|
case DiagnosticsEngine::Warning:
|
||||||
|
ls_diag.severity = lsDiagnosticSeverity::Warning;
|
||||||
|
break;
|
||||||
|
case DiagnosticsEngine::Error:
|
||||||
|
case DiagnosticsEngine::Fatal:
|
||||||
|
ls_diag.severity = lsDiagnosticSeverity::Error;
|
||||||
|
}
|
||||||
|
ls_diag.message = I->getMessage().str();
|
||||||
|
for (const FixItHint &FixIt : I->getFixIts()) {
|
||||||
|
lsTextEdit edit;
|
||||||
|
edit.newText = FixIt.CodeToInsert;
|
||||||
|
r = FromCharRange(SM, LangOpts, FixIt.RemoveRange.getAsRange());
|
||||||
|
edit.range =
|
||||||
|
lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}};
|
||||||
|
ls_diag.fixits_.push_back(edit);
|
||||||
|
}
|
||||||
|
ls_diags.push_back(ls_diag);
|
||||||
}
|
}
|
||||||
completion_manager->on_diagnostic_(path, ls_diagnostics);
|
completion_manager->on_diagnostic_(path, ls_diags);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,8 +19,6 @@ struct CompletionSession
|
|||||||
: public std::enable_shared_from_this<CompletionSession> {
|
: public std::enable_shared_from_this<CompletionSession> {
|
||||||
// Translation unit for clang.
|
// Translation unit for clang.
|
||||||
struct Tu {
|
struct Tu {
|
||||||
ClangIndex index{0, 0};
|
|
||||||
|
|
||||||
// When |tu| was last parsed.
|
// When |tu| was last parsed.
|
||||||
std::optional<std::chrono::time_point<std::chrono::high_resolution_clock>>
|
std::optional<std::chrono::time_point<std::chrono::high_resolution_clock>>
|
||||||
last_parsed_at;
|
last_parsed_at;
|
||||||
|
242
src/clang_tu.cc
242
src/clang_tu.cc
@ -4,173 +4,113 @@
|
|||||||
#include "log.hh"
|
#include "log.hh"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
|
#include "working_files.h"
|
||||||
|
|
||||||
|
#include <llvm/Support/CrashRecoveryContext.h>
|
||||||
|
using namespace clang;
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
#include <string.h>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
namespace {
|
Range FromSourceRange(const SourceManager &SM, const LangOptions &LangOpts,
|
||||||
|
SourceRange R, llvm::sys::fs::UniqueID *UniqueID,
|
||||||
void EmitDiagnostics(std::string path,
|
bool token) {
|
||||||
std::vector<const char*> args,
|
SourceLocation BLoc = R.getBegin(), ELoc = R.getEnd();
|
||||||
CXTranslationUnit tu) {
|
std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc);
|
||||||
std::string output = "Fatal errors while trying to parse " + path + "\n";
|
std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc);
|
||||||
output +=
|
if (token)
|
||||||
"Args: " +
|
EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts);
|
||||||
StringJoinMap(args, [](const char* arg) { return std::string(arg); }) +
|
unsigned l0 = SM.getLineNumber(BInfo.first, BInfo.second) - 1,
|
||||||
"\n";
|
c0 = SM.getColumnNumber(BInfo.first, BInfo.second) - 1,
|
||||||
|
l1 = SM.getLineNumber(EInfo.first, EInfo.second) - 1,
|
||||||
size_t num_diagnostics = clang_getNumDiagnostics(tu);
|
c1 = SM.getColumnNumber(EInfo.first, EInfo.second) - 1;
|
||||||
for (unsigned i = 0; i < num_diagnostics; ++i) {
|
if (l0 > INT16_MAX)
|
||||||
output += " - ";
|
l0 = 0;
|
||||||
|
if (c0 > INT16_MAX)
|
||||||
CXDiagnostic diagnostic = clang_getDiagnostic(tu, i);
|
c0 = 0;
|
||||||
|
if (l1 > INT16_MAX)
|
||||||
// Location.
|
l1 = 0;
|
||||||
CXFile file;
|
if (c1 > INT16_MAX)
|
||||||
unsigned int line, column;
|
c1 = 0;
|
||||||
clang_getSpellingLocation(clang_getDiagnosticLocation(diagnostic), &file,
|
if (UniqueID) {
|
||||||
&line, &column, nullptr);
|
if (const FileEntry *F = SM.getFileEntryForID(BInfo.first))
|
||||||
std::string path = FileName(file);
|
*UniqueID = F->getUniqueID();
|
||||||
output += path + ":" + std::to_string(line - 1) + ":" +
|
else
|
||||||
std::to_string(column) + " ";
|
*UniqueID = llvm::sys::fs::UniqueID(0, 0);
|
||||||
|
|
||||||
// Severity
|
|
||||||
switch (clang_getDiagnosticSeverity(diagnostic)) {
|
|
||||||
case CXDiagnostic_Ignored:
|
|
||||||
case CXDiagnostic_Note:
|
|
||||||
output += "[info]";
|
|
||||||
break;
|
|
||||||
case CXDiagnostic_Warning:
|
|
||||||
output += "[warning]";
|
|
||||||
break;
|
|
||||||
case CXDiagnostic_Error:
|
|
||||||
output += "[error]";
|
|
||||||
break;
|
|
||||||
case CXDiagnostic_Fatal:
|
|
||||||
output += "[fatal]";
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Content.
|
|
||||||
output += " " + ToString(clang_getDiagnosticSpelling(diagnostic));
|
|
||||||
|
|
||||||
clang_disposeDiagnostic(diagnostic);
|
|
||||||
|
|
||||||
output += "\n";
|
|
||||||
}
|
}
|
||||||
|
return {{int16_t(l0), int16_t(c0)}, {int16_t(l1), int16_t(c1)}};
|
||||||
LOG_S(WARNING) << output;
|
|
||||||
}
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
ClangIndex::ClangIndex() : ClangIndex(1, 0) {}
|
|
||||||
|
|
||||||
ClangIndex::ClangIndex(int exclude_declarations_from_pch,
|
|
||||||
int display_diagnostics) {
|
|
||||||
// llvm::InitializeAllTargets (and possibly others) called by
|
|
||||||
// clang_createIndex transtively modifies/reads lib/Support/TargetRegistry.cpp
|
|
||||||
// FirstTarget. There will be a race condition if two threads call
|
|
||||||
// clang_createIndex concurrently.
|
|
||||||
static std::mutex mutex_;
|
|
||||||
std::lock_guard<std::mutex> lock(mutex_);
|
|
||||||
|
|
||||||
cx_index =
|
|
||||||
clang_createIndex(exclude_declarations_from_pch, display_diagnostics);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ClangIndex::~ClangIndex() {
|
Range FromCharRange(const SourceManager &SM, const LangOptions &LangOpts,
|
||||||
clang_disposeIndex(cx_index);
|
SourceRange R,
|
||||||
|
llvm::sys::fs::UniqueID *UniqueID) {
|
||||||
|
return FromSourceRange(SM, LangOpts, R, UniqueID, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
Range FromTokenRange(const SourceManager &SM, const LangOptions &LangOpts,
|
||||||
std::unique_ptr<ClangTranslationUnit> ClangTranslationUnit::Create(
|
SourceRange R,
|
||||||
ClangIndex* index,
|
llvm::sys::fs::UniqueID *UniqueID) {
|
||||||
const std::string& filepath,
|
return FromSourceRange(SM, LangOpts, R, UniqueID, true);
|
||||||
const std::vector<std::string>& arguments,
|
}
|
||||||
std::vector<CXUnsavedFile>& unsaved_files,
|
|
||||||
unsigned flags) {
|
|
||||||
std::vector<const char*> args;
|
|
||||||
for (auto& arg : arguments)
|
|
||||||
args.push_back(arg.c_str());
|
|
||||||
|
|
||||||
CXTranslationUnit cx_tu;
|
std::vector<ASTUnit::RemappedFile>
|
||||||
CXErrorCode error_code;
|
GetRemapped(const WorkingFiles::Snapshot &snapshot) {
|
||||||
{
|
std::vector<ASTUnit::RemappedFile> Remapped;
|
||||||
error_code = clang_parseTranslationUnit2FullArgv(
|
for (auto &file : snapshot.files) {
|
||||||
index->cx_index, nullptr, args.data(), (int)args.size(),
|
std::unique_ptr<llvm::MemoryBuffer> MB =
|
||||||
unsaved_files.data(), (unsigned)unsaved_files.size(), flags, &cx_tu);
|
llvm::MemoryBuffer::getMemBufferCopy(file.content, file.filename);
|
||||||
|
Remapped.emplace_back(file.filename, MB.release());
|
||||||
}
|
}
|
||||||
|
return Remapped;
|
||||||
|
}
|
||||||
|
|
||||||
if (error_code != CXError_Success && cx_tu)
|
std::unique_ptr<ClangTranslationUnit>
|
||||||
EmitDiagnostics(filepath, args, cx_tu);
|
ClangTranslationUnit::Create(const std::string &filepath,
|
||||||
|
const std::vector<std::string> &args,
|
||||||
|
const WorkingFiles::Snapshot &snapshot) {
|
||||||
|
std::vector<const char *> Args;
|
||||||
|
for (auto& arg : args)
|
||||||
|
Args.push_back(arg.c_str());
|
||||||
|
Args.push_back("-fno-spell-checking");
|
||||||
|
|
||||||
// We sometimes dump the command to logs and ask the user to run it. Include
|
auto ret = std::make_unique<ClangTranslationUnit>();
|
||||||
// -fsyntax-only so they don't do a full compile.
|
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
|
||||||
auto make_msg = [&]() {
|
CompilerInstance::createDiagnostics(new DiagnosticOptions));
|
||||||
return "Please try running the following, identify which flag causes the "
|
std::vector<ASTUnit::RemappedFile> Remapped = GetRemapped(snapshot);
|
||||||
"issue, and report a bug. ccls will then filter the flag for you "
|
|
||||||
" automatically:\n " +
|
ret->PCHCO = std::make_shared<PCHContainerOperations>();
|
||||||
StringJoin(args, " ") + " -fsyntax-only";
|
std::unique_ptr<ASTUnit> ErrUnit, Unit;
|
||||||
|
llvm::CrashRecoveryContext CRC;
|
||||||
|
auto parse = [&]() {
|
||||||
|
Unit.reset(ASTUnit::LoadFromCommandLine(
|
||||||
|
Args.data(), Args.data() + Args.size(),
|
||||||
|
/*PCHContainerOpts=*/ret->PCHCO, Diags,
|
||||||
|
/*ResourceFilePath=*/"", /*OnlyLocalDecls=*/false,
|
||||||
|
/*CaptureDiagnostics=*/true, Remapped,
|
||||||
|
/*RemappedFilesKeepOriginalName=*/true, 1, TU_Prefix,
|
||||||
|
/*CacheCodeCompletionResults=*/true, true,
|
||||||
|
/*AllowPCHWithCompilerErrors=*/true, SkipFunctionBodiesScope::None,
|
||||||
|
/*SingleFileParse=*/false,
|
||||||
|
/*UserFilesAreVolatile=*/true, false,
|
||||||
|
ret->PCHCO->getRawReader().getFormat(), &ErrUnit));
|
||||||
};
|
};
|
||||||
|
if (!RunSafely(CRC, parse)) {
|
||||||
switch (error_code) {
|
LOG_S(ERROR)
|
||||||
case CXError_Success:
|
<< "clang crashed for " << filepath << "\n"
|
||||||
return std::make_unique<ClangTranslationUnit>(cx_tu);
|
<< StringJoin(args, " ") + " -fsyntax-only";
|
||||||
case CXError_Failure:
|
return {};
|
||||||
LOG_S(ERROR) << "libclang generic failure for " << filepath << ". "
|
|
||||||
<< make_msg();
|
|
||||||
return nullptr;
|
|
||||||
case CXError_Crashed:
|
|
||||||
LOG_S(ERROR) << "libclang crashed for " << filepath << ". " << make_msg();
|
|
||||||
return nullptr;
|
|
||||||
case CXError_InvalidArguments:
|
|
||||||
LOG_S(ERROR) << "libclang had invalid arguments for " << filepath << ". "
|
|
||||||
<< make_msg();
|
|
||||||
return nullptr;
|
|
||||||
case CXError_ASTReadError:
|
|
||||||
LOG_S(ERROR) << "libclang had ast read error for " << filepath << ". "
|
|
||||||
<< make_msg();
|
|
||||||
return nullptr;
|
|
||||||
}
|
}
|
||||||
|
if (!Unit && !ErrUnit)
|
||||||
|
return {};
|
||||||
|
|
||||||
return nullptr;
|
ret->Unit = std::move(Unit);
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
// static
|
int ClangTranslationUnit::Reparse(llvm::CrashRecoveryContext &CRC,
|
||||||
std::unique_ptr<ClangTranslationUnit> ClangTranslationUnit::Reparse(
|
const WorkingFiles::Snapshot &snapshot) {
|
||||||
std::unique_ptr<ClangTranslationUnit> tu,
|
int ret = 1;
|
||||||
std::vector<CXUnsavedFile>& unsaved) {
|
auto parse = [&]() { ret = Unit->Reparse(PCHCO, GetRemapped(snapshot)); };
|
||||||
int error_code = clang_reparseTranslationUnit(
|
(void)RunSafely(CRC, parse);
|
||||||
tu->cx_tu, (unsigned)unsaved.size(), unsaved.data(),
|
return ret;
|
||||||
clang_defaultReparseOptions(tu->cx_tu));
|
|
||||||
|
|
||||||
if (error_code != CXError_Success && tu->cx_tu)
|
|
||||||
EmitDiagnostics("<unknown>", {}, tu->cx_tu);
|
|
||||||
|
|
||||||
switch (error_code) {
|
|
||||||
case CXError_Success:
|
|
||||||
return tu;
|
|
||||||
case CXError_Failure:
|
|
||||||
LOG_S(ERROR) << "libclang reparse generic failure";
|
|
||||||
return nullptr;
|
|
||||||
case CXError_Crashed:
|
|
||||||
LOG_S(ERROR) << "libclang reparse crashed";
|
|
||||||
return nullptr;
|
|
||||||
case CXError_InvalidArguments:
|
|
||||||
LOG_S(ERROR) << "libclang reparse had invalid arguments";
|
|
||||||
return nullptr;
|
|
||||||
case CXError_ASTReadError:
|
|
||||||
LOG_S(ERROR) << "libclang reparse had ast read error";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
ClangTranslationUnit::ClangTranslationUnit(CXTranslationUnit tu) : cx_tu(tu) {}
|
|
||||||
|
|
||||||
ClangTranslationUnit::~ClangTranslationUnit() {
|
|
||||||
clang_disposeTranslationUnit(cx_tu);
|
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,45 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include "position.h"
|
#include "position.h"
|
||||||
|
#include "working_files.h"
|
||||||
|
|
||||||
#include <clang-c/Index.h>
|
#include <clang/Frontend/ASTUnit.h>
|
||||||
|
#include <clang/Frontend/CompilerInstance.h>
|
||||||
|
#include <llvm/Support/CrashRecoveryContext.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
// Simple RAII wrapper about CXIndex.
|
std::vector<clang::ASTUnit::RemappedFile>
|
||||||
// Note: building a ClangIndex instance acquires a global lock, since libclang
|
GetRemapped(const WorkingFiles::Snapshot &snapshot);
|
||||||
// API does not appear to be thread-safe here.
|
|
||||||
class ClangIndex {
|
Range FromCharRange(const clang::SourceManager &SM, const clang::LangOptions &LangOpts,
|
||||||
public:
|
clang::SourceRange R,
|
||||||
ClangIndex();
|
llvm::sys::fs::UniqueID *UniqueID = nullptr);
|
||||||
ClangIndex(int exclude_declarations_from_pch, int display_diagnostics);
|
|
||||||
~ClangIndex();
|
Range FromTokenRange(const clang::SourceManager &SM, const clang::LangOptions &LangOpts,
|
||||||
CXIndex cx_index;
|
clang::SourceRange R,
|
||||||
};
|
llvm::sys::fs::UniqueID *UniqueID = nullptr);
|
||||||
|
|
||||||
|
template <typename Fn>
|
||||||
|
bool RunSafely(llvm::CrashRecoveryContext &CRC, Fn &&fn) {
|
||||||
|
const char *env = getenv("CCLS_CRASH_RECOVERY");
|
||||||
|
if (env && strcmp(env, "0") == 0) {
|
||||||
|
fn();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return CRC.RunSafely(fn);
|
||||||
|
}
|
||||||
|
|
||||||
// RAII wrapper around CXTranslationUnit which also makes it much more
|
|
||||||
// challenging to use a CXTranslationUnit instance that is not correctly
|
|
||||||
// initialized.
|
|
||||||
struct ClangTranslationUnit {
|
struct ClangTranslationUnit {
|
||||||
static std::unique_ptr<ClangTranslationUnit> Create(
|
static std::unique_ptr<ClangTranslationUnit>
|
||||||
ClangIndex* index,
|
Create(const std::string &filepath, const std::vector<std::string> &arguments,
|
||||||
const std::string& filepath,
|
const WorkingFiles::Snapshot &snapshot);
|
||||||
const std::vector<std::string>& arguments,
|
|
||||||
std::vector<CXUnsavedFile>& unsaved_files,
|
|
||||||
unsigned flags);
|
|
||||||
|
|
||||||
static std::unique_ptr<ClangTranslationUnit> Reparse(
|
int Reparse(llvm::CrashRecoveryContext &CRC,
|
||||||
std::unique_ptr<ClangTranslationUnit> tu,
|
const WorkingFiles::Snapshot &snapshot);
|
||||||
std::vector<CXUnsavedFile>& unsaved);
|
|
||||||
|
|
||||||
explicit ClangTranslationUnit(CXTranslationUnit tu);
|
std::shared_ptr<clang::PCHContainerOperations> PCHCO;
|
||||||
~ClangTranslationUnit();
|
std::unique_ptr<clang::ASTUnit> Unit;
|
||||||
|
|
||||||
CXTranslationUnit cx_tu;
|
|
||||||
};
|
};
|
||||||
|
@ -1,113 +1,12 @@
|
|||||||
#include "clang_utils.h"
|
#include "clang_utils.h"
|
||||||
|
|
||||||
|
#include "filesystem.hh"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
|
|
||||||
#include "filesystem.hh"
|
#include <clang/AST/Type.h>
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
lsRange GetLsRangeForFixIt(const CXSourceRange& range) {
|
|
||||||
CXSourceLocation start = clang_getRangeStart(range);
|
|
||||||
CXSourceLocation end = clang_getRangeEnd(range);
|
|
||||||
|
|
||||||
unsigned int start_line, start_column;
|
|
||||||
clang_getSpellingLocation(start, nullptr, &start_line, &start_column,
|
|
||||||
nullptr);
|
|
||||||
unsigned int end_line, end_column;
|
|
||||||
clang_getSpellingLocation(end, nullptr, &end_line, &end_column, nullptr);
|
|
||||||
|
|
||||||
return lsRange{lsPosition{int(start_line) - 1, int(start_column) - 1},
|
|
||||||
lsPosition{int(end_line) - 1, int(end_column)}};
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
// See clang_formatDiagnostic
|
|
||||||
std::optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,
|
|
||||||
const std::string& path) {
|
|
||||||
// Get diagnostic location.
|
|
||||||
CXFile file;
|
|
||||||
unsigned start_line, start_column;
|
|
||||||
clang_getSpellingLocation(clang_getDiagnosticLocation(diagnostic), &file,
|
|
||||||
&start_line, &start_column, nullptr);
|
|
||||||
|
|
||||||
if (file && path != FileName(file)) {
|
|
||||||
clang_disposeDiagnostic(diagnostic);
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned end_line = start_line, end_column = start_column,
|
|
||||||
num_ranges = clang_getDiagnosticNumRanges(diagnostic);
|
|
||||||
for (unsigned i = 0; i < num_ranges; i++) {
|
|
||||||
CXFile file0, file1;
|
|
||||||
unsigned line0, column0, line1, column1;
|
|
||||||
CXSourceRange range = clang_getDiagnosticRange(diagnostic, i);
|
|
||||||
clang_getSpellingLocation(clang_getRangeStart(range), &file0, &line0,
|
|
||||||
&column0, nullptr);
|
|
||||||
clang_getSpellingLocation(clang_getRangeEnd(range), &file1, &line1,
|
|
||||||
&column1, nullptr);
|
|
||||||
if (file0 != file1 || file0 != file)
|
|
||||||
continue;
|
|
||||||
if (line0 < start_line || (line0 == start_line && column0 < start_column)) {
|
|
||||||
start_line = line0;
|
|
||||||
start_column = column0;
|
|
||||||
}
|
|
||||||
if (line1 > end_line || (line1 == end_line && column1 > end_column)) {
|
|
||||||
end_line = line1;
|
|
||||||
end_column = column1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build diagnostic.
|
|
||||||
lsDiagnostic ls_diagnostic;
|
|
||||||
ls_diagnostic.range = lsRange{{int(start_line) - 1, int(start_column) - 1},
|
|
||||||
{int(end_line) - 1, int(end_column) - 1}};
|
|
||||||
|
|
||||||
ls_diagnostic.message = ToString(clang_getDiagnosticSpelling(diagnostic));
|
|
||||||
|
|
||||||
// Append the flag that enables this diagnostic, ie, [-Wswitch]
|
|
||||||
std::string enabling_flag =
|
|
||||||
ToString(clang_getDiagnosticOption(diagnostic, nullptr));
|
|
||||||
if (!enabling_flag.empty())
|
|
||||||
ls_diagnostic.message += " [" + enabling_flag + "]";
|
|
||||||
|
|
||||||
ls_diagnostic.code = clang_getDiagnosticCategory(diagnostic);
|
|
||||||
|
|
||||||
switch (clang_getDiagnosticSeverity(diagnostic)) {
|
|
||||||
case CXDiagnostic_Ignored:
|
|
||||||
// llvm_unreachable
|
|
||||||
break;
|
|
||||||
case CXDiagnostic_Note:
|
|
||||||
ls_diagnostic.severity = lsDiagnosticSeverity::Information;
|
|
||||||
break;
|
|
||||||
case CXDiagnostic_Warning:
|
|
||||||
ls_diagnostic.severity = lsDiagnosticSeverity::Warning;
|
|
||||||
break;
|
|
||||||
case CXDiagnostic_Error:
|
|
||||||
case CXDiagnostic_Fatal:
|
|
||||||
ls_diagnostic.severity = lsDiagnosticSeverity::Error;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Report fixits
|
|
||||||
unsigned num_fixits = clang_getDiagnosticNumFixIts(diagnostic);
|
|
||||||
for (unsigned i = 0; i < num_fixits; ++i) {
|
|
||||||
CXSourceRange replacement_range;
|
|
||||||
CXString text = clang_getDiagnosticFixIt(diagnostic, i, &replacement_range);
|
|
||||||
|
|
||||||
lsTextEdit edit;
|
|
||||||
edit.newText = ToString(text);
|
|
||||||
edit.range = GetLsRangeForFixIt(replacement_range);
|
|
||||||
ls_diagnostic.fixits_.push_back(edit);
|
|
||||||
}
|
|
||||||
|
|
||||||
clang_disposeDiagnostic(diagnostic);
|
|
||||||
|
|
||||||
return ls_diagnostic;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string FileName(CXFile file) {
|
std::string FileName(CXFile file) {
|
||||||
std::string ret;
|
std::string ret;
|
||||||
// clang > 6
|
// clang > 6
|
||||||
@ -153,34 +52,145 @@ std::string ToString(CXCursorKind kind) {
|
|||||||
return ToString(clang_getCursorKindSpelling(kind));
|
return ToString(clang_getCursorKindSpelling(kind));
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* ClangBuiltinTypeName(CXTypeKind kind) {
|
// clang::BuiltinType::getName without PrintingPolicy
|
||||||
switch (kind) {
|
const char* ClangBuiltinTypeName(int kind) {
|
||||||
// clang-format off
|
switch (BuiltinType::Kind(kind)) {
|
||||||
case CXType_Bool: return "bool";
|
case BuiltinType::Void:
|
||||||
case CXType_Char_U: return "char";
|
return "void";
|
||||||
case CXType_UChar: return "unsigned char";
|
case BuiltinType::Bool:
|
||||||
case CXType_Char16: return "char16_t";
|
return "bool";
|
||||||
case CXType_Char32: return "char32_t";
|
case BuiltinType::Char_S:
|
||||||
case CXType_UShort: return "unsigned short";
|
return "char";
|
||||||
case CXType_UInt: return "unsigned int";
|
case BuiltinType::Char_U:
|
||||||
case CXType_ULong: return "unsigned long";
|
return "char";
|
||||||
case CXType_ULongLong: return "unsigned long long";
|
case BuiltinType::SChar:
|
||||||
case CXType_UInt128: return "unsigned __int128";
|
return "signed char";
|
||||||
case CXType_Char_S: return "char";
|
case BuiltinType::Short:
|
||||||
case CXType_SChar: return "signed char";
|
return "short";
|
||||||
case CXType_WChar: return "wchar_t";
|
case BuiltinType::Int:
|
||||||
case CXType_Short: return "short";
|
return "int";
|
||||||
case CXType_Int: return "int";
|
case BuiltinType::Long:
|
||||||
case CXType_Long: return "long";
|
return "long";
|
||||||
case CXType_LongLong: return "long long";
|
case BuiltinType::LongLong:
|
||||||
case CXType_Int128: return "__int128";
|
return "long long";
|
||||||
case CXType_Float: return "float";
|
case BuiltinType::Int128:
|
||||||
case CXType_Double: return "double";
|
return "__int128";
|
||||||
case CXType_LongDouble: return "long double";
|
case BuiltinType::UChar:
|
||||||
case CXType_Float128: return "__float128";
|
return "unsigned char";
|
||||||
case CXType_Half: return "_Float16";
|
case BuiltinType::UShort:
|
||||||
case CXType_NullPtr: return "nullptr";
|
return "unsigned short";
|
||||||
default: return "";
|
case BuiltinType::UInt:
|
||||||
// clang-format on
|
return "unsigned int";
|
||||||
|
case BuiltinType::ULong:
|
||||||
|
return "unsigned long";
|
||||||
|
case BuiltinType::ULongLong:
|
||||||
|
return "unsigned long long";
|
||||||
|
case BuiltinType::UInt128:
|
||||||
|
return "unsigned __int128";
|
||||||
|
case BuiltinType::Half:
|
||||||
|
return "__fp16";
|
||||||
|
case BuiltinType::Float:
|
||||||
|
return "float";
|
||||||
|
case BuiltinType::Double:
|
||||||
|
return "double";
|
||||||
|
case BuiltinType::LongDouble:
|
||||||
|
return "long double";
|
||||||
|
case BuiltinType::ShortAccum:
|
||||||
|
return "short _Accum";
|
||||||
|
case BuiltinType::Accum:
|
||||||
|
return "_Accum";
|
||||||
|
case BuiltinType::LongAccum:
|
||||||
|
return "long _Accum";
|
||||||
|
case BuiltinType::UShortAccum:
|
||||||
|
return "unsigned short _Accum";
|
||||||
|
case BuiltinType::UAccum:
|
||||||
|
return "unsigned _Accum";
|
||||||
|
case BuiltinType::ULongAccum:
|
||||||
|
return "unsigned long _Accum";
|
||||||
|
case BuiltinType::BuiltinType::ShortFract:
|
||||||
|
return "short _Fract";
|
||||||
|
case BuiltinType::BuiltinType::Fract:
|
||||||
|
return "_Fract";
|
||||||
|
case BuiltinType::BuiltinType::LongFract:
|
||||||
|
return "long _Fract";
|
||||||
|
case BuiltinType::BuiltinType::UShortFract:
|
||||||
|
return "unsigned short _Fract";
|
||||||
|
case BuiltinType::BuiltinType::UFract:
|
||||||
|
return "unsigned _Fract";
|
||||||
|
case BuiltinType::BuiltinType::ULongFract:
|
||||||
|
return "unsigned long _Fract";
|
||||||
|
case BuiltinType::BuiltinType::SatShortAccum:
|
||||||
|
return "_Sat short _Accum";
|
||||||
|
case BuiltinType::BuiltinType::SatAccum:
|
||||||
|
return "_Sat _Accum";
|
||||||
|
case BuiltinType::BuiltinType::SatLongAccum:
|
||||||
|
return "_Sat long _Accum";
|
||||||
|
case BuiltinType::BuiltinType::SatUShortAccum:
|
||||||
|
return "_Sat unsigned short _Accum";
|
||||||
|
case BuiltinType::BuiltinType::SatUAccum:
|
||||||
|
return "_Sat unsigned _Accum";
|
||||||
|
case BuiltinType::BuiltinType::SatULongAccum:
|
||||||
|
return "_Sat unsigned long _Accum";
|
||||||
|
case BuiltinType::BuiltinType::SatShortFract:
|
||||||
|
return "_Sat short _Fract";
|
||||||
|
case BuiltinType::BuiltinType::SatFract:
|
||||||
|
return "_Sat _Fract";
|
||||||
|
case BuiltinType::BuiltinType::SatLongFract:
|
||||||
|
return "_Sat long _Fract";
|
||||||
|
case BuiltinType::BuiltinType::SatUShortFract:
|
||||||
|
return "_Sat unsigned short _Fract";
|
||||||
|
case BuiltinType::BuiltinType::SatUFract:
|
||||||
|
return "_Sat unsigned _Fract";
|
||||||
|
case BuiltinType::BuiltinType::SatULongFract:
|
||||||
|
return "_Sat unsigned long _Fract";
|
||||||
|
case BuiltinType::Float16:
|
||||||
|
return "_Float16";
|
||||||
|
case BuiltinType::Float128:
|
||||||
|
return "__float128";
|
||||||
|
case BuiltinType::WChar_S:
|
||||||
|
case BuiltinType::WChar_U:
|
||||||
|
return "wchar_t";
|
||||||
|
case BuiltinType::Char8:
|
||||||
|
return "char8_t";
|
||||||
|
case BuiltinType::Char16:
|
||||||
|
return "char16_t";
|
||||||
|
case BuiltinType::Char32:
|
||||||
|
return "char32_t";
|
||||||
|
case BuiltinType::NullPtr:
|
||||||
|
return "nullptr_t";
|
||||||
|
case BuiltinType::Overload:
|
||||||
|
return "<overloaded function type>";
|
||||||
|
case BuiltinType::BoundMember:
|
||||||
|
return "<bound member function type>";
|
||||||
|
case BuiltinType::PseudoObject:
|
||||||
|
return "<pseudo-object type>";
|
||||||
|
case BuiltinType::Dependent:
|
||||||
|
return "<dependent type>";
|
||||||
|
case BuiltinType::UnknownAny:
|
||||||
|
return "<unknown type>";
|
||||||
|
case BuiltinType::ARCUnbridgedCast:
|
||||||
|
return "<ARC unbridged cast type>";
|
||||||
|
case BuiltinType::BuiltinFn:
|
||||||
|
return "<builtin fn type>";
|
||||||
|
case BuiltinType::ObjCId:
|
||||||
|
return "id";
|
||||||
|
case BuiltinType::ObjCClass:
|
||||||
|
return "Class";
|
||||||
|
case BuiltinType::ObjCSel:
|
||||||
|
return "SEL";
|
||||||
|
case BuiltinType::OCLSampler:
|
||||||
|
return "sampler_t";
|
||||||
|
case BuiltinType::OCLEvent:
|
||||||
|
return "event_t";
|
||||||
|
case BuiltinType::OCLClkEvent:
|
||||||
|
return "clk_event_t";
|
||||||
|
case BuiltinType::OCLQueue:
|
||||||
|
return "queue_t";
|
||||||
|
case BuiltinType::OCLReserveID:
|
||||||
|
return "reserve_id_t";
|
||||||
|
case BuiltinType::OMPArraySection:
|
||||||
|
return "<OpenMP array section type>";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,6 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
std::optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,
|
|
||||||
const std::string& path);
|
|
||||||
|
|
||||||
// Returns the absolute path to |file|.
|
// Returns the absolute path to |file|.
|
||||||
std::string FileName(CXFile file);
|
std::string FileName(CXFile file);
|
||||||
std::string FileName(const clang::FileEntry& file);
|
std::string FileName(const clang::FileEntry& file);
|
||||||
@ -19,4 +16,4 @@ std::string ToString(CXString cx_string);
|
|||||||
|
|
||||||
std::string ToString(CXCursorKind cursor_kind);
|
std::string ToString(CXCursorKind cursor_kind);
|
||||||
|
|
||||||
const char* ClangBuiltinTypeName(CXTypeKind);
|
const char* ClangBuiltinTypeName(int);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
#include "indexer.h"
|
#include "indexer.h"
|
||||||
|
|
||||||
|
#include "clang_tu.h"
|
||||||
#include "log.hh"
|
#include "log.hh"
|
||||||
#include "platform.h"
|
#include "platform.h"
|
||||||
#include "serializer.h"
|
#include "serializer.h"
|
||||||
@ -83,43 +84,6 @@ StringRef GetSourceInRange(const SourceManager &SM, const LangOptions &LangOpts,
|
|||||||
BInfo.second);
|
BInfo.second);
|
||||||
}
|
}
|
||||||
|
|
||||||
Range FromSourceRange(const SourceManager &SM, const LangOptions &LangOpts,
|
|
||||||
SourceRange R, llvm::sys::fs::UniqueID *UniqueID,
|
|
||||||
bool token) {
|
|
||||||
SourceLocation BLoc = R.getBegin(), ELoc = R.getEnd();
|
|
||||||
std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc);
|
|
||||||
std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc);
|
|
||||||
if (token)
|
|
||||||
EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, LangOpts);
|
|
||||||
unsigned l0 = SM.getLineNumber(BInfo.first, BInfo.second) - 1,
|
|
||||||
c0 = SM.getColumnNumber(BInfo.first, BInfo.second) - 1,
|
|
||||||
l1 = SM.getLineNumber(EInfo.first, EInfo.second) - 1,
|
|
||||||
c1 = SM.getColumnNumber(EInfo.first, EInfo.second) - 1;
|
|
||||||
if (l0 > INT16_MAX) l0 = 0;
|
|
||||||
if (c0 > INT16_MAX) c0 = 0;
|
|
||||||
if (l1 > INT16_MAX) l1 = 0;
|
|
||||||
if (c1 > INT16_MAX) c1 = 0;
|
|
||||||
if (UniqueID) {
|
|
||||||
if (const FileEntry *F = SM.getFileEntryForID(BInfo.first))
|
|
||||||
*UniqueID = F->getUniqueID();
|
|
||||||
else
|
|
||||||
*UniqueID = llvm::sys::fs::UniqueID(0, 0);
|
|
||||||
}
|
|
||||||
return {{int16_t(l0), int16_t(c0)}, {int16_t(l1), int16_t(c1)}};
|
|
||||||
}
|
|
||||||
|
|
||||||
Range FromCharRange(const SourceManager &SM, const LangOptions &LangOpts,
|
|
||||||
SourceRange R,
|
|
||||||
llvm::sys::fs::UniqueID *UniqueID = nullptr) {
|
|
||||||
return FromSourceRange(SM, LangOpts, R, UniqueID, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
Range FromTokenRange(const SourceManager &SM, const LangOptions &LangOpts,
|
|
||||||
SourceRange R,
|
|
||||||
llvm::sys::fs::UniqueID *UniqueID = nullptr) {
|
|
||||||
return FromSourceRange(SM, LangOpts, R, UniqueID, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
SymbolKind GetSymbolKind(const Decl* D) {
|
SymbolKind GetSymbolKind(const Decl* D) {
|
||||||
switch (D->getKind()) {
|
switch (D->getKind()) {
|
||||||
case Decl::TranslationUnit:
|
case Decl::TranslationUnit:
|
||||||
@ -1154,29 +1118,20 @@ std::vector<std::unique_ptr<IndexFile>> Index(
|
|||||||
DataConsumer, IndexOpts, std::make_unique<IndexFrontendAction>(param));
|
DataConsumer, IndexOpts, std::make_unique<IndexFrontendAction>(param));
|
||||||
|
|
||||||
DiagnosticErrorTrap DiagTrap(*Diags);
|
DiagnosticErrorTrap DiagTrap(*Diags);
|
||||||
bool success = false;
|
|
||||||
llvm::CrashRecoveryContext CRC;
|
llvm::CrashRecoveryContext CRC;
|
||||||
{
|
auto compile = [&]() {
|
||||||
auto compile = [&]() {
|
ASTUnit::LoadFromCompilerInvocationAction(
|
||||||
success = ASTUnit::LoadFromCompilerInvocationAction(
|
std::move(CI), PCHCO, Diags, IndexAction.get(), Unit.get(),
|
||||||
std::move(CI), PCHCO, Diags, IndexAction.get(), Unit.get(),
|
/*Persistent=*/true, /*ResourceDir=*/"",
|
||||||
/*Persistent=*/true, /*ResourceDir=*/"",
|
/*OnlyLocalDecls=*/true,
|
||||||
/*OnlyLocalDecls=*/true,
|
/*CaptureDiagnostics=*/true, 0, false, false, true);
|
||||||
/*CaptureDiagnostics=*/true, 0, false, false, true);
|
};
|
||||||
};
|
if (!RunSafely(CRC, compile)) {
|
||||||
const char *env = getenv("CCLS_CRASH_RECOVERY");
|
LOG_S(ERROR) << "clang crashed for " << file;
|
||||||
if (env && strcmp(env, "0") == 0)
|
|
||||||
compile();
|
|
||||||
else
|
|
||||||
CRC.RunSafely(compile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Unit) {
|
|
||||||
LOG_S(ERROR) << "failed to index " << file;
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
if (!success) {
|
if (!Unit) {
|
||||||
LOG_S(ERROR) << "clang crashed for " << file;
|
LOG_S(ERROR) << "failed to index " << file;
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -51,6 +51,8 @@ struct Handler_TextDocumentDidOpen
|
|||||||
|
|
||||||
include_complete->AddFile(working_file->filename);
|
include_complete->AddFile(working_file->filename);
|
||||||
clang_complete->NotifyView(path);
|
clang_complete->NotifyView(path);
|
||||||
|
if (g_config->diagnostics.onParse)
|
||||||
|
clang_complete->DiagnosticsUpdate({params.textDocument.uri});
|
||||||
if (params.args.size())
|
if (params.args.size())
|
||||||
project->SetFlagsForFile(params.args, path);
|
project->SetFlagsForFile(params.args, path);
|
||||||
|
|
||||||
|
@ -195,20 +195,6 @@ std::optional<int> FindMatchingLine(const std::vector<std::string>& index_lines,
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::vector<CXUnsavedFile> WorkingFiles::Snapshot::AsUnsavedFiles() const {
|
|
||||||
std::vector<CXUnsavedFile> result;
|
|
||||||
result.reserve(files.size());
|
|
||||||
for (auto& file : files) {
|
|
||||||
CXUnsavedFile unsaved;
|
|
||||||
unsaved.Filename = file.filename.c_str();
|
|
||||||
unsaved.Contents = file.content.c_str();
|
|
||||||
unsaved.Length = (unsigned long)file.content.size();
|
|
||||||
|
|
||||||
result.push_back(unsaved);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
WorkingFile::WorkingFile(const std::string& filename,
|
WorkingFile::WorkingFile(const std::string& filename,
|
||||||
const std::string& buffer_content)
|
const std::string& buffer_content)
|
||||||
: filename(filename), buffer_content(buffer_content) {
|
: filename(filename), buffer_content(buffer_content) {
|
||||||
|
@ -84,7 +84,6 @@ struct WorkingFiles {
|
|||||||
std::string content;
|
std::string content;
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<CXUnsavedFile> AsUnsavedFiles() const;
|
|
||||||
std::vector<File> files;
|
std::vector<File> files;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user