2018-08-21 05:27:52 +00:00
|
|
|
/* Copyright 2017-2018 ccls Authors
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
==============================================================================*/
|
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
#include "sema_manager.hh"
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2018-09-23 20:31:06 +00:00
|
|
|
#include "clang_tu.hh"
|
2018-05-27 19:24:56 +00:00
|
|
|
#include "filesystem.hh"
|
|
|
|
#include "log.hh"
|
2018-12-14 04:13:35 +00:00
|
|
|
#include "pipeline.hh"
|
2018-10-29 04:21:21 +00:00
|
|
|
#include "platform.hh"
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2018-08-28 05:42:40 +00:00
|
|
|
#include <clang/Lex/PreprocessorOptions.h>
|
2018-07-14 17:00:04 +00:00
|
|
|
#include <clang/Sema/CodeCompleteConsumer.h>
|
2018-09-11 05:37:01 +00:00
|
|
|
#include <clang/Sema/Sema.h>
|
2018-05-27 19:24:56 +00:00
|
|
|
#include <llvm/ADT/Twine.h>
|
2018-07-15 07:57:48 +00:00
|
|
|
#include <llvm/Config/llvm-config.h>
|
2018-07-16 05:49:32 +00:00
|
|
|
#include <llvm/Support/CrashRecoveryContext.h>
|
2018-05-27 19:24:56 +00:00
|
|
|
#include <llvm/Support/Threading.h>
|
2018-07-14 17:00:04 +00:00
|
|
|
using namespace clang;
|
2018-05-13 20:30:24 +00:00
|
|
|
using namespace llvm;
|
|
|
|
|
2017-03-26 21:40:34 +00:00
|
|
|
#include <algorithm>
|
2018-09-22 08:37:00 +00:00
|
|
|
#include <chrono>
|
|
|
|
#include <ratio>
|
2017-04-17 01:22:59 +00:00
|
|
|
#include <thread>
|
2018-09-22 08:37:00 +00:00
|
|
|
namespace chrono = std::chrono;
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2018-10-03 00:34:02 +00:00
|
|
|
#if LLVM_VERSION_MAJOR < 8
|
|
|
|
namespace clang::vfs {
|
|
|
|
struct ProxyFileSystem : FileSystem {
|
|
|
|
explicit ProxyFileSystem(IntrusiveRefCntPtr<FileSystem> FS)
|
|
|
|
: FS(std::move(FS)) {}
|
|
|
|
llvm::ErrorOr<Status> status(const Twine &Path) override {
|
|
|
|
return FS->status(Path);
|
|
|
|
}
|
|
|
|
llvm::ErrorOr<std::unique_ptr<File>>
|
|
|
|
openFileForRead(const Twine &Path) override {
|
|
|
|
return FS->openFileForRead(Path);
|
|
|
|
}
|
|
|
|
directory_iterator dir_begin(const Twine &Dir, std::error_code &EC) override {
|
|
|
|
return FS->dir_begin(Dir, EC);
|
|
|
|
}
|
|
|
|
llvm::ErrorOr<std::string> getCurrentWorkingDirectory() const override {
|
|
|
|
return FS->getCurrentWorkingDirectory();
|
|
|
|
}
|
|
|
|
std::error_code setCurrentWorkingDirectory(const Twine &Path) override {
|
|
|
|
return FS->setCurrentWorkingDirectory(Path);
|
|
|
|
}
|
|
|
|
#if LLVM_VERSION_MAJOR == 7
|
|
|
|
std::error_code getRealPath(const Twine &Path,
|
|
|
|
SmallVectorImpl<char> &Output) const override {
|
|
|
|
return FS->getRealPath(Path, Output);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
FileSystem &getUnderlyingFS() { return *FS; }
|
|
|
|
IntrusiveRefCntPtr<FileSystem> FS;
|
|
|
|
};
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
namespace ccls {
|
2018-10-06 22:23:23 +00:00
|
|
|
|
2018-11-03 20:52:43 +00:00
|
|
|
TextEdit ToTextEdit(const clang::SourceManager &SM, const clang::LangOptions &L,
|
|
|
|
const clang::FixItHint &FixIt) {
|
|
|
|
TextEdit edit;
|
2018-10-06 22:23:23 +00:00
|
|
|
edit.newText = FixIt.CodeToInsert;
|
|
|
|
auto r = FromCharSourceRange(SM, L, FixIt.RemoveRange);
|
|
|
|
edit.range =
|
|
|
|
lsRange{{r.start.line, r.start.column}, {r.end.line, r.end.column}};
|
|
|
|
return edit;
|
|
|
|
}
|
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
using IncludeStructure = std::vector<std::pair<std::string, int64_t>>;
|
|
|
|
|
2018-10-03 00:34:02 +00:00
|
|
|
struct PreambleStatCache {
|
2018-10-10 16:52:41 +00:00
|
|
|
llvm::StringMap<ErrorOr<llvm::vfs::Status>> Cache;
|
2018-10-03 00:34:02 +00:00
|
|
|
|
2018-10-10 16:52:41 +00:00
|
|
|
void Update(Twine Path, ErrorOr<llvm::vfs::Status> S) {
|
2018-10-03 00:34:02 +00:00
|
|
|
Cache.try_emplace(Path.str(), std::move(S));
|
|
|
|
}
|
|
|
|
|
2018-10-10 16:52:41 +00:00
|
|
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
|
|
|
|
Producer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
|
|
|
|
struct VFS : llvm::vfs::ProxyFileSystem {
|
2018-10-03 00:34:02 +00:00
|
|
|
PreambleStatCache &Cache;
|
|
|
|
|
2018-10-10 16:52:41 +00:00
|
|
|
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
|
|
|
|
PreambleStatCache &Cache)
|
2018-10-03 00:34:02 +00:00
|
|
|
: ProxyFileSystem(std::move(FS)), Cache(Cache) {}
|
2018-10-10 16:52:41 +00:00
|
|
|
llvm::ErrorOr<std::unique_ptr<llvm::vfs::File>>
|
2018-10-03 00:34:02 +00:00
|
|
|
openFileForRead(const Twine &Path) override {
|
|
|
|
auto File = getUnderlyingFS().openFileForRead(Path);
|
|
|
|
if (!File || !*File)
|
|
|
|
return File;
|
|
|
|
Cache.Update(Path, File->get()->status());
|
|
|
|
return File;
|
|
|
|
}
|
2018-10-10 16:52:41 +00:00
|
|
|
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
|
2018-10-03 00:34:02 +00:00
|
|
|
auto S = getUnderlyingFS().status(Path);
|
|
|
|
Cache.Update(Path, S);
|
|
|
|
return S;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return new VFS(std::move(FS), *this);
|
|
|
|
}
|
|
|
|
|
2018-10-10 16:52:41 +00:00
|
|
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem>
|
|
|
|
Consumer(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS) {
|
|
|
|
struct VFS : llvm::vfs::ProxyFileSystem {
|
2018-10-03 00:34:02 +00:00
|
|
|
const PreambleStatCache &Cache;
|
2018-10-10 16:52:41 +00:00
|
|
|
VFS(IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
|
2018-10-03 00:34:02 +00:00
|
|
|
const PreambleStatCache &Cache)
|
|
|
|
: ProxyFileSystem(std::move(FS)), Cache(Cache) {}
|
2018-10-10 16:52:41 +00:00
|
|
|
llvm::ErrorOr<llvm::vfs::Status> status(const Twine &Path) override {
|
2018-10-03 00:34:02 +00:00
|
|
|
auto I = Cache.Cache.find(Path.str());
|
|
|
|
if (I != Cache.Cache.end())
|
|
|
|
return I->getValue();
|
|
|
|
return getUnderlyingFS().status(Path);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
return new VFS(std::move(FS), *this);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct PreambleData {
|
2018-12-01 06:44:52 +00:00
|
|
|
PreambleData(clang::PrecompiledPreamble P, IncludeStructure includes,
|
|
|
|
std::vector<Diag> diags,
|
2018-10-03 00:34:02 +00:00
|
|
|
std::unique_ptr<PreambleStatCache> stat_cache)
|
2018-12-01 06:44:52 +00:00
|
|
|
: Preamble(std::move(P)), includes(std::move(includes)),
|
|
|
|
diags(std::move(diags)), stat_cache(std::move(stat_cache)) {}
|
2018-10-03 00:34:02 +00:00
|
|
|
clang::PrecompiledPreamble Preamble;
|
2018-12-01 06:44:52 +00:00
|
|
|
IncludeStructure includes;
|
2018-10-03 00:34:02 +00:00
|
|
|
std::vector<Diag> diags;
|
|
|
|
std::unique_ptr<PreambleStatCache> stat_cache;
|
|
|
|
};
|
|
|
|
|
2017-03-26 21:40:34 +00:00
|
|
|
namespace {
|
2018-08-29 05:49:53 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
class StoreInclude : public PPCallbacks {
|
|
|
|
const SourceManager &SM;
|
|
|
|
IncludeStructure &out;
|
|
|
|
DenseSet<const FileEntry *> Seen;
|
2018-08-29 05:49:53 +00:00
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
public:
|
|
|
|
StoreInclude(const SourceManager &SM, IncludeStructure &out)
|
|
|
|
: SM(SM), out(out) {}
|
|
|
|
void InclusionDirective(SourceLocation HashLoc, const Token &IncludeTok,
|
|
|
|
StringRef FileName, bool IsAngled,
|
|
|
|
CharSourceRange FilenameRange, const FileEntry *File,
|
|
|
|
StringRef SearchPath, StringRef RelativePath,
|
2019-01-21 02:44:44 +00:00
|
|
|
const clang::Module *Imported,
|
|
|
|
SrcMgr::CharacteristicKind FileKind) override {
|
2018-12-01 06:44:52 +00:00
|
|
|
(void)SM;
|
|
|
|
if (File && Seen.insert(File).second)
|
|
|
|
out.emplace_back(PathFromFileEntry(*File), File->getModificationTime());
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class CclsPreambleCallbacks : public PreambleCallbacks {
|
|
|
|
public:
|
|
|
|
void BeforeExecute(CompilerInstance &CI) override {
|
|
|
|
SM = &CI.getSourceManager();
|
|
|
|
}
|
|
|
|
std::unique_ptr<PPCallbacks> createPPCallbacks() override {
|
|
|
|
return std::make_unique<StoreInclude>(*SM, includes);
|
|
|
|
}
|
|
|
|
SourceManager *SM = nullptr;
|
|
|
|
IncludeStructure includes;
|
|
|
|
};
|
2018-07-14 23:02:59 +00:00
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
class StoreDiags : public DiagnosticConsumer {
|
|
|
|
const LangOptions *LangOpts;
|
|
|
|
std::optional<Diag> last;
|
|
|
|
std::vector<Diag> output;
|
2018-09-22 08:37:00 +00:00
|
|
|
std::string path;
|
|
|
|
std::unordered_map<unsigned, bool> FID2concerned;
|
2018-08-29 05:49:53 +00:00
|
|
|
void Flush() {
|
|
|
|
if (!last)
|
|
|
|
return;
|
2018-09-22 08:37:00 +00:00
|
|
|
bool mentions = last->concerned || last->edits.size();
|
2018-08-29 05:49:53 +00:00
|
|
|
if (!mentions)
|
|
|
|
for (auto &N : last->notes)
|
2018-09-22 08:37:00 +00:00
|
|
|
if (N.concerned)
|
2018-08-29 05:49:53 +00:00
|
|
|
mentions = true;
|
|
|
|
if (mentions)
|
|
|
|
output.push_back(std::move(*last));
|
|
|
|
last.reset();
|
|
|
|
}
|
|
|
|
public:
|
2018-09-22 08:37:00 +00:00
|
|
|
StoreDiags(std::string path) : path(path) {}
|
2018-08-29 05:49:53 +00:00
|
|
|
std::vector<Diag> Take() {
|
|
|
|
return std::move(output);
|
|
|
|
}
|
2018-09-22 08:37:00 +00:00
|
|
|
bool IsConcerned(const SourceManager &SM, SourceLocation L) {
|
|
|
|
FileID FID = SM.getFileID(L);
|
|
|
|
auto it = FID2concerned.try_emplace(FID.getHashValue());
|
|
|
|
if (it.second) {
|
|
|
|
const FileEntry *FE = SM.getFileEntryForID(FID);
|
2018-09-23 20:31:06 +00:00
|
|
|
it.first->second = FE && PathFromFileEntry(*FE) == path;
|
2018-09-22 08:37:00 +00:00
|
|
|
}
|
|
|
|
return it.first->second;
|
|
|
|
}
|
2018-08-29 05:49:53 +00:00
|
|
|
void BeginSourceFile(const LangOptions &Opts, const Preprocessor *) override {
|
|
|
|
LangOpts = &Opts;
|
|
|
|
}
|
|
|
|
void EndSourceFile() override {
|
|
|
|
Flush();
|
|
|
|
}
|
|
|
|
void HandleDiagnostic(DiagnosticsEngine::Level Level,
|
2018-11-03 20:52:43 +00:00
|
|
|
const clang::Diagnostic &Info) override {
|
2018-08-29 05:49:53 +00:00
|
|
|
DiagnosticConsumer::HandleDiagnostic(Level, Info);
|
|
|
|
SourceLocation L = Info.getLocation();
|
|
|
|
if (!L.isValid()) return;
|
|
|
|
const SourceManager &SM = Info.getSourceManager();
|
2018-09-22 08:37:00 +00:00
|
|
|
StringRef Filename = SM.getFilename(Info.getLocation());
|
2018-11-10 23:05:21 +00:00
|
|
|
bool concerned = SM.isWrittenInMainFile(L);
|
2018-08-29 05:49:53 +00:00
|
|
|
auto fillDiagBase = [&](DiagBase &d) {
|
|
|
|
llvm::SmallString<64> Message;
|
|
|
|
Info.FormatDiagnostic(Message);
|
|
|
|
d.range =
|
|
|
|
FromCharSourceRange(SM, *LangOpts, DiagnosticRange(Info, *LangOpts));
|
|
|
|
d.message = Message.str();
|
2018-09-22 08:37:00 +00:00
|
|
|
d.concerned = concerned;
|
|
|
|
d.file = Filename;
|
2018-08-29 05:49:53 +00:00
|
|
|
d.level = Level;
|
|
|
|
d.category = DiagnosticIDs::getCategoryNumberForDiag(Info.getID());
|
|
|
|
};
|
|
|
|
|
|
|
|
auto addFix = [&](bool SyntheticMessage) -> bool {
|
2018-09-22 08:37:00 +00:00
|
|
|
if (!concerned)
|
2018-08-29 05:49:53 +00:00
|
|
|
return false;
|
|
|
|
for (const FixItHint &FixIt : Info.getFixItHints()) {
|
2018-09-22 08:37:00 +00:00
|
|
|
if (!IsConcerned(SM, FixIt.RemoveRange.getBegin()))
|
2018-08-29 05:49:53 +00:00
|
|
|
return false;
|
2018-10-06 22:23:23 +00:00
|
|
|
last->edits.push_back(ToTextEdit(SM, *LangOpts, FixIt));
|
2018-08-29 05:49:53 +00:00
|
|
|
}
|
|
|
|
return true;
|
|
|
|
};
|
2017-05-11 06:25:41 +00:00
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
if (Level == DiagnosticsEngine::Note || Level == DiagnosticsEngine::Remark) {
|
|
|
|
if (Info.getFixItHints().size()) {
|
|
|
|
addFix(false);
|
|
|
|
} else {
|
|
|
|
Note &n = last->notes.emplace_back();
|
|
|
|
fillDiagBase(n);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
Flush();
|
|
|
|
last = Diag();
|
|
|
|
fillDiagBase(*last);
|
|
|
|
if (!Info.getFixItHints().empty())
|
|
|
|
addFix(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
2017-05-11 06:25:41 +00:00
|
|
|
|
2018-08-30 07:29:08 +00:00
|
|
|
std::unique_ptr<CompilerInstance> BuildCompilerInstance(
|
2018-12-01 06:44:52 +00:00
|
|
|
Session &session, std::unique_ptr<CompilerInvocation> CI,
|
2018-10-10 16:52:41 +00:00
|
|
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS, DiagnosticConsumer &DC,
|
2018-12-21 04:53:50 +00:00
|
|
|
const PreambleData *preamble, const std::string &main,
|
2018-11-09 05:18:41 +00:00
|
|
|
std::unique_ptr<llvm::MemoryBuffer> &Buf) {
|
2019-01-21 02:44:44 +00:00
|
|
|
if (preamble)
|
2018-11-09 05:18:41 +00:00
|
|
|
preamble->Preamble.OverridePreamble(*CI, FS, Buf.get());
|
2019-01-21 02:44:44 +00:00
|
|
|
else
|
2018-12-21 04:53:50 +00:00
|
|
|
CI->getPreprocessorOpts().addRemappedFile(main, Buf.get());
|
2018-08-29 05:49:53 +00:00
|
|
|
|
|
|
|
auto Clang = std::make_unique<CompilerInstance>(session.PCH);
|
|
|
|
Clang->setInvocation(std::move(CI));
|
|
|
|
Clang->createDiagnostics(&DC, false);
|
|
|
|
Clang->setTarget(TargetInfo::CreateTargetInfo(
|
|
|
|
Clang->getDiagnostics(), Clang->getInvocation().TargetOpts));
|
|
|
|
if (!Clang->hasTarget())
|
|
|
|
return nullptr;
|
2019-03-29 14:48:58 +00:00
|
|
|
Clang->getPreprocessorOpts().RetainRemappedFileBuffers = true;
|
2018-09-28 20:41:50 +00:00
|
|
|
// Construct SourceManager with UserFilesAreVolatile: true because otherwise
|
|
|
|
// RequiresNullTerminator: true may cause out-of-bounds read when a file is
|
|
|
|
// mmap'ed but is saved concurrently.
|
2019-03-27 01:42:48 +00:00
|
|
|
#if LLVM_VERSION_MAJOR >= 9 // rC357037
|
|
|
|
Clang->createFileManager(FS);
|
|
|
|
#else
|
|
|
|
Clang->setVirtualFileSystem(FS);
|
2018-09-28 20:41:50 +00:00
|
|
|
Clang->createFileManager();
|
2019-03-27 01:42:48 +00:00
|
|
|
#endif
|
2018-09-28 20:41:50 +00:00
|
|
|
Clang->setSourceManager(new SourceManager(Clang->getDiagnostics(),
|
|
|
|
Clang->getFileManager(), true));
|
2018-12-21 04:53:50 +00:00
|
|
|
auto &IS = Clang->getFrontendOpts().Inputs;
|
|
|
|
if (IS.size()) {
|
|
|
|
assert(IS[0].isFile());
|
|
|
|
IS[0] = FrontendInputFile(main, IS[0].getKind(), IS[0].isSystem());
|
|
|
|
}
|
2018-08-29 05:49:53 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
void BuildPreamble(Session &session, CompilerInvocation &CI,
|
2018-10-10 16:52:41 +00:00
|
|
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS,
|
2018-12-01 06:44:52 +00:00
|
|
|
const SemaManager::PreambleTask &task,
|
2018-10-03 00:34:02 +00:00
|
|
|
std::unique_ptr<PreambleStatCache> stat_cache) {
|
|
|
|
std::shared_ptr<PreambleData> OldP = session.GetPreamble();
|
2018-12-01 06:44:52 +00:00
|
|
|
std::string content = session.wfiles->GetContent(task.path);
|
2018-10-03 00:34:02 +00:00
|
|
|
std::unique_ptr<llvm::MemoryBuffer> Buf =
|
|
|
|
llvm::MemoryBuffer::getMemBuffer(content);
|
|
|
|
auto Bounds = ComputePreambleBounds(*CI.getLangOpts(), Buf.get(), 0);
|
2018-12-01 06:44:52 +00:00
|
|
|
if (!task.from_diag && OldP &&
|
|
|
|
OldP->Preamble.CanReuse(CI, Buf.get(), Bounds, FS.get()))
|
2018-10-03 00:34:02 +00:00
|
|
|
return;
|
2018-11-10 23:05:21 +00:00
|
|
|
// -Werror makes warnings issued as errors, which stops parsing
|
|
|
|
// prematurely because of -ferror-limit=. This also works around the issue
|
|
|
|
// of -Werror + -Wunused-parameter in interaction with SkipFunctionBodies.
|
|
|
|
auto &Ws = CI.getDiagnosticOpts().Warnings;
|
|
|
|
Ws.erase(std::remove(Ws.begin(), Ws.end(), "error"), Ws.end());
|
2018-10-03 00:34:02 +00:00
|
|
|
CI.getDiagnosticOpts().IgnoreWarnings = false;
|
|
|
|
CI.getFrontendOpts().SkipFunctionBodies = true;
|
2018-10-12 23:36:07 +00:00
|
|
|
CI.getLangOpts()->CommentOpts.ParseAllComments = g_config->index.comments > 1;
|
2019-04-12 05:26:07 +00:00
|
|
|
CI.getLangOpts()->RetainCommentsFromSystemHeaders = true;
|
2018-10-03 00:34:02 +00:00
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
StoreDiags DC(task.path);
|
2018-10-03 00:34:02 +00:00
|
|
|
IntrusiveRefCntPtr<DiagnosticsEngine> DE =
|
|
|
|
CompilerInstance::createDiagnostics(&CI.getDiagnosticOpts(), &DC, false);
|
2018-12-01 06:44:52 +00:00
|
|
|
if (OldP) {
|
|
|
|
std::lock_guard lock(session.wfiles->mutex);
|
|
|
|
for (auto &include : OldP->includes)
|
|
|
|
if (WorkingFile *wf = session.wfiles->GetFileUnlocked(include.first))
|
|
|
|
CI.getPreprocessorOpts().addRemappedFile(
|
|
|
|
include.first,
|
|
|
|
llvm::MemoryBuffer::getMemBufferCopy(wf->buffer_content).release());
|
|
|
|
}
|
|
|
|
|
|
|
|
CclsPreambleCallbacks PC;
|
2018-10-03 00:34:02 +00:00
|
|
|
if (auto NewPreamble = PrecompiledPreamble::Build(
|
2018-12-01 06:44:52 +00:00
|
|
|
CI, Buf.get(), Bounds, *DE, FS, session.PCH, true, PC)) {
|
|
|
|
assert(!CI.getPreprocessorOpts().RetainRemappedFileBuffers);
|
|
|
|
if (OldP) {
|
|
|
|
auto &old_includes = OldP->includes;
|
|
|
|
auto it = old_includes.begin();
|
|
|
|
std::sort(PC.includes.begin(), PC.includes.end());
|
|
|
|
for (auto &include : PC.includes)
|
|
|
|
if (include.second == 0) {
|
|
|
|
while (it != old_includes.end() && it->first < include.first)
|
|
|
|
++it;
|
|
|
|
if (it == old_includes.end())
|
|
|
|
break;
|
|
|
|
include.second = it->second;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-10-03 00:34:02 +00:00
|
|
|
std::lock_guard lock(session.mutex);
|
|
|
|
session.preamble = std::make_shared<PreambleData>(
|
2018-12-01 06:44:52 +00:00
|
|
|
std::move(*NewPreamble), std::move(PC.includes), DC.Take(),
|
|
|
|
std::move(stat_cache));
|
2018-10-03 00:34:02 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
void *PreambleMain(void *manager_) {
|
|
|
|
auto *manager = static_cast<SemaManager *>(manager_);
|
|
|
|
set_thread_name("preamble");
|
2017-05-11 06:25:41 +00:00
|
|
|
while (true) {
|
2018-12-01 06:44:52 +00:00
|
|
|
SemaManager::PreambleTask task = manager->preamble_tasks.Dequeue();
|
2018-12-14 04:13:35 +00:00
|
|
|
if (pipeline::quit.load(std::memory_order_relaxed))
|
|
|
|
break;
|
2017-06-10 01:02:48 +00:00
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
bool created = false;
|
|
|
|
std::shared_ptr<Session> session =
|
|
|
|
manager->EnsureSession(task.path, &created);
|
2017-05-11 06:25:41 +00:00
|
|
|
|
2018-10-03 00:34:02 +00:00
|
|
|
auto stat_cache = std::make_unique<PreambleStatCache>();
|
2018-10-10 16:52:41 +00:00
|
|
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
|
|
|
|
stat_cache->Producer(session->FS);
|
2018-10-01 05:54:48 +00:00
|
|
|
if (std::unique_ptr<CompilerInvocation> CI =
|
2018-12-24 05:22:51 +00:00
|
|
|
BuildCompilerInvocation(task.path, session->file.args, FS))
|
2018-12-01 06:44:52 +00:00
|
|
|
BuildPreamble(*session, *CI, FS, task, std::move(stat_cache));
|
|
|
|
|
2019-01-13 10:33:18 +00:00
|
|
|
if (task.comp_task) {
|
|
|
|
manager->comp_tasks.PushBack(std::move(task.comp_task));
|
|
|
|
} else if (task.from_diag) {
|
2018-12-01 06:44:52 +00:00
|
|
|
manager->ScheduleDiag(task.path, 0);
|
|
|
|
} else {
|
|
|
|
int debounce =
|
|
|
|
created ? g_config->diagnostics.onOpen : g_config->diagnostics.onSave;
|
|
|
|
if (debounce >= 0)
|
|
|
|
manager->ScheduleDiag(task.path, debounce);
|
2018-09-11 23:42:04 +00:00
|
|
|
}
|
2017-05-11 06:25:41 +00:00
|
|
|
}
|
2018-12-14 04:13:35 +00:00
|
|
|
pipeline::ThreadLeave();
|
2018-10-02 16:34:55 +00:00
|
|
|
return nullptr;
|
2017-05-11 06:25:41 +00:00
|
|
|
}
|
|
|
|
|
2018-10-02 16:34:55 +00:00
|
|
|
void *CompletionMain(void *manager_) {
|
2018-12-01 06:44:52 +00:00
|
|
|
auto *manager = static_cast<SemaManager *>(manager_);
|
2018-10-02 16:34:55 +00:00
|
|
|
set_thread_name("comp");
|
2017-04-17 01:22:59 +00:00
|
|
|
while (true) {
|
2018-12-01 06:44:52 +00:00
|
|
|
std::unique_ptr<SemaManager::CompTask> task = manager->comp_tasks.Dequeue();
|
2018-12-14 04:13:35 +00:00
|
|
|
if (pipeline::quit.load(std::memory_order_relaxed))
|
|
|
|
break;
|
2018-02-22 07:13:42 +00:00
|
|
|
|
|
|
|
// Drop older requests if we're not buffering.
|
2018-04-04 06:05:41 +00:00
|
|
|
while (g_config->completion.dropOldRequests &&
|
2018-12-01 06:44:52 +00:00
|
|
|
!manager->comp_tasks.IsEmpty()) {
|
|
|
|
manager->on_dropped_(task->id);
|
|
|
|
task->Consumer.reset();
|
|
|
|
task->on_complete(nullptr);
|
|
|
|
task = manager->comp_tasks.Dequeue();
|
2018-12-14 04:13:35 +00:00
|
|
|
if (pipeline::quit.load(std::memory_order_relaxed))
|
|
|
|
break;
|
2018-02-22 07:13:42 +00:00
|
|
|
}
|
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
std::shared_ptr<Session> session = manager->EnsureSession(task->path);
|
2018-10-03 00:34:02 +00:00
|
|
|
std::shared_ptr<PreambleData> preamble = session->GetPreamble();
|
2018-10-10 16:52:41 +00:00
|
|
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
|
2018-10-03 00:34:02 +00:00
|
|
|
preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS;
|
2018-08-28 05:42:40 +00:00
|
|
|
std::unique_ptr<CompilerInvocation> CI =
|
2018-12-24 05:22:51 +00:00
|
|
|
BuildCompilerInvocation(task->path, session->file.args, FS);
|
2018-08-28 05:42:40 +00:00
|
|
|
if (!CI)
|
|
|
|
continue;
|
|
|
|
auto &FOpts = CI->getFrontendOpts();
|
2018-12-01 06:44:52 +00:00
|
|
|
FOpts.CodeCompleteOpts = task->CCOpts;
|
|
|
|
FOpts.CodeCompletionAt.FileName = task->path;
|
|
|
|
FOpts.CodeCompletionAt.Line = task->position.line + 1;
|
|
|
|
FOpts.CodeCompletionAt.Column = task->position.character + 1;
|
2018-08-30 07:29:08 +00:00
|
|
|
FOpts.SkipFunctionBodies = true;
|
|
|
|
CI->getLangOpts()->CommentOpts.ParseAllComments = true;
|
2017-10-23 05:07:50 +00:00
|
|
|
|
2018-09-07 21:38:08 +00:00
|
|
|
DiagnosticConsumer DC;
|
2018-12-01 06:44:52 +00:00
|
|
|
std::string content = manager->wfiles->GetContent(task->path);
|
2018-11-09 05:18:41 +00:00
|
|
|
auto Buf = llvm::MemoryBuffer::getMemBuffer(content);
|
2019-01-13 10:33:18 +00:00
|
|
|
PreambleBounds Bounds =
|
|
|
|
ComputePreambleBounds(*CI->getLangOpts(), Buf.get(), 0);
|
2018-11-09 05:18:41 +00:00
|
|
|
bool in_preamble =
|
2018-12-01 06:44:52 +00:00
|
|
|
GetOffsetForPosition({task->position.line, task->position.character},
|
2019-01-13 10:33:18 +00:00
|
|
|
content) < (int)Bounds.Size;
|
|
|
|
if (in_preamble) {
|
2018-11-09 05:18:41 +00:00
|
|
|
preamble.reset();
|
2019-01-13 10:33:18 +00:00
|
|
|
} else if (preamble && Bounds.Size != preamble->Preamble.getBounds().Size) {
|
|
|
|
manager->preamble_tasks.PushBack({task->path, std::move(task), false},
|
|
|
|
true);
|
|
|
|
continue;
|
|
|
|
}
|
2018-10-03 00:34:02 +00:00
|
|
|
auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC,
|
2018-12-01 06:44:52 +00:00
|
|
|
preamble.get(), task->path, Buf);
|
2018-08-29 05:49:53 +00:00
|
|
|
if (!Clang)
|
|
|
|
continue;
|
2018-08-28 05:42:40 +00:00
|
|
|
|
2018-11-09 05:18:41 +00:00
|
|
|
Clang->getPreprocessorOpts().SingleFileParseMode = in_preamble;
|
2018-12-01 06:44:52 +00:00
|
|
|
Clang->setCodeCompletionConsumer(task->Consumer.release());
|
2018-08-29 05:49:53 +00:00
|
|
|
if (!Parse(*Clang))
|
2018-08-28 05:42:40 +00:00
|
|
|
continue;
|
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
task->on_complete(&Clang->getCodeCompletionConsumer());
|
2017-04-17 01:22:59 +00:00
|
|
|
}
|
2018-12-14 04:13:35 +00:00
|
|
|
pipeline::ThreadLeave();
|
2018-10-02 16:34:55 +00:00
|
|
|
return nullptr;
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
|
2018-09-13 06:07:47 +00:00
|
|
|
llvm::StringRef diagLeveltoString(DiagnosticsEngine::Level Lvl) {
|
|
|
|
switch (Lvl) {
|
|
|
|
case DiagnosticsEngine::Ignored:
|
|
|
|
return "ignored";
|
|
|
|
case DiagnosticsEngine::Note:
|
|
|
|
return "note";
|
|
|
|
case DiagnosticsEngine::Remark:
|
|
|
|
return "remark";
|
|
|
|
case DiagnosticsEngine::Warning:
|
|
|
|
return "warning";
|
|
|
|
case DiagnosticsEngine::Error:
|
|
|
|
return "error";
|
|
|
|
case DiagnosticsEngine::Fatal:
|
|
|
|
return "fatal error";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-02-21 01:23:21 +00:00
|
|
|
void PrintDiag(llvm::raw_string_ostream &OS, const DiagBase &d) {
|
2018-09-22 08:37:00 +00:00
|
|
|
if (d.concerned)
|
2018-09-13 06:07:47 +00:00
|
|
|
OS << llvm::sys::path::filename(d.file);
|
|
|
|
else
|
|
|
|
OS << d.file;
|
|
|
|
auto pos = d.range.start;
|
|
|
|
OS << ":" << (pos.line + 1) << ":" << (pos.column + 1) << ":"
|
2018-09-22 08:37:00 +00:00
|
|
|
<< (d.concerned ? " " : "\n");
|
2018-09-13 06:07:47 +00:00
|
|
|
OS << diagLeveltoString(d.level) << ": " << d.message;
|
|
|
|
}
|
|
|
|
|
2018-10-02 16:34:55 +00:00
|
|
|
void *DiagnosticMain(void *manager_) {
|
2018-12-01 06:44:52 +00:00
|
|
|
auto *manager = static_cast<SemaManager *>(manager_);
|
2018-10-02 16:34:55 +00:00
|
|
|
set_thread_name("diag");
|
2018-04-14 16:52:17 +00:00
|
|
|
while (true) {
|
2018-12-01 06:44:52 +00:00
|
|
|
SemaManager::DiagTask task = manager->diag_tasks.Dequeue();
|
2018-12-14 04:13:35 +00:00
|
|
|
if (pipeline::quit.load(std::memory_order_relaxed))
|
|
|
|
break;
|
2018-12-01 06:44:52 +00:00
|
|
|
int64_t wait = task.wait_until -
|
2018-09-22 08:37:00 +00:00
|
|
|
chrono::duration_cast<chrono::milliseconds>(
|
|
|
|
chrono::high_resolution_clock::now().time_since_epoch())
|
|
|
|
.count();
|
|
|
|
if (wait > 0)
|
2018-12-01 06:44:52 +00:00
|
|
|
std::this_thread::sleep_for(
|
|
|
|
chrono::duration<int64_t, std::milli>(std::min(wait, task.debounce)));
|
2017-04-17 01:22:59 +00:00
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
std::shared_ptr<Session> session = manager->EnsureSession(task.path);
|
2018-10-03 00:34:02 +00:00
|
|
|
std::shared_ptr<PreambleData> preamble = session->GetPreamble();
|
2018-10-10 16:52:41 +00:00
|
|
|
IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS =
|
2018-10-03 00:34:02 +00:00
|
|
|
preamble ? preamble->stat_cache->Consumer(session->FS) : session->FS;
|
2018-12-01 06:44:52 +00:00
|
|
|
if (preamble) {
|
|
|
|
bool rebuild = false;
|
|
|
|
{
|
|
|
|
std::lock_guard lock(manager->wfiles->mutex);
|
|
|
|
for (auto &include : preamble->includes)
|
|
|
|
if (WorkingFile *wf = manager->wfiles->GetFileUnlocked(include.first);
|
|
|
|
wf && include.second < wf->timestamp) {
|
|
|
|
include.second = wf->timestamp;
|
|
|
|
rebuild = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (rebuild) {
|
2019-01-13 10:33:18 +00:00
|
|
|
manager->preamble_tasks.PushBack({task.path, nullptr, true}, true);
|
2018-12-01 06:44:52 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
2018-11-09 05:18:41 +00:00
|
|
|
|
2018-08-29 05:49:53 +00:00
|
|
|
std::unique_ptr<CompilerInvocation> CI =
|
2018-12-24 05:22:51 +00:00
|
|
|
BuildCompilerInvocation(task.path, session->file.args, FS);
|
2018-08-29 05:49:53 +00:00
|
|
|
if (!CI)
|
2018-04-14 16:52:17 +00:00
|
|
|
continue;
|
2018-11-23 04:07:39 +00:00
|
|
|
// If main file is a header, add -Wno-unused-function
|
|
|
|
if (lookupExtension(session->file.filename).second)
|
|
|
|
CI->getDiagnosticOpts().Warnings.push_back("no-unused-function");
|
2018-09-08 17:37:48 +00:00
|
|
|
CI->getDiagnosticOpts().IgnoreWarnings = false;
|
|
|
|
CI->getLangOpts()->SpellChecking = g_config->diagnostics.spellChecking;
|
2018-12-01 06:44:52 +00:00
|
|
|
StoreDiags DC(task.path);
|
|
|
|
std::string content = manager->wfiles->GetContent(task.path);
|
2018-11-09 05:18:41 +00:00
|
|
|
auto Buf = llvm::MemoryBuffer::getMemBuffer(content);
|
2018-10-03 00:34:02 +00:00
|
|
|
auto Clang = BuildCompilerInstance(*session, std::move(CI), FS, DC,
|
2018-12-01 06:44:52 +00:00
|
|
|
preamble.get(), task.path, Buf);
|
2018-08-29 05:49:53 +00:00
|
|
|
if (!Clang)
|
2018-04-14 16:52:17 +00:00
|
|
|
continue;
|
2018-08-29 05:49:53 +00:00
|
|
|
if (!Parse(*Clang))
|
|
|
|
continue;
|
2018-04-14 16:52:17 +00:00
|
|
|
|
2018-11-03 20:52:43 +00:00
|
|
|
auto Fill = [](const DiagBase &d, Diagnostic &ret) {
|
2018-09-13 06:07:47 +00:00
|
|
|
ret.range = lsRange{{d.range.start.line, d.range.start.column},
|
|
|
|
{d.range.end.line, d.range.end.column}};
|
2018-08-29 05:49:53 +00:00
|
|
|
switch (d.level) {
|
2018-07-14 23:02:59 +00:00
|
|
|
case DiagnosticsEngine::Ignored:
|
|
|
|
// llvm_unreachable
|
|
|
|
case DiagnosticsEngine::Remark:
|
2018-11-04 18:30:18 +00:00
|
|
|
ret.severity = 4;
|
2018-09-13 06:07:47 +00:00
|
|
|
break;
|
|
|
|
case DiagnosticsEngine::Note:
|
2018-11-04 18:30:18 +00:00
|
|
|
ret.severity = 3;
|
2018-09-13 06:07:47 +00:00
|
|
|
break;
|
2018-07-14 23:02:59 +00:00
|
|
|
case DiagnosticsEngine::Warning:
|
2018-11-04 18:30:18 +00:00
|
|
|
ret.severity = 2;
|
2018-07-14 23:02:59 +00:00
|
|
|
break;
|
|
|
|
case DiagnosticsEngine::Error:
|
|
|
|
case DiagnosticsEngine::Fatal:
|
2018-11-04 18:30:18 +00:00
|
|
|
ret.severity = 1;
|
2018-09-13 06:07:47 +00:00
|
|
|
break;
|
2018-07-14 23:02:59 +00:00
|
|
|
}
|
2018-09-13 06:07:47 +00:00
|
|
|
ret.code = d.category;
|
|
|
|
return ret;
|
|
|
|
};
|
|
|
|
|
2018-09-22 08:37:00 +00:00
|
|
|
std::vector<Diag> diags = DC.Take();
|
|
|
|
if (std::shared_ptr<PreambleData> preamble = session->GetPreamble())
|
|
|
|
diags.insert(diags.end(), preamble->diags.begin(), preamble->diags.end());
|
2018-11-03 20:52:43 +00:00
|
|
|
std::vector<Diagnostic> ls_diags;
|
2018-09-22 08:37:00 +00:00
|
|
|
for (auto &d : diags) {
|
|
|
|
if (!d.concerned)
|
2018-09-13 06:07:47 +00:00
|
|
|
continue;
|
2018-11-03 20:52:43 +00:00
|
|
|
Diagnostic &ls_diag = ls_diags.emplace_back();
|
2018-09-13 06:07:47 +00:00
|
|
|
Fill(d, ls_diag);
|
2018-08-29 05:49:53 +00:00
|
|
|
ls_diag.fixits_ = d.edits;
|
2019-02-21 01:23:21 +00:00
|
|
|
if (g_config->client.diagnosticsRelatedInformation) {
|
|
|
|
ls_diag.message = d.message;
|
|
|
|
for (const Note &n : d.notes) {
|
|
|
|
SmallString<256> Str(n.file);
|
|
|
|
llvm::sys::path::remove_dots(Str, true);
|
|
|
|
Location loc{DocumentUri::FromPath(Str.str()),
|
|
|
|
lsRange{{n.range.start.line, n.range.start.column},
|
|
|
|
{n.range.end.line, n.range.end.column}}};
|
|
|
|
ls_diag.relatedInformation.push_back({loc, n.message});
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
std::string buf;
|
|
|
|
llvm::raw_string_ostream OS(buf);
|
|
|
|
OS << d.message;
|
|
|
|
for (const Note &n : d.notes) {
|
|
|
|
OS << "\n\n";
|
|
|
|
PrintDiag(OS, n);
|
|
|
|
}
|
2018-09-13 06:07:47 +00:00
|
|
|
OS.flush();
|
2019-02-21 01:23:21 +00:00
|
|
|
ls_diag.message = std::move(buf);
|
|
|
|
for (const Note &n : d.notes) {
|
|
|
|
if (!n.concerned)
|
|
|
|
continue;
|
|
|
|
Diagnostic &ls_diag1 = ls_diags.emplace_back();
|
|
|
|
Fill(n, ls_diag1);
|
|
|
|
buf.clear();
|
|
|
|
OS << n.message << "\n\n";
|
|
|
|
PrintDiag(OS, d);
|
|
|
|
OS.flush();
|
|
|
|
ls_diag1.message = std::move(buf);
|
|
|
|
}
|
2018-09-13 06:07:47 +00:00
|
|
|
}
|
2018-04-14 16:52:17 +00:00
|
|
|
}
|
2018-09-22 08:37:00 +00:00
|
|
|
|
|
|
|
{
|
2018-12-01 06:44:52 +00:00
|
|
|
std::lock_guard lock(manager->wfiles->mutex);
|
|
|
|
if (WorkingFile *wf = manager->wfiles->GetFileUnlocked(task.path))
|
|
|
|
wf->diagnostics = ls_diags;
|
2018-09-22 08:37:00 +00:00
|
|
|
}
|
2018-12-01 06:44:52 +00:00
|
|
|
manager->on_diagnostic_(task.path, ls_diags);
|
2018-04-14 16:52:17 +00:00
|
|
|
}
|
2018-12-14 04:13:35 +00:00
|
|
|
pipeline::ThreadLeave();
|
2018-10-02 16:34:55 +00:00
|
|
|
return nullptr;
|
2018-04-14 16:52:17 +00:00
|
|
|
}
|
|
|
|
|
2018-08-09 17:08:14 +00:00
|
|
|
} // namespace
|
2018-02-18 17:15:39 +00:00
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
std::shared_ptr<PreambleData> Session::GetPreamble() {
|
2018-08-29 05:49:53 +00:00
|
|
|
std::lock_guard<std::mutex> lock(mutex);
|
|
|
|
return preamble;
|
2018-08-28 05:42:40 +00:00
|
|
|
}
|
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
SemaManager::SemaManager(Project *project, WorkingFiles *wfiles,
|
|
|
|
OnDiagnostic on_diagnostic, OnDropped on_dropped)
|
|
|
|
: project_(project), wfiles(wfiles), on_diagnostic_(on_diagnostic),
|
|
|
|
on_dropped_(on_dropped), PCH(std::make_shared<PCHContainerOperations>()) {
|
|
|
|
SpawnThread(ccls::PreambleMain, this);
|
2018-10-02 16:34:55 +00:00
|
|
|
SpawnThread(ccls::CompletionMain, this);
|
|
|
|
SpawnThread(ccls::DiagnosticMain, this);
|
2017-04-17 01:22:59 +00:00
|
|
|
}
|
2017-03-26 21:40:34 +00:00
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
void SemaManager::ScheduleDiag(const std::string &path, int debounce) {
|
2018-09-22 08:37:00 +00:00
|
|
|
static GroupMatch match(g_config->diagnostics.whitelist,
|
|
|
|
g_config->diagnostics.blacklist);
|
2018-11-27 06:22:27 +00:00
|
|
|
if (!match.Matches(path))
|
2018-09-22 08:37:00 +00:00
|
|
|
return;
|
|
|
|
int64_t now = chrono::duration_cast<chrono::milliseconds>(
|
|
|
|
chrono::high_resolution_clock::now().time_since_epoch())
|
|
|
|
.count();
|
|
|
|
bool flag = false;
|
|
|
|
{
|
|
|
|
std::lock_guard lock(diag_mutex);
|
|
|
|
int64_t &next = next_diag[path];
|
|
|
|
auto &d = g_config->diagnostics;
|
|
|
|
if (next <= now ||
|
|
|
|
now - next > std::max(d.onChange, std::max(d.onChange, d.onSave))) {
|
|
|
|
next = now + debounce;
|
|
|
|
flag = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (flag)
|
2018-12-01 06:44:52 +00:00
|
|
|
diag_tasks.PushBack({path, now + debounce, debounce}, false);
|
2017-03-26 21:40:34 +00:00
|
|
|
}
|
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
void SemaManager::OnView(const std::string &path) {
|
|
|
|
std::lock_guard lock(mutex);
|
|
|
|
if (!sessions.Get(path))
|
|
|
|
preamble_tasks.PushBack(PreambleTask{path}, true);
|
2017-05-26 06:40:38 +00:00
|
|
|
}
|
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
void SemaManager::OnSave(const std::string &path) {
|
|
|
|
preamble_tasks.PushBack(PreambleTask{path}, true);
|
2017-05-26 06:40:38 +00:00
|
|
|
}
|
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
void SemaManager::OnClose(const std::string &path) {
|
|
|
|
std::lock_guard lock(mutex);
|
|
|
|
sessions.Take(path);
|
2017-10-17 18:43:33 +00:00
|
|
|
}
|
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
std::shared_ptr<ccls::Session>
|
|
|
|
SemaManager::EnsureSession(const std::string &path, bool *created) {
|
|
|
|
std::lock_guard lock(mutex);
|
|
|
|
std::shared_ptr<ccls::Session> session = sessions.Get(path);
|
|
|
|
if (!session) {
|
|
|
|
session = std::make_shared<ccls::Session>(
|
2018-12-24 05:22:51 +00:00
|
|
|
project_->FindEntry(path, false, false), wfiles, PCH);
|
2018-12-22 02:23:35 +00:00
|
|
|
std::string line;
|
|
|
|
if (LOG_V_ENABLED(1)) {
|
|
|
|
line = "\n ";
|
|
|
|
for (auto &arg : session->file.args)
|
|
|
|
(line += ' ') += arg;
|
|
|
|
}
|
|
|
|
LOG_S(INFO) << "create session for " << path << line;
|
2018-12-01 06:44:52 +00:00
|
|
|
sessions.Insert(path, session);
|
|
|
|
if (created)
|
|
|
|
*created = true;
|
2017-05-26 06:40:38 +00:00
|
|
|
}
|
2018-08-29 05:49:53 +00:00
|
|
|
return session;
|
2017-06-10 01:02:48 +00:00
|
|
|
}
|
2018-03-20 12:33:02 +00:00
|
|
|
|
2018-12-01 06:44:52 +00:00
|
|
|
void SemaManager::Clear() {
|
|
|
|
LOG_S(INFO) << "clear all sessions";
|
|
|
|
std::lock_guard lock(mutex);
|
2018-09-22 08:37:00 +00:00
|
|
|
sessions.Clear();
|
2018-03-20 12:33:02 +00:00
|
|
|
}
|
2018-12-14 04:13:35 +00:00
|
|
|
|
|
|
|
void SemaManager::Quit() {
|
|
|
|
comp_tasks.PushBack(nullptr);
|
|
|
|
diag_tasks.PushBack({});
|
|
|
|
preamble_tasks.PushBack({});
|
|
|
|
}
|
2018-10-28 17:49:31 +00:00
|
|
|
} // namespace ccls
|