IgnoreDiagnostics::log(DiagLevel, Info);
}
+static bool AllowCrashPragmasForTest = false;
+void allowCrashPragmasForTest() { AllowCrashPragmasForTest = true; }
+
void disableUnsupportedOptions(CompilerInvocation &CI) {
// Disable "clang -verify" diagnostics, they are rarely useful in clangd, and
// our compiler invocation set-up doesn't seem to work with it (leading
CI.getPreprocessorOpts().PCHWithHdrStop = false;
CI.getPreprocessorOpts().PCHWithHdrStopCreate = false;
// Don't crash on `#pragma clang __debug parser_crash`
- CI.getPreprocessorOpts().DisablePragmaDebugCrash = true;
+ if (!AllowCrashPragmasForTest)
+ CI.getPreprocessorOpts().DisablePragmaDebugCrash = true;
// Always default to raw container format as clangd doesn't registry any other
// and clang dies when faced with unknown formats.
std::unique_ptr<llvm::MemoryBuffer> MainFile,
IntrusiveRefCntPtr<llvm::vfs::FileSystem>, DiagnosticConsumer &);
+/// Respect `#pragma clang __debug crash` etc, which are usually disabled.
+/// This may only be called before threads are spawned.
+void allowCrashPragmasForTest();
+
} // namespace clangd
} // namespace clang
void startTask(llvm::StringRef Name, llvm::unique_function<void()> Task,
llvm::Optional<UpdateType> Update,
TUScheduler::ASTActionInvalidation);
+ /// Runs a task synchronously.
+ void runTask(llvm::StringRef Name, llvm::function_ref<void()> Task);
/// Determines the next action to perform.
/// All actions that should never run are discarded.
generateDiagnostics(std::move(CI), std::move(PI), std::move(CIDiags));
};
if (RunSync) {
- Task();
+ runTask(TaskName, Task);
return;
}
{
RequestsCV.notify_all();
}
+void ASTWorker::runTask(llvm::StringRef Name, llvm::function_ref<void()> Task) {
+ ThreadCrashReporter ScopedReporter([this, Name]() {
+ llvm::errs() << "Signalled during AST worker action: " << Name << "\n";
+ crashDumpParseInputs(llvm::errs(), FileInputs);
+ });
+ trace::Span Tracer(Name);
+ WithContext WithProvidedContext(ContextProvider(FileName));
+ Task();
+}
+
void ASTWorker::startTask(llvm::StringRef Name,
llvm::unique_function<void()> Task,
llvm::Optional<UpdateType> Update,
TUScheduler::ASTActionInvalidation Invalidation) {
if (RunSync) {
assert(!Done && "running a task after stop()");
- trace::Span Tracer(Name + ":" + llvm::sys::path::filename(FileName));
- WithContext WithProvidedContext(ContextProvider(FileName));
- Task();
+ runTask(Name, Task);
return;
}
Lock.lock();
}
WithContext Guard(std::move(CurrentRequest->Ctx));
- trace::Span Tracer(CurrentRequest->Name);
Status.update([&](TUStatus &Status) {
Status.ASTActivity.K = ASTAction::RunningAction;
Status.ASTActivity.Name = CurrentRequest->Name;
});
- WithContext WithProvidedContext(ContextProvider(FileName));
- ThreadCrashReporter ScopedReporter([this]() {
- const char *Name = CurrentRequest ? CurrentRequest->Name.c_str() : "";
- llvm::errs() << "Signalled during AST worker action: " << Name << "\n";
- crashDumpParseInputs(llvm::errs(), FileInputs);
- });
- CurrentRequest->Action();
+ runTask(CurrentRequest->Name, CurrentRequest->Action);
}
bool IsEmpty = false;
--- /dev/null
+# RUN: not --crash clangd -lit-test < %s 2> %t.err
+# RUN: FileCheck %s < %t.err --check-prefixes=CHECK,CHECK-SYNC
+# RUN: not --crash clangd -lit-test -sync=0 < %s 2> %t.async.err
+# RUN: FileCheck %s < %t.async.err
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{}}
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{
+ "uri":"test:///foo.cc",
+ "languageId":"cpp",
+ "text":"int x;\n#pragma clang __debug llvm_fatal_error"
+}}}
+---
+{"jsonrpc":"2.0","id":1,"method":"sync","params":{}}
+# CHECK: Signalled during AST worker action: Build AST
+# CHECK-NEXT: Filename: foo.cc
+# CHECK-SYNC: Signalled during AST worker action: Update
+# CHECK-SYNC: Filename: foo.cc
+# CHECK-SYNC: Signalled while processing message:
+# CHECK-SYNC: "languageId":"cpp"
--- /dev/null
+# RUN: not --crash clangd -lit-test < %s 2> %t.err
+# RUN: FileCheck %s < %t.err --check-prefixes=CHECK,CHECK-SYNC
+# RUN: not --crash clangd -lit-test -sync=0 < %s 2> %t.async.err
+# RUN: FileCheck %s < %t.async.err
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{}}
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{
+ "uri":"test:///foo.cc",
+ "languageId":"cpp",
+ "text":"#pragma clang __debug llvm_fatal_error"
+}}}
+---
+{"jsonrpc":"2.0","id":1,"method":"sync","params":{}}
+# CHECK: Signalled while building preamble
+# CHECK-NEXT: Filename: foo.cc
+# CHECK-SYNC: Signalled during AST worker action: Update
+# CHECK-SYNC: Filename: foo.cc
+# CHECK-SYNC: Signalled while processing message:
+# CHECK-SYNC: "languageId":"cpp"
#include "ClangdLSPServer.h"
#include "CodeComplete.h"
+#include "Compiler.h"
#include "Config.h"
#include "ConfigProvider.h"
#include "Feature.h"
"lit-test",
cat(Misc),
desc("Abbreviation for -input-style=delimited -pretty -sync "
- "-enable-test-scheme -enable-config=0 -log=verbose. "
+ "-enable-test-scheme -enable-config=0 -log=verbose -crash-pragmas. "
"Intended to simplify lit tests"),
init(false),
Hidden,
};
+opt<bool> CrashPragmas{
+ "crash-pragmas",
+ cat(Misc),
+ desc("Respect `#pragma clang __debug crash` and friends."),
+ init(false),
+ Hidden,
+};
+
opt<Path> CheckFile{
"check",
cat(Misc),
llvm::cl::ParseCommandLineOptions(argc, argv, Overview,
/*Errs=*/nullptr, FlagsEnvVar);
if (Test) {
- Sync = true;
+ if (!Sync.getNumOccurrences())
+ Sync = true;
+ if (!CrashPragmas.getNumOccurrences())
+ CrashPragmas = true;
InputStyle = JSONStreamStyle::Delimited;
LogLevel = Logger::Verbose;
PrettyPrint = true;
static URISchemeRegistry::Add<TestScheme> X(
"test", "Test scheme for clangd lit tests.");
}
+ if (CrashPragmas)
+ allowCrashPragmasForTest();
if (!Sync && WorkerThreadsCount == 0) {
llvm::errs() << "A number of worker threads cannot be 0. Did you mean to "