mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-30 11:27:07 +00:00
Use Clang C++ for completion and diagnostics
This commit is contained in:
parent
4612aa062b
commit
5dcccea285
@ -5,6 +5,8 @@
|
||||
#include "log.hh"
|
||||
#include "platform.h"
|
||||
|
||||
#include "clang/Frontend/CompilerInstance.h"
|
||||
#include "clang/Frontend/FrontendDiagnostic.h"
|
||||
#include <clang/Sema/CodeCompleteConsumer.h>
|
||||
#include <llvm/ADT/Twine.h>
|
||||
#include <llvm/Support/Threading.h>
|
||||
@ -16,25 +18,15 @@ using namespace llvm;
|
||||
|
||||
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) {
|
||||
SmallString<128> Ret;
|
||||
sys::path::append(Ret, sys::path::parent_path(path), sys::path::stem(path));
|
||||
return Ret.str();
|
||||
}
|
||||
|
||||
unsigned GetCompletionPriority(const CodeCompletionString& CCS,
|
||||
unsigned GetCompletionPriority(const CodeCompletionString &CCS,
|
||||
CXCursorKind result_kind,
|
||||
const std::optional<std::string>& typedText) {
|
||||
const std::optional<std::string> &typedText) {
|
||||
unsigned priority = CCS.getPriority();
|
||||
if (CCS.getAvailability() != CXAvailability_Available ||
|
||||
result_kind == CXCursor_Destructor ||
|
||||
@ -196,35 +188,29 @@ void BuildCompletionItemTexts(std::vector<lsCompletionItem> &out,
|
||||
// clang-format on
|
||||
}
|
||||
|
||||
for (auto i = out_first; i < out.size(); ++i)
|
||||
out[i].label += text;
|
||||
if (Kind != CodeCompletionString::CK_Informative)
|
||||
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)
|
||||
continue;
|
||||
|
||||
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 +=
|
||||
if (Kind == CodeCompletionString::CK_Placeholder) {
|
||||
out[i].insertText +=
|
||||
"${" + std::to_string(out[i].parameters_.size()) + ":" + text + "}";
|
||||
out[i].insertTextFormat = lsInsertTextFormat::Snippet;
|
||||
} else {
|
||||
out[i].insertText += text;
|
||||
out[i].insertTextFormat = lsInsertTextFormat::Snippet;
|
||||
} else {
|
||||
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
|
||||
@ -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,
|
||||
std::shared_ptr<CompletionSession> session,
|
||||
std::unique_ptr<ClangTranslationUnit>* tu,
|
||||
ClangIndex* index,
|
||||
bool emit_diag) {
|
||||
// Nothing to do. We already have a translation unit.
|
||||
if (*tu)
|
||||
return;
|
||||
|
||||
std::vector<std::string> 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");
|
||||
}
|
||||
|
||||
const auto &args = session->file.args;
|
||||
WorkingFiles::Snapshot snapshot = session->working_files->AsSnapshot(
|
||||
{StripFileType(session->file.filename)});
|
||||
std::vector<CXUnsavedFile> unsaved = snapshot.AsUnsavedFiles();
|
||||
|
||||
LOG_S(INFO) << "Creating completion session with arguments "
|
||||
<< StringJoin(args, " ");
|
||||
*tu = ClangTranslationUnit::Create(index, session->file.filename, args,
|
||||
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);
|
||||
}
|
||||
*tu = ClangTranslationUnit::Create(session->file.filename, args, snapshot);
|
||||
}
|
||||
|
||||
void CompletionPreloadMain(ClangCompleteManager* completion_manager) {
|
||||
@ -374,8 +391,7 @@ void CompletionPreloadMain(ClangCompleteManager* completion_manager) {
|
||||
continue;
|
||||
|
||||
std::unique_ptr<ClangTranslationUnit> parsing;
|
||||
TryEnsureDocumentParsed(completion_manager, session, &parsing, &tu->index,
|
||||
true);
|
||||
TryEnsureDocumentParsed(completion_manager, session, &parsing, true);
|
||||
|
||||
// Activate new translation unit.
|
||||
std::lock_guard<std::mutex> lock(tu->lock);
|
||||
@ -404,84 +420,43 @@ void CompletionQueryMain(ClangCompleteManager* completion_manager) {
|
||||
true /*create_if_needed*/);
|
||||
|
||||
std::lock_guard<std::mutex> lock(session->completion.lock);
|
||||
TryEnsureDocumentParsed(completion_manager, session, &session->completion.tu,
|
||||
&session->completion.index, false);
|
||||
TryEnsureDocumentParsed(completion_manager, session, &session->completion.tu, false);
|
||||
|
||||
// It is possible we failed to create the document despite
|
||||
// |TryEnsureDocumentParsed|.
|
||||
if (!session->completion.tu)
|
||||
continue;
|
||||
if (ClangTranslationUnit* tu = session->completion.tu.get()) {
|
||||
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 =
|
||||
completion_manager->working_files_->AsSnapshot({StripFileType(path)});
|
||||
std::vector<CXUnsavedFile> unsaved = snapshot.AsUnsavedFiles();
|
||||
IntrusiveRefCntPtr<SourceManager> SrcMgr(
|
||||
new SourceManager(*Diag, *FileMgr));
|
||||
std::vector<ASTUnit::RemappedFile> Remapped = GetRemapped(snapshot);
|
||||
SmallVector<StoredDiagnostic, 8> Diagnostics;
|
||||
SmallVector<const llvm::MemoryBuffer *, 1> TemporaryBuffers;
|
||||
|
||||
unsigned const kCompleteOptions =
|
||||
CXCodeComplete_IncludeMacros | CXCodeComplete_IncludeBriefComments;
|
||||
CXCodeCompleteResults* cx_results = clang_codeCompleteAt(
|
||||
session->completion.tu->cx_tu, session->file.filename.c_str(),
|
||||
request->position.line + 1, request->position.character + 1,
|
||||
unsaved.data(), (unsigned)unsaved.size(), kCompleteOptions);
|
||||
if (!cx_results) {
|
||||
request->on_complete({}, false /*is_cached_result*/);
|
||||
continue;
|
||||
CodeCompleteOptions Opts;
|
||||
LangOptions LangOpts;
|
||||
Opts.IncludeBriefComments = true;
|
||||
Opts.LoadExternal = false;
|
||||
Opts.IncludeFixIts = true;
|
||||
CaptureCompletionResults capture(Opts);
|
||||
tu->Unit->CodeComplete(session->file.filename, request->position.line + 1,
|
||||
request->position.character + 1, Remapped,
|
||||
/*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.
|
||||
std::lock_guard<std::mutex> lock(session->diagnostics.lock);
|
||||
TryEnsureDocumentParsed(
|
||||
completion_manager, session, &session->diagnostics.tu,
|
||||
&session->diagnostics.index, false /*emit_diagnostics*/);
|
||||
TryEnsureDocumentParsed(completion_manager, session,
|
||||
&session->diagnostics.tu,
|
||||
false /*emit_diagnostics*/);
|
||||
|
||||
// It is possible we failed to create the document despite
|
||||
// |TryEnsureDocumentParsed|.
|
||||
if (!session->diagnostics.tu)
|
||||
ClangTranslationUnit* tu = session->diagnostics.tu.get();
|
||||
if (!tu)
|
||||
continue;
|
||||
|
||||
WorkingFiles::Snapshot snapshot =
|
||||
completion_manager->working_files_->AsSnapshot({StripFileType(path)});
|
||||
std::vector<CXUnsavedFile> unsaved = snapshot.AsUnsavedFiles();
|
||||
|
||||
// Emit diagnostics.
|
||||
session->diagnostics.tu = ClangTranslationUnit::Reparse(
|
||||
std::move(session->diagnostics.tu), unsaved);
|
||||
if (!session->diagnostics.tu) {
|
||||
llvm::CrashRecoveryContext CRC;
|
||||
if (tu->Reparse(CRC, snapshot)) {
|
||||
LOG_S(ERROR) << "Reparsing translation unit for diagnostics failed for "
|
||||
<< path;
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t num_diagnostics =
|
||||
clang_getNumDiagnostics(session->diagnostics.tu->cx_tu);
|
||||
std::vector<lsDiagnostic> ls_diagnostics;
|
||||
ls_diagnostics.reserve(num_diagnostics);
|
||||
for (unsigned i = 0; i < num_diagnostics; ++i) {
|
||||
CXDiagnostic cx_diag =
|
||||
clang_getDiagnostic(session->diagnostics.tu->cx_tu, i);
|
||||
std::optional<lsDiagnostic> diagnostic =
|
||||
BuildAndDisposeDiagnostic(cx_diag, path);
|
||||
// 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);
|
||||
auto &SM = tu->Unit->getSourceManager();
|
||||
auto &LangOpts = tu->Unit->getLangOpts();
|
||||
std::vector<lsDiagnostic> ls_diags;
|
||||
for (ASTUnit::stored_diag_iterator I = tu->Unit->stored_diag_begin(),
|
||||
E = tu->Unit->stored_diag_end();
|
||||
I != E; ++I) {
|
||||
FullSourceLoc FLoc = I->getLocation();
|
||||
SourceRange R;
|
||||
for (const auto &CR : I->getRanges()) {
|
||||
auto RT = Lexer::makeFileCharRange(CR, SM, LangOpts);
|
||||
if (SM.isPointWithin(FLoc, RT.getBegin(), RT.getEnd())) {
|
||||
R = CR.getAsRange();
|
||||
break;
|
||||
}
|
||||
}
|
||||
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> {
|
||||
// Translation unit for clang.
|
||||
struct Tu {
|
||||
ClangIndex index{0, 0};
|
||||
|
||||
// When |tu| was last parsed.
|
||||
std::optional<std::chrono::time_point<std::chrono::high_resolution_clock>>
|
||||
last_parsed_at;
|
||||
|
242
src/clang_tu.cc
242
src/clang_tu.cc
@ -4,173 +4,113 @@
|
||||
#include "log.hh"
|
||||
#include "platform.h"
|
||||
#include "utils.h"
|
||||
#include "working_files.h"
|
||||
|
||||
#include <llvm/Support/CrashRecoveryContext.h>
|
||||
using namespace clang;
|
||||
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
#include <algorithm>
|
||||
#include <mutex>
|
||||
|
||||
namespace {
|
||||
|
||||
void EmitDiagnostics(std::string path,
|
||||
std::vector<const char*> args,
|
||||
CXTranslationUnit tu) {
|
||||
std::string output = "Fatal errors while trying to parse " + path + "\n";
|
||||
output +=
|
||||
"Args: " +
|
||||
StringJoinMap(args, [](const char* arg) { return std::string(arg); }) +
|
||||
"\n";
|
||||
|
||||
size_t num_diagnostics = clang_getNumDiagnostics(tu);
|
||||
for (unsigned i = 0; i < num_diagnostics; ++i) {
|
||||
output += " - ";
|
||||
|
||||
CXDiagnostic diagnostic = clang_getDiagnostic(tu, i);
|
||||
|
||||
// Location.
|
||||
CXFile file;
|
||||
unsigned int line, column;
|
||||
clang_getSpellingLocation(clang_getDiagnosticLocation(diagnostic), &file,
|
||||
&line, &column, nullptr);
|
||||
std::string path = FileName(file);
|
||||
output += path + ":" + std::to_string(line - 1) + ":" +
|
||||
std::to_string(column) + " ";
|
||||
|
||||
// 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";
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
return {{int16_t(l0), int16_t(c0)}, {int16_t(l1), int16_t(c1)}};
|
||||
}
|
||||
|
||||
ClangIndex::~ClangIndex() {
|
||||
clang_disposeIndex(cx_index);
|
||||
Range FromCharRange(const SourceManager &SM, const LangOptions &LangOpts,
|
||||
SourceRange R,
|
||||
llvm::sys::fs::UniqueID *UniqueID) {
|
||||
return FromSourceRange(SM, LangOpts, R, UniqueID, false);
|
||||
}
|
||||
|
||||
// static
|
||||
std::unique_ptr<ClangTranslationUnit> ClangTranslationUnit::Create(
|
||||
ClangIndex* index,
|
||||
const std::string& filepath,
|
||||
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());
|
||||
Range FromTokenRange(const SourceManager &SM, const LangOptions &LangOpts,
|
||||
SourceRange R,
|
||||
llvm::sys::fs::UniqueID *UniqueID) {
|
||||
return FromSourceRange(SM, LangOpts, R, UniqueID, true);
|
||||
}
|
||||
|
||||
CXTranslationUnit cx_tu;
|
||||
CXErrorCode error_code;
|
||||
{
|
||||
error_code = clang_parseTranslationUnit2FullArgv(
|
||||
index->cx_index, nullptr, args.data(), (int)args.size(),
|
||||
unsaved_files.data(), (unsigned)unsaved_files.size(), flags, &cx_tu);
|
||||
std::vector<ASTUnit::RemappedFile>
|
||||
GetRemapped(const WorkingFiles::Snapshot &snapshot) {
|
||||
std::vector<ASTUnit::RemappedFile> Remapped;
|
||||
for (auto &file : snapshot.files) {
|
||||
std::unique_ptr<llvm::MemoryBuffer> MB =
|
||||
llvm::MemoryBuffer::getMemBufferCopy(file.content, file.filename);
|
||||
Remapped.emplace_back(file.filename, MB.release());
|
||||
}
|
||||
return Remapped;
|
||||
}
|
||||
|
||||
if (error_code != CXError_Success && cx_tu)
|
||||
EmitDiagnostics(filepath, args, cx_tu);
|
||||
std::unique_ptr<ClangTranslationUnit>
|
||||
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
|
||||
// -fsyntax-only so they don't do a full compile.
|
||||
auto make_msg = [&]() {
|
||||
return "Please try running the following, identify which flag causes the "
|
||||
"issue, and report a bug. ccls will then filter the flag for you "
|
||||
" automatically:\n " +
|
||||
StringJoin(args, " ") + " -fsyntax-only";
|
||||
auto ret = std::make_unique<ClangTranslationUnit>();
|
||||
IntrusiveRefCntPtr<DiagnosticsEngine> Diags(
|
||||
CompilerInstance::createDiagnostics(new DiagnosticOptions));
|
||||
std::vector<ASTUnit::RemappedFile> Remapped = GetRemapped(snapshot);
|
||||
|
||||
ret->PCHCO = std::make_shared<PCHContainerOperations>();
|
||||
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));
|
||||
};
|
||||
|
||||
switch (error_code) {
|
||||
case CXError_Success:
|
||||
return std::make_unique<ClangTranslationUnit>(cx_tu);
|
||||
case CXError_Failure:
|
||||
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 (!RunSafely(CRC, parse)) {
|
||||
LOG_S(ERROR)
|
||||
<< "clang crashed for " << filepath << "\n"
|
||||
<< StringJoin(args, " ") + " -fsyntax-only";
|
||||
return {};
|
||||
}
|
||||
if (!Unit && !ErrUnit)
|
||||
return {};
|
||||
|
||||
return nullptr;
|
||||
ret->Unit = std::move(Unit);
|
||||
return ret;
|
||||
}
|
||||
|
||||
// static
|
||||
std::unique_ptr<ClangTranslationUnit> ClangTranslationUnit::Reparse(
|
||||
std::unique_ptr<ClangTranslationUnit> tu,
|
||||
std::vector<CXUnsavedFile>& unsaved) {
|
||||
int error_code = clang_reparseTranslationUnit(
|
||||
tu->cx_tu, (unsigned)unsaved.size(), unsaved.data(),
|
||||
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);
|
||||
int ClangTranslationUnit::Reparse(llvm::CrashRecoveryContext &CRC,
|
||||
const WorkingFiles::Snapshot &snapshot) {
|
||||
int ret = 1;
|
||||
auto parse = [&]() { ret = Unit->Reparse(PCHCO, GetRemapped(snapshot)); };
|
||||
(void)RunSafely(CRC, parse);
|
||||
return ret;
|
||||
}
|
||||
|
@ -1,40 +1,45 @@
|
||||
#pragma once
|
||||
#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 <string>
|
||||
#include <vector>
|
||||
#include <stdlib.h>
|
||||
|
||||
// Simple RAII wrapper about CXIndex.
|
||||
// Note: building a ClangIndex instance acquires a global lock, since libclang
|
||||
// API does not appear to be thread-safe here.
|
||||
class ClangIndex {
|
||||
public:
|
||||
ClangIndex();
|
||||
ClangIndex(int exclude_declarations_from_pch, int display_diagnostics);
|
||||
~ClangIndex();
|
||||
CXIndex cx_index;
|
||||
};
|
||||
std::vector<clang::ASTUnit::RemappedFile>
|
||||
GetRemapped(const WorkingFiles::Snapshot &snapshot);
|
||||
|
||||
Range FromCharRange(const clang::SourceManager &SM, const clang::LangOptions &LangOpts,
|
||||
clang::SourceRange R,
|
||||
llvm::sys::fs::UniqueID *UniqueID = nullptr);
|
||||
|
||||
Range FromTokenRange(const clang::SourceManager &SM, const clang::LangOptions &LangOpts,
|
||||
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 {
|
||||
static std::unique_ptr<ClangTranslationUnit> Create(
|
||||
ClangIndex* index,
|
||||
const std::string& filepath,
|
||||
const std::vector<std::string>& arguments,
|
||||
std::vector<CXUnsavedFile>& unsaved_files,
|
||||
unsigned flags);
|
||||
static std::unique_ptr<ClangTranslationUnit>
|
||||
Create(const std::string &filepath, const std::vector<std::string> &arguments,
|
||||
const WorkingFiles::Snapshot &snapshot);
|
||||
|
||||
static std::unique_ptr<ClangTranslationUnit> Reparse(
|
||||
std::unique_ptr<ClangTranslationUnit> tu,
|
||||
std::vector<CXUnsavedFile>& unsaved);
|
||||
int Reparse(llvm::CrashRecoveryContext &CRC,
|
||||
const WorkingFiles::Snapshot &snapshot);
|
||||
|
||||
explicit ClangTranslationUnit(CXTranslationUnit tu);
|
||||
~ClangTranslationUnit();
|
||||
|
||||
CXTranslationUnit cx_tu;
|
||||
std::shared_ptr<clang::PCHContainerOperations> PCHCO;
|
||||
std::unique_ptr<clang::ASTUnit> Unit;
|
||||
};
|
||||
|
@ -1,113 +1,12 @@
|
||||
#include "clang_utils.h"
|
||||
|
||||
#include "filesystem.hh"
|
||||
#include "platform.h"
|
||||
|
||||
#include "filesystem.hh"
|
||||
#include <clang/AST/Type.h>
|
||||
using namespace clang;
|
||||
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 ret;
|
||||
// clang > 6
|
||||
@ -153,34 +52,145 @@ std::string ToString(CXCursorKind kind) {
|
||||
return ToString(clang_getCursorKindSpelling(kind));
|
||||
}
|
||||
|
||||
const char* ClangBuiltinTypeName(CXTypeKind kind) {
|
||||
switch (kind) {
|
||||
// clang-format off
|
||||
case CXType_Bool: return "bool";
|
||||
case CXType_Char_U: return "char";
|
||||
case CXType_UChar: return "unsigned char";
|
||||
case CXType_Char16: return "char16_t";
|
||||
case CXType_Char32: return "char32_t";
|
||||
case CXType_UShort: return "unsigned short";
|
||||
case CXType_UInt: return "unsigned int";
|
||||
case CXType_ULong: return "unsigned long";
|
||||
case CXType_ULongLong: return "unsigned long long";
|
||||
case CXType_UInt128: return "unsigned __int128";
|
||||
case CXType_Char_S: return "char";
|
||||
case CXType_SChar: return "signed char";
|
||||
case CXType_WChar: return "wchar_t";
|
||||
case CXType_Short: return "short";
|
||||
case CXType_Int: return "int";
|
||||
case CXType_Long: return "long";
|
||||
case CXType_LongLong: return "long long";
|
||||
case CXType_Int128: return "__int128";
|
||||
case CXType_Float: return "float";
|
||||
case CXType_Double: return "double";
|
||||
case CXType_LongDouble: return "long double";
|
||||
case CXType_Float128: return "__float128";
|
||||
case CXType_Half: return "_Float16";
|
||||
case CXType_NullPtr: return "nullptr";
|
||||
default: return "";
|
||||
// clang-format on
|
||||
// clang::BuiltinType::getName without PrintingPolicy
|
||||
const char* ClangBuiltinTypeName(int kind) {
|
||||
switch (BuiltinType::Kind(kind)) {
|
||||
case BuiltinType::Void:
|
||||
return "void";
|
||||
case BuiltinType::Bool:
|
||||
return "bool";
|
||||
case BuiltinType::Char_S:
|
||||
return "char";
|
||||
case BuiltinType::Char_U:
|
||||
return "char";
|
||||
case BuiltinType::SChar:
|
||||
return "signed char";
|
||||
case BuiltinType::Short:
|
||||
return "short";
|
||||
case BuiltinType::Int:
|
||||
return "int";
|
||||
case BuiltinType::Long:
|
||||
return "long";
|
||||
case BuiltinType::LongLong:
|
||||
return "long long";
|
||||
case BuiltinType::Int128:
|
||||
return "__int128";
|
||||
case BuiltinType::UChar:
|
||||
return "unsigned char";
|
||||
case BuiltinType::UShort:
|
||||
return "unsigned short";
|
||||
case BuiltinType::UInt:
|
||||
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 <vector>
|
||||
|
||||
std::optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,
|
||||
const std::string& path);
|
||||
|
||||
// Returns the absolute path to |file|.
|
||||
std::string FileName(CXFile file);
|
||||
std::string FileName(const clang::FileEntry& file);
|
||||
@ -19,4 +16,4 @@ std::string ToString(CXString cx_string);
|
||||
|
||||
std::string ToString(CXCursorKind cursor_kind);
|
||||
|
||||
const char* ClangBuiltinTypeName(CXTypeKind);
|
||||
const char* ClangBuiltinTypeName(int);
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include "indexer.h"
|
||||
|
||||
#include "clang_tu.h"
|
||||
#include "log.hh"
|
||||
#include "platform.h"
|
||||
#include "serializer.h"
|
||||
@ -83,43 +84,6 @@ StringRef GetSourceInRange(const SourceManager &SM, const LangOptions &LangOpts,
|
||||
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) {
|
||||
switch (D->getKind()) {
|
||||
case Decl::TranslationUnit:
|
||||
@ -1167,29 +1131,20 @@ std::vector<std::unique_ptr<IndexFile>> Index(
|
||||
DataConsumer, IndexOpts, std::make_unique<IndexFrontendAction>(param));
|
||||
|
||||
DiagnosticErrorTrap DiagTrap(*Diags);
|
||||
bool success = false;
|
||||
llvm::CrashRecoveryContext CRC;
|
||||
{
|
||||
auto compile = [&]() {
|
||||
success = ASTUnit::LoadFromCompilerInvocationAction(
|
||||
std::move(CI), PCHCO, Diags, IndexAction.get(), Unit.get(),
|
||||
/*Persistent=*/true, /*ResourceDir=*/"",
|
||||
/*OnlyLocalDecls=*/true,
|
||||
/*CaptureDiagnostics=*/true, 0, false, false, true);
|
||||
};
|
||||
const char *env = getenv("CCLS_CRASH_RECOVERY");
|
||||
if (env && strcmp(env, "0") == 0)
|
||||
compile();
|
||||
else
|
||||
CRC.RunSafely(compile);
|
||||
}
|
||||
|
||||
if (!Unit) {
|
||||
LOG_S(ERROR) << "failed to index " << file;
|
||||
auto compile = [&]() {
|
||||
ASTUnit::LoadFromCompilerInvocationAction(
|
||||
std::move(CI), PCHCO, Diags, IndexAction.get(), Unit.get(),
|
||||
/*Persistent=*/true, /*ResourceDir=*/"",
|
||||
/*OnlyLocalDecls=*/true,
|
||||
/*CaptureDiagnostics=*/true, 0, false, false, true);
|
||||
};
|
||||
if (!RunSafely(CRC, compile)) {
|
||||
LOG_S(ERROR) << "clang crashed for " << file;
|
||||
return {};
|
||||
}
|
||||
if (!success) {
|
||||
LOG_S(ERROR) << "clang crashed for " << file;
|
||||
if (!Unit) {
|
||||
LOG_S(ERROR) << "failed to index " << file;
|
||||
return {};
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,8 @@ struct Handler_TextDocumentDidOpen
|
||||
|
||||
include_complete->AddFile(working_file->filename);
|
||||
clang_complete->NotifyView(path);
|
||||
if (g_config->diagnostics.onParse)
|
||||
clang_complete->DiagnosticsUpdate({params.textDocument.uri});
|
||||
if (params.args.size())
|
||||
project->SetFlagsForFile(params.args, path);
|
||||
|
||||
|
@ -195,20 +195,6 @@ std::optional<int> FindMatchingLine(const std::vector<std::string>& index_lines,
|
||||
|
||||
} // 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,
|
||||
const std::string& buffer_content)
|
||||
: filename(filename), buffer_content(buffer_content) {
|
||||
|
@ -84,7 +84,6 @@ struct WorkingFiles {
|
||||
std::string content;
|
||||
};
|
||||
|
||||
std::vector<CXUnsavedFile> AsUnsavedFiles() const;
|
||||
std::vector<File> files;
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user