New diagnostics

This commit is contained in:
Fangrui Song 2018-08-28 22:49:53 -07:00
parent a66533b023
commit fea457616a
4 changed files with 238 additions and 247 deletions

View File

@ -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) {

View File

@ -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.

View File

@ -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;
}

View File

@ -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;
};