mirror of
https://github.com/MaskRay/ccls.git
synced 2024-11-22 07:35:08 +00:00
New diagnostics
This commit is contained in:
parent
a66533b023
commit
fea457616a
@ -22,6 +22,7 @@ using namespace llvm;
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
|
namespace ccls {
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
std::string StripFileType(const std::string &path) {
|
std::string StripFileType(const std::string &path) {
|
||||||
@ -292,6 +293,38 @@ void BuildDetailString(const CodeCompletionString &CCS, lsCompletionItem &item,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool LocationInRange(SourceLocation L, CharSourceRange R,
|
||||||
|
const SourceManager &M) {
|
||||||
|
assert(R.isCharRange());
|
||||||
|
if (!R.isValid() || M.getFileID(R.getBegin()) != M.getFileID(R.getEnd()) ||
|
||||||
|
M.getFileID(R.getBegin()) != M.getFileID(L))
|
||||||
|
return false;
|
||||||
|
return L != R.getEnd() && M.isPointWithin(L, R.getBegin(), R.getEnd());
|
||||||
|
}
|
||||||
|
|
||||||
|
CharSourceRange DiagnosticRange(const clang::Diagnostic &D, const LangOptions &L) {
|
||||||
|
auto &M = D.getSourceManager();
|
||||||
|
auto Loc = M.getFileLoc(D.getLocation());
|
||||||
|
// Accept the first range that contains the location.
|
||||||
|
for (const auto &CR : D.getRanges()) {
|
||||||
|
auto R = Lexer::makeFileCharRange(CR, M, L);
|
||||||
|
if (LocationInRange(Loc, R, M))
|
||||||
|
return R;
|
||||||
|
}
|
||||||
|
// The range may be given as a fixit hint instead.
|
||||||
|
for (const auto &F : D.getFixItHints()) {
|
||||||
|
auto R = Lexer::makeFileCharRange(F.RemoveRange, M, L);
|
||||||
|
if (LocationInRange(Loc, R, M))
|
||||||
|
return R;
|
||||||
|
}
|
||||||
|
// If no suitable range is found, just use the token at the location.
|
||||||
|
auto R = Lexer::makeFileCharRange(CharSourceRange::getTokenRange(Loc), M, L);
|
||||||
|
if (!R.isValid()) // Fall back to location only, let the editor deal with it.
|
||||||
|
R = CharSourceRange::getCharRange(Loc);
|
||||||
|
return R;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
class CaptureCompletionResults : public CodeCompleteConsumer {
|
class CaptureCompletionResults : public CodeCompleteConsumer {
|
||||||
std::shared_ptr<clang::GlobalCodeCompletionAllocator> Alloc;
|
std::shared_ptr<clang::GlobalCodeCompletionAllocator> Alloc;
|
||||||
CodeCompletionTUInfo CCTUInfo;
|
CodeCompletionTUInfo CCTUInfo;
|
||||||
@ -360,23 +393,83 @@ public:
|
|||||||
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
CodeCompletionTUInfo &getCodeCompletionTUInfo() override { return CCTUInfo; }
|
||||||
};
|
};
|
||||||
|
|
||||||
void TryEnsureDocumentParsed(ClangCompleteManager *manager,
|
class StoreDiags : public DiagnosticConsumer {
|
||||||
std::shared_ptr<CompletionSession> session,
|
const LangOptions *LangOpts;
|
||||||
std::unique_ptr<ClangTranslationUnit> *tu,
|
std::optional<Diag> last;
|
||||||
bool diagnostic) {
|
std::vector<Diag> output;
|
||||||
// Nothing to do. We already have a translation unit.
|
void Flush() {
|
||||||
if (*tu)
|
if (!last)
|
||||||
return;
|
return;
|
||||||
|
bool mentions = last->inside_main || last->edits.size();
|
||||||
|
if (!mentions)
|
||||||
|
for (auto &N : last->notes)
|
||||||
|
if (N.inside_main)
|
||||||
|
mentions = true;
|
||||||
|
if (mentions)
|
||||||
|
output.push_back(std::move(*last));
|
||||||
|
last.reset();
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
std::vector<Diag> Take() {
|
||||||
|
return std::move(output);
|
||||||
|
}
|
||||||
|
void BeginSourceFile(const LangOptions &Opts, const Preprocessor *) override {
|
||||||
|
LangOpts = &Opts;
|
||||||
|
}
|
||||||
|
void EndSourceFile() override {
|
||||||
|
Flush();
|
||||||
|
}
|
||||||
|
void HandleDiagnostic(DiagnosticsEngine::Level Level,
|
||||||
|
const Diagnostic &Info) override {
|
||||||
|
DiagnosticConsumer::HandleDiagnostic(Level, Info);
|
||||||
|
SourceLocation L = Info.getLocation();
|
||||||
|
if (!L.isValid()) return;
|
||||||
|
const SourceManager &SM = Info.getSourceManager();
|
||||||
|
bool inside_main = SM.isInMainFile(L);
|
||||||
|
auto fillDiagBase = [&](DiagBase &d) {
|
||||||
|
llvm::SmallString<64> Message;
|
||||||
|
Info.FormatDiagnostic(Message);
|
||||||
|
d.range =
|
||||||
|
FromCharSourceRange(SM, *LangOpts, DiagnosticRange(Info, *LangOpts));
|
||||||
|
d.message = Message.str();
|
||||||
|
d.inside_main = inside_main;
|
||||||
|
d.file = SM.getFilename(Info.getLocation());
|
||||||
|
d.level = Level;
|
||||||
|
d.category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
|
||||||
|
};
|
||||||
|
|
||||||
const auto &args = session->file.args;
|
auto addFix = [&](bool SyntheticMessage) -> bool {
|
||||||
WorkingFiles::Snapshot snapshot = session->wfiles->AsSnapshot(
|
if (!inside_main)
|
||||||
{StripFileType(session->file.filename)});
|
return false;
|
||||||
|
for (const FixItHint &FixIt : Info.getFixItHints()) {
|
||||||
|
if (!SM.isInMainFile(FixIt.RemoveRange.getBegin()))
|
||||||
|
return false;
|
||||||
|
lsTextEdit edit;
|
||||||
|
edit.newText = FixIt.CodeToInsert;
|
||||||
|
auto r = FromCharSourceRange(SM, *LangOpts, FixIt.RemoveRange);
|
||||||
|
edit.range =
|
||||||
|
lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}};
|
||||||
|
last->edits.push_back(std::move(edit));
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
LOG_S(INFO) << "create " << (diagnostic ? "diagnostic" : "completion")
|
if (Level == DiagnosticsEngine::Note || Level == DiagnosticsEngine::Remark) {
|
||||||
<< " TU for " << session->file.filename;
|
if (Info.getFixItHints().size()) {
|
||||||
*tu = ClangTranslationUnit::Create(session->file.filename, args, snapshot,
|
addFix(false);
|
||||||
diagnostic);
|
} else {
|
||||||
}
|
Note &n = last->notes.emplace_back();
|
||||||
|
fillDiagBase(n);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Flush();
|
||||||
|
last = Diag();
|
||||||
|
fillDiagBase(*last);
|
||||||
|
if (!Info.getFixItHints().empty())
|
||||||
|
addFix(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
std::unique_ptr<CompilerInvocation>
|
std::unique_ptr<CompilerInvocation>
|
||||||
buildCompilerInvocation(const std::vector<std::string> &args,
|
buildCompilerInvocation(const std::vector<std::string> &args,
|
||||||
@ -389,12 +482,61 @@ buildCompilerInvocation(const std::vector<std::string> &args,
|
|||||||
std::unique_ptr<CompilerInvocation> CI =
|
std::unique_ptr<CompilerInvocation> CI =
|
||||||
createInvocationFromCommandLine(cargs, Diags, VFS);
|
createInvocationFromCommandLine(cargs, Diags, VFS);
|
||||||
if (CI) {
|
if (CI) {
|
||||||
|
CI->getFrontendOpts().DisableFree = false;
|
||||||
CI->getLangOpts()->CommentOpts.ParseAllComments = true;
|
CI->getLangOpts()->CommentOpts.ParseAllComments = true;
|
||||||
CI->getLangOpts()->SpellChecking = false;
|
CI->getLangOpts()->SpellChecking = false;
|
||||||
}
|
}
|
||||||
return CI;
|
return CI;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<CompilerInstance>
|
||||||
|
BuildCompilerInstance(CompletionSession &session,
|
||||||
|
std::unique_ptr<CompilerInvocation> CI,
|
||||||
|
DiagnosticConsumer &DC,
|
||||||
|
const WorkingFiles::Snapshot &snapshot,
|
||||||
|
std::vector<std::unique_ptr<llvm::MemoryBuffer>> &Bufs) {
|
||||||
|
for (auto &file : snapshot.files) {
|
||||||
|
Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(file.content));
|
||||||
|
if (file.filename == session.file.filename) {
|
||||||
|
if (auto Preamble = session.GetPreamble()) {
|
||||||
|
#if LLVM_VERSION_MAJOR >= 7
|
||||||
|
Preamble->Preamble.OverridePreamble(*CI, session.FS,
|
||||||
|
Bufs.back().get());
|
||||||
|
#else
|
||||||
|
Preamble->Preamble.AddImplicitPreamble(*CI, session->FS,
|
||||||
|
Bufs.back().get());
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
CI->getPreprocessorOpts().addRemappedFile(
|
||||||
|
CI->getFrontendOpts().Inputs[0].getFile(), Bufs.back().get());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
CI->getPreprocessorOpts().addRemappedFile(file.filename,
|
||||||
|
Bufs.back().get());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Clang = std::make_unique<CompilerInstance>(session.PCH);
|
||||||
|
Clang->setInvocation(std::move(CI));
|
||||||
|
Clang->setVirtualFileSystem(session.FS);
|
||||||
|
Clang->createDiagnostics(&DC, false);
|
||||||
|
Clang->setTarget(TargetInfo::CreateTargetInfo(
|
||||||
|
Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
|
||||||
|
if (!Clang->hasTarget())
|
||||||
|
return nullptr;
|
||||||
|
return Clang;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Parse(CompilerInstance &Clang) {
|
||||||
|
SyntaxOnlyAction Action;
|
||||||
|
if (!Action.BeginSourceFile(Clang, Clang.getFrontendOpts().Inputs[0]))
|
||||||
|
return false;
|
||||||
|
if (!Action.Execute())
|
||||||
|
return false;
|
||||||
|
Action.EndSourceFile();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void CompletionPreloadMain(ClangCompleteManager *completion_manager) {
|
void CompletionPreloadMain(ClangCompleteManager *completion_manager) {
|
||||||
while (true) {
|
while (true) {
|
||||||
// Fetching the completion request blocks until we have a request.
|
// Fetching the completion request blocks until we have a request.
|
||||||
@ -449,56 +591,23 @@ void CompletionQueryMain(ClangCompleteManager *completion_manager) {
|
|||||||
#endif
|
#endif
|
||||||
CCOpts.IncludeCodePatterns = true;
|
CCOpts.IncludeCodePatterns = true;
|
||||||
auto &FOpts = CI->getFrontendOpts();
|
auto &FOpts = CI->getFrontendOpts();
|
||||||
FOpts.DisableFree = false;
|
|
||||||
FOpts.CodeCompleteOpts = CCOpts;
|
FOpts.CodeCompleteOpts = CCOpts;
|
||||||
FOpts.CodeCompletionAt.FileName = session->file.filename;
|
FOpts.CodeCompletionAt.FileName = session->file.filename;
|
||||||
FOpts.CodeCompletionAt.Line = request->position.line + 1;
|
FOpts.CodeCompletionAt.Line = request->position.line + 1;
|
||||||
FOpts.CodeCompletionAt.Column = request->position.character + 1;
|
FOpts.CodeCompletionAt.Column = request->position.character + 1;
|
||||||
|
|
||||||
|
StoreDiags DC;
|
||||||
WorkingFiles::Snapshot snapshot =
|
WorkingFiles::Snapshot snapshot =
|
||||||
completion_manager->working_files_->AsSnapshot({StripFileType(path)});
|
completion_manager->working_files_->AsSnapshot({StripFileType(path)});
|
||||||
|
|
||||||
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Bufs;
|
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Bufs;
|
||||||
for (auto &file : snapshot.files) {
|
auto Clang = BuildCompilerInstance(*session, std::move(CI), DC, snapshot, Bufs);
|
||||||
Bufs.push_back(llvm::MemoryBuffer::getMemBuffer(file.content));
|
if (!Clang)
|
||||||
if (file.filename == session->file.filename) {
|
continue;
|
||||||
if (auto Preamble = session->GetPreamble()) {
|
|
||||||
#if LLVM_VERSION_MAJOR >= 7
|
|
||||||
Preamble->Preamble.OverridePreamble(*CI, session->FS,
|
|
||||||
Bufs.back().get());
|
|
||||||
#else
|
|
||||||
Preamble->Preamble.AddImplicitPreamble(*CI, session->FS,
|
|
||||||
Bufs.back().get());
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
CI->getPreprocessorOpts().addRemappedFile(
|
|
||||||
CI->getFrontendOpts().Inputs[0].getFile(), Bufs.back().get());
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
CI->getPreprocessorOpts().addRemappedFile(file.filename,
|
|
||||||
Bufs.back().get());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
IgnoringDiagConsumer DC;
|
|
||||||
auto Clang = std::make_unique<CompilerInstance>(session->PCH);
|
|
||||||
Clang->setInvocation(std::move(CI));
|
|
||||||
Clang->createDiagnostics(&DC, false);
|
|
||||||
auto Consumer = new CaptureCompletionResults(CCOpts);
|
auto Consumer = new CaptureCompletionResults(CCOpts);
|
||||||
Clang->setCodeCompletionConsumer(Consumer);
|
Clang->setCodeCompletionConsumer(Consumer);
|
||||||
Clang->setVirtualFileSystem(session->FS);
|
if (!Parse(*Clang))
|
||||||
Clang->setTarget(TargetInfo::CreateTargetInfo(
|
|
||||||
Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
|
|
||||||
if (!Clang->hasTarget())
|
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
SyntaxOnlyAction Action;
|
|
||||||
if (!Action.BeginSourceFile(*Clang, Clang->getFrontendOpts().Inputs[0]))
|
|
||||||
continue;
|
|
||||||
if (!Action.Execute())
|
|
||||||
continue;
|
|
||||||
Action.EndSourceFile();
|
|
||||||
for (auto &Buf : Bufs)
|
for (auto &Buf : Bufs)
|
||||||
Buf.release();
|
Buf.release();
|
||||||
|
|
||||||
@ -518,51 +627,31 @@ void DiagnosticQueryMain(ClangCompleteManager *manager) {
|
|||||||
std::shared_ptr<CompletionSession> session = manager->TryGetSession(
|
std::shared_ptr<CompletionSession> session = manager->TryGetSession(
|
||||||
path, true /*mark_as_completion*/, true /*create_if_needed*/);
|
path, true /*mark_as_completion*/, true /*create_if_needed*/);
|
||||||
|
|
||||||
// At this point, we must have a translation unit. Block until we have one.
|
std::unique_ptr<CompilerInvocation> CI =
|
||||||
std::lock_guard<std::mutex> lock(session->diagnostics.lock);
|
buildCompilerInvocation(session->file.args, session->FS);
|
||||||
TryEnsureDocumentParsed(manager, session, &session->diagnostics.tu, true);
|
if (!CI)
|
||||||
|
|
||||||
// It is possible we failed to create the document despite
|
|
||||||
// |TryEnsureDocumentParsed|.
|
|
||||||
ClangTranslationUnit *tu = session->diagnostics.tu.get();
|
|
||||||
if (!tu)
|
|
||||||
continue;
|
continue;
|
||||||
|
StoreDiags DC;
|
||||||
WorkingFiles::Snapshot snapshot =
|
WorkingFiles::Snapshot snapshot =
|
||||||
manager->working_files_->AsSnapshot({StripFileType(path)});
|
manager->working_files_->AsSnapshot({StripFileType(path)});
|
||||||
llvm::CrashRecoveryContext CRC;
|
std::vector<std::unique_ptr<llvm::MemoryBuffer>> Bufs;
|
||||||
if (tu->Reparse(CRC, snapshot)) {
|
auto Clang = BuildCompilerInstance(*session, std::move(CI), DC, snapshot, Bufs);
|
||||||
LOG_S(ERROR) << "Reparsing translation unit for diagnostics failed for "
|
if (!Clang)
|
||||||
<< path;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
if (!Parse(*Clang))
|
||||||
|
continue;
|
||||||
|
for (auto &Buf : Bufs)
|
||||||
|
Buf.release();
|
||||||
|
|
||||||
auto &LangOpts = tu->Unit->getLangOpts();
|
|
||||||
std::vector<lsDiagnostic> ls_diags;
|
std::vector<lsDiagnostic> ls_diags;
|
||||||
for (ASTUnit::stored_diag_iterator I = tu->Unit->stored_diag_begin(),
|
for (auto &d : DC.Take()) {
|
||||||
E = tu->Unit->stored_diag_end();
|
if (!d.inside_main)
|
||||||
I != E; ++I) {
|
|
||||||
FullSourceLoc FLoc = I->getLocation();
|
|
||||||
if (!FLoc.isValid()) // why?
|
|
||||||
continue;
|
continue;
|
||||||
const FileEntry *FE = FLoc.getFileEntry();
|
lsDiagnostic &ls_diag = ls_diags.emplace_back();
|
||||||
if (!FE || FileName(*FE) != path)
|
ls_diag.range = lsRange{{d.range.start.line, d.range.start.column},
|
||||||
continue;
|
{d.range.end.line, d.range.end.column}};
|
||||||
const auto &SM = FLoc.getManager();
|
ls_diag.message = d.message;
|
||||||
SourceRange R;
|
switch (d.level) {
|
||||||
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:
|
case DiagnosticsEngine::Ignored:
|
||||||
// llvm_unreachable
|
// llvm_unreachable
|
||||||
case DiagnosticsEngine::Note:
|
case DiagnosticsEngine::Note:
|
||||||
@ -576,16 +665,8 @@ void DiagnosticQueryMain(ClangCompleteManager *manager) {
|
|||||||
case DiagnosticsEngine::Fatal:
|
case DiagnosticsEngine::Fatal:
|
||||||
ls_diag.severity = lsDiagnosticSeverity::Error;
|
ls_diag.severity = lsDiagnosticSeverity::Error;
|
||||||
}
|
}
|
||||||
ls_diag.message = I->getMessage().str();
|
ls_diag.code = d.category;
|
||||||
for (const FixItHint &FixIt : I->getFixIts()) {
|
ls_diag.fixits_ = d.edits;
|
||||||
lsTextEdit edit;
|
|
||||||
edit.newText = FixIt.CodeToInsert;
|
|
||||||
r = FromCharSourceRange(SM, LangOpts, FixIt.RemoveRange);
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
manager->on_diagnostic_(path, ls_diags);
|
manager->on_diagnostic_(path, ls_diags);
|
||||||
}
|
}
|
||||||
@ -594,15 +675,15 @@ void DiagnosticQueryMain(ClangCompleteManager *manager) {
|
|||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
std::shared_ptr<PreambleData> CompletionSession::GetPreamble() {
|
std::shared_ptr<PreambleData> CompletionSession::GetPreamble() {
|
||||||
std::lock_guard<std::mutex> lock(completion.lock);
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
return completion.preamble;
|
return preamble;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CompletionSession::BuildPreamble(CompilerInvocation &CI) {
|
void CompletionSession::BuildPreamble(CompilerInvocation &CI) {
|
||||||
std::shared_ptr<PreambleData> OldP = GetPreamble();
|
std::shared_ptr<PreambleData> OldP = GetPreamble();
|
||||||
std::string contents = wfiles->GetContent(file.filename);
|
std::string content = wfiles->GetContent(file.filename);
|
||||||
std::unique_ptr<llvm::MemoryBuffer> Buf =
|
std::unique_ptr<llvm::MemoryBuffer> Buf =
|
||||||
llvm::MemoryBuffer::getMemBuffer(contents);
|
llvm::MemoryBuffer::getMemBuffer(content);
|
||||||
auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0);
|
auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0);
|
||||||
if (OldP && OldP->Preamble.CanReuse(CI, Buf.get(), Bounds, FS.get()))
|
if (OldP && OldP->Preamble.CanReuse(CI, Buf.get(), Bounds, FS.get()))
|
||||||
return;
|
return;
|
||||||
@ -612,19 +693,20 @@ void CompletionSession::BuildPreamble(CompilerInvocation &CI) {
|
|||||||
CI.getPreprocessorOpts().WriteCommentListToPCH = false;
|
CI.getPreprocessorOpts().WriteCommentListToPCH = false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
DiagnosticConsumer DC;
|
StoreDiags DC;
|
||||||
IntrusiveRefCntPtr<DiagnosticsEngine> PreambleDE =
|
IntrusiveRefCntPtr<DiagnosticsEngine> DE =
|
||||||
CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(),
|
CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false);
|
||||||
&DC, false);
|
|
||||||
PreambleCallbacks PP;
|
PreambleCallbacks PP;
|
||||||
if (auto NewPreamble = PrecompiledPreamble::Build(
|
if (auto NewPreamble = PrecompiledPreamble::Build(CI, Buf.get(), Bounds,
|
||||||
CI, Buf.get(), Bounds, *PreambleDE, FS, PCH, true, PP)) {
|
*DE, FS, PCH, true, PP)) {
|
||||||
std::lock_guard<std::mutex> lock(completion.lock);
|
std::lock_guard<std::mutex> lock(mutex);
|
||||||
completion.preamble =
|
preamble =
|
||||||
std::make_shared<PreambleData>(std::move(*NewPreamble));
|
std::make_shared<PreambleData>(std::move(*NewPreamble), DC.Take());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace ccls
|
||||||
|
|
||||||
ClangCompleteManager::ClangCompleteManager(Project *project,
|
ClangCompleteManager::ClangCompleteManager(Project *project,
|
||||||
WorkingFiles *working_files,
|
WorkingFiles *working_files,
|
||||||
OnDiagnostic on_diagnostic,
|
OnDiagnostic on_diagnostic,
|
||||||
@ -636,17 +718,17 @@ ClangCompleteManager::ClangCompleteManager(Project *project,
|
|||||||
PCH(std::make_shared<PCHContainerOperations>()) {
|
PCH(std::make_shared<PCHContainerOperations>()) {
|
||||||
std::thread([&]() {
|
std::thread([&]() {
|
||||||
set_thread_name("comp-query");
|
set_thread_name("comp-query");
|
||||||
CompletionQueryMain(this);
|
ccls::CompletionQueryMain(this);
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
std::thread([&]() {
|
std::thread([&]() {
|
||||||
set_thread_name("comp-preload");
|
set_thread_name("comp-preload");
|
||||||
CompletionPreloadMain(this);
|
ccls::CompletionPreloadMain(this);
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
std::thread([&]() {
|
std::thread([&]() {
|
||||||
set_thread_name("diag-query");
|
set_thread_name("diag-query");
|
||||||
DiagnosticQueryMain(this);
|
ccls::DiagnosticQueryMain(this);
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
@ -734,43 +816,42 @@ bool ClangCompleteManager::EnsureCompletionOrCreatePreloadSession(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// No CompletionSession, create new one.
|
// No CompletionSession, create new one.
|
||||||
auto session = std::make_shared<CompletionSession>(
|
auto session = std::make_shared<ccls::CompletionSession>(
|
||||||
project_->FindCompilationEntryForFile(filename), working_files_, PCH);
|
project_->FindCompilationEntryForFile(filename), working_files_, PCH);
|
||||||
preloaded_sessions_.Insert(session->file.filename, session);
|
preloaded_sessions_.Insert(session->file.filename, session);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<CompletionSession>
|
std::shared_ptr<ccls::CompletionSession>
|
||||||
ClangCompleteManager::TryGetSession(const std::string &filename,
|
ClangCompleteManager::TryGetSession(const std::string &filename,
|
||||||
bool mark_as_completion,
|
bool mark_as_completion,
|
||||||
bool create_if_needed) {
|
bool create_if_needed) {
|
||||||
std::lock_guard<std::mutex> lock(sessions_lock_);
|
std::lock_guard<std::mutex> lock(sessions_lock_);
|
||||||
|
|
||||||
// Try to find a preloaded session.
|
// Try to find a preloaded session.
|
||||||
std::shared_ptr<CompletionSession> preloaded_session =
|
std::shared_ptr<ccls::CompletionSession> preloaded =
|
||||||
preloaded_sessions_.TryGet(filename);
|
preloaded_sessions_.TryGet(filename);
|
||||||
|
|
||||||
if (preloaded_session) {
|
if (preloaded) {
|
||||||
// If this request is for a completion, we should move it to
|
// If this request is for a completion, we should move it to
|
||||||
// |completion_sessions|.
|
// |completion_sessions|.
|
||||||
if (mark_as_completion) {
|
if (mark_as_completion) {
|
||||||
preloaded_sessions_.TryTake(filename);
|
preloaded_sessions_.TryTake(filename);
|
||||||
completion_sessions_.Insert(filename, preloaded_session);
|
completion_sessions_.Insert(filename, preloaded);
|
||||||
}
|
}
|
||||||
|
return preloaded;
|
||||||
return preloaded_session;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to find a completion session. If none create one.
|
// Try to find a completion session. If none create one.
|
||||||
std::shared_ptr<CompletionSession> completion_session =
|
std::shared_ptr<ccls::CompletionSession> session =
|
||||||
completion_sessions_.TryGet(filename);
|
completion_sessions_.TryGet(filename);
|
||||||
if (!completion_session && create_if_needed) {
|
if (!session && create_if_needed) {
|
||||||
completion_session = std::make_shared<CompletionSession>(
|
session = std::make_shared<ccls::CompletionSession>(
|
||||||
project_->FindCompilationEntryForFile(filename), working_files_, PCH);
|
project_->FindCompilationEntryForFile(filename), working_files_, PCH);
|
||||||
completion_sessions_.Insert(filename, completion_session);
|
completion_sessions_.Insert(filename, session);
|
||||||
}
|
}
|
||||||
|
|
||||||
return completion_session;
|
return session;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ClangCompleteManager::FlushSession(const std::string &filename) {
|
void ClangCompleteManager::FlushSession(const std::string &filename) {
|
||||||
|
@ -19,27 +19,33 @@
|
|||||||
#include <mutex>
|
#include <mutex>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
namespace ccls {
|
||||||
|
struct DiagBase {
|
||||||
|
Range range;
|
||||||
|
std::string message;
|
||||||
|
std::string file;
|
||||||
|
clang::DiagnosticsEngine::Level level = clang::DiagnosticsEngine::Note;
|
||||||
|
unsigned category;
|
||||||
|
bool inside_main = false;
|
||||||
|
};
|
||||||
|
struct Note : DiagBase {};
|
||||||
|
struct Diag : DiagBase {
|
||||||
|
std::vector<Note> notes;
|
||||||
|
std::vector<lsTextEdit> edits;
|
||||||
|
};
|
||||||
|
|
||||||
struct PreambleData {
|
struct PreambleData {
|
||||||
PreambleData(clang::PrecompiledPreamble P) : Preamble(std::move(P)) {}
|
PreambleData(clang::PrecompiledPreamble P, std::vector<Diag> diags)
|
||||||
|
: Preamble(std::move(P)), diags(std::move(diags)) {}
|
||||||
clang::PrecompiledPreamble Preamble;
|
clang::PrecompiledPreamble Preamble;
|
||||||
|
std::vector<Diag> diags;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct CompletionSession
|
struct CompletionSession
|
||||||
: public std::enable_shared_from_this<CompletionSession> {
|
: public std::enable_shared_from_this<CompletionSession> {
|
||||||
// Translation unit for clang.
|
std::mutex mutex;
|
||||||
struct Tu {
|
std::shared_ptr<PreambleData> preamble;
|
||||||
// When |tu| was last parsed.
|
std::vector<Diag> diags;
|
||||||
std::optional<std::chrono::time_point<std::chrono::high_resolution_clock>>
|
|
||||||
last_parsed_at;
|
|
||||||
// Acquired when |tu| is being used.
|
|
||||||
std::mutex lock;
|
|
||||||
std::unique_ptr<ClangTranslationUnit> tu;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct CU {
|
|
||||||
std::mutex lock;
|
|
||||||
std::shared_ptr<PreambleData> preamble;
|
|
||||||
};
|
|
||||||
|
|
||||||
Project::Entry file;
|
Project::Entry file;
|
||||||
WorkingFiles *wfiles;
|
WorkingFiles *wfiles;
|
||||||
@ -49,9 +55,6 @@ struct CompletionSession
|
|||||||
clang::vfs::getRealFileSystem();
|
clang::vfs::getRealFileSystem();
|
||||||
std::shared_ptr<clang::PCHContainerOperations> PCH;
|
std::shared_ptr<clang::PCHContainerOperations> PCH;
|
||||||
|
|
||||||
CU completion;
|
|
||||||
Tu diagnostics;
|
|
||||||
|
|
||||||
CompletionSession(const Project::Entry &file, WorkingFiles *wfiles,
|
CompletionSession(const Project::Entry &file, WorkingFiles *wfiles,
|
||||||
std::shared_ptr<clang::PCHContainerOperations> PCH)
|
std::shared_ptr<clang::PCHContainerOperations> PCH)
|
||||||
: file(file), wfiles(wfiles), PCH(PCH) {}
|
: file(file), wfiles(wfiles), PCH(PCH) {}
|
||||||
@ -59,6 +62,7 @@ struct CompletionSession
|
|||||||
std::shared_ptr<PreambleData> GetPreamble();
|
std::shared_ptr<PreambleData> GetPreamble();
|
||||||
void BuildPreamble(clang::CompilerInvocation &CI);
|
void BuildPreamble(clang::CompilerInvocation &CI);
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
struct ClangCompleteManager {
|
struct ClangCompleteManager {
|
||||||
using OnDiagnostic = std::function<void(
|
using OnDiagnostic = std::function<void(
|
||||||
@ -118,9 +122,9 @@ struct ClangCompleteManager {
|
|||||||
bool EnsureCompletionOrCreatePreloadSession(const std::string &filename);
|
bool EnsureCompletionOrCreatePreloadSession(const std::string &filename);
|
||||||
// Tries to find an edit session for |filename|. This will move the session
|
// Tries to find an edit session for |filename|. This will move the session
|
||||||
// from view to edit.
|
// from view to edit.
|
||||||
std::shared_ptr<CompletionSession> TryGetSession(const std::string &filename,
|
std::shared_ptr<ccls::CompletionSession>
|
||||||
bool mark_as_completion,
|
TryGetSession(const std::string &filename, bool mark_as_completion,
|
||||||
bool create_if_needed);
|
bool create_if_needed);
|
||||||
|
|
||||||
// Flushes all saved sessions with the supplied filename
|
// Flushes all saved sessions with the supplied filename
|
||||||
void FlushSession(const std::string &filename);
|
void FlushSession(const std::string &filename);
|
||||||
@ -137,7 +141,7 @@ struct ClangCompleteManager {
|
|||||||
OnDiagnostic on_diagnostic_;
|
OnDiagnostic on_diagnostic_;
|
||||||
OnDropped on_dropped_;
|
OnDropped on_dropped_;
|
||||||
|
|
||||||
using LruSessionCache = LruCache<std::string, CompletionSession>;
|
using LruSessionCache = LruCache<std::string, ccls::CompletionSession>;
|
||||||
|
|
||||||
// CompletionSession instances which are preloaded, ie, files which the user
|
// CompletionSession instances which are preloaded, ie, files which the user
|
||||||
// has viewed but not requested code completion for.
|
// has viewed but not requested code completion for.
|
||||||
|
@ -4,12 +4,8 @@
|
|||||||
#include "clang_tu.h"
|
#include "clang_tu.h"
|
||||||
|
|
||||||
#include "clang_utils.h"
|
#include "clang_utils.h"
|
||||||
#include "log.hh"
|
|
||||||
#include "platform.h"
|
|
||||||
#include "utils.h"
|
|
||||||
#include "working_files.h"
|
|
||||||
|
|
||||||
#include <llvm/Support/CrashRecoveryContext.h>
|
#include <clang/Lex/Lexer.h>
|
||||||
using namespace clang;
|
using namespace clang;
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
@ -55,72 +51,3 @@ Range FromTokenRange(const SourceManager &SM, const LangOptions &LangOpts,
|
|||||||
return FromCharSourceRange(SM, LangOpts, CharSourceRange::getTokenRange(R),
|
return FromCharSourceRange(SM, LangOpts, CharSourceRange::getTokenRange(R),
|
||||||
UniqueID);
|
UniqueID);
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<ClangTranslationUnit> ClangTranslationUnit::Create(
|
|
||||||
const std::string &filepath, const std::vector<std::string> &args,
|
|
||||||
const WorkingFiles::Snapshot &snapshot, bool diagnostic) {
|
|
||||||
std::vector<const char *> Args;
|
|
||||||
for (auto &arg : args)
|
|
||||||
Args.push_back(arg.c_str());
|
|
||||||
Args.push_back("-fallow-editor-placeholders");
|
|
||||||
if (!diagnostic)
|
|
||||||
Args.push_back("-fno-spell-checking");
|
|
||||||
|
|
||||||
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=*/g_config->clang.resourceDir,
|
|
||||||
/*OnlyLocalDecls=*/false,
|
|
||||||
/*CaptureDiagnostics=*/diagnostic, Remapped,
|
|
||||||
/*RemappedFilesKeepOriginalName=*/true, 0,
|
|
||||||
diagnostic ? TU_Complete : TU_Prefix,
|
|
||||||
/*CacheCodeCompletionResults=*/true, g_config->index.comments,
|
|
||||||
/*AllowPCHWithCompilerErrors=*/true,
|
|
||||||
#if LLVM_VERSION_MAJOR >= 7
|
|
||||||
SkipFunctionBodiesScope::None,
|
|
||||||
#else
|
|
||||||
!diagnostic,
|
|
||||||
#endif
|
|
||||||
/*SingleFileParse=*/false,
|
|
||||||
/*UserFilesAreVolatile=*/true, false,
|
|
||||||
ret->PCHCO->getRawReader().getFormat(), &ErrUnit));
|
|
||||||
};
|
|
||||||
if (!CRC.RunSafely(parse)) {
|
|
||||||
LOG_S(ERROR) << "clang crashed for " << filepath << "\n"
|
|
||||||
<< StringJoin(args, " ") + " -fsyntax-only";
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
if (!Unit && !ErrUnit)
|
|
||||||
return {};
|
|
||||||
|
|
||||||
ret->Unit = std::move(Unit);
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
int ClangTranslationUnit::Reparse(llvm::CrashRecoveryContext &CRC,
|
|
||||||
const WorkingFiles::Snapshot &snapshot) {
|
|
||||||
int ret = 1;
|
|
||||||
(void)CRC.RunSafely(
|
|
||||||
[&]() { ret = Unit->Reparse(PCHCO, GetRemapped(snapshot)); });
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
@ -3,19 +3,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "position.h"
|
#include "position.h"
|
||||||
#include "working_files.h"
|
|
||||||
|
|
||||||
#include <clang/Frontend/ASTUnit.h>
|
#include <clang/Basic/SourceManager.h>
|
||||||
#include <clang/Frontend/CompilerInstance.h>
|
|
||||||
#include <llvm/Support/CrashRecoveryContext.h>
|
|
||||||
|
|
||||||
#include <memory>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
std::vector<clang::ASTUnit::RemappedFile>
|
|
||||||
GetRemapped(const WorkingFiles::Snapshot &snapshot);
|
|
||||||
|
|
||||||
Range FromCharSourceRange(const clang::SourceManager &SM,
|
Range FromCharSourceRange(const clang::SourceManager &SM,
|
||||||
const clang::LangOptions &LangOpts,
|
const clang::LangOptions &LangOpts,
|
||||||
@ -29,15 +20,3 @@ Range FromCharRange(const clang::SourceManager &SM,
|
|||||||
Range FromTokenRange(const clang::SourceManager &SM,
|
Range FromTokenRange(const clang::SourceManager &SM,
|
||||||
const clang::LangOptions &LangOpts, clang::SourceRange R,
|
const clang::LangOptions &LangOpts, clang::SourceRange R,
|
||||||
llvm::sys::fs::UniqueID *UniqueID = nullptr);
|
llvm::sys::fs::UniqueID *UniqueID = nullptr);
|
||||||
|
|
||||||
struct ClangTranslationUnit {
|
|
||||||
static std::unique_ptr<ClangTranslationUnit>
|
|
||||||
Create(const std::string &filepath, const std::vector<std::string> &args,
|
|
||||||
const WorkingFiles::Snapshot &snapshot, bool diagnostic);
|
|
||||||
|
|
||||||
int Reparse(llvm::CrashRecoveryContext &CRC,
|
|
||||||
const WorkingFiles::Snapshot &snapshot);
|
|
||||||
|
|
||||||
std::shared_ptr<clang::PCHContainerOperations> PCHCO;
|
|
||||||
std::unique_ptr<clang::ASTUnit> Unit;
|
|
||||||
};
|
|
||||||
|
Loading…
Reference in New Issue
Block a user