Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -132,10 +132,17 @@ void ClangdLSPServer::onDocumentDidOpen(DidOpenTextDocumentParams &Params) { PathRef File = Params.textDocument.uri.file(); - if (Params.metadata && !Params.metadata->extraFlags.empty()) { - NonCachedCDB.setExtraFlagsForFile(File, - std::move(Params.metadata->extraFlags)); - CDB.invalidate(File); + if (Params.metadata) { + if (!Params.metadata->extraFlags.empty()) { + NonCachedCDB.setExtraFlagsForFile(File, + std::move(Params.metadata->extraFlags)); + CDB.invalidate(File); + } + if (!Params.metadata->compilationCommand.empty()) { + NonCachedCDB.overrideCompilationCommandForFile( + File, std::move(Params.metadata->compilationCommand)); + CDB.invalidate(File); + } } std::string &Contents = Params.textDocument.text; Index: clangd/GlobalCompilationDatabase.h =================================================================== --- clangd/GlobalCompilationDatabase.h +++ clangd/GlobalCompilationDatabase.h @@ -68,10 +68,16 @@ /// Sets the extra flags that should be added to a file. void setExtraFlagsForFile(PathRef File, std::vector<std::string> ExtraFlags); + /// Overrides the compilation command for a particular file. + void overrideCompilationCommandForFile( + PathRef File, std::vector<std::string> CompilationCommand); + private: tooling::CompilationDatabase *getCDBForFile(PathRef File) const; tooling::CompilationDatabase *getCDBInDirLocked(PathRef File) const; void addExtraFlags(PathRef File, tooling::CompileCommand &C) const; + llvm::Optional<tooling::CompileCommand> + getOverridenCompilationCommand(PathRef File) const; mutable std::mutex Mutex; /// Caches compilation databases loaded from directories(keys are @@ -81,6 +87,8 @@ /// Stores extra flags per file. llvm::StringMap<std::vector<std::string>> ExtraFlagsForFile; + /// Stores overriden compilation commands per file. + llvm::StringMap<std::vector<std::string>> OverridenCompilationCommands; /// Used for command argument pointing to folder where compile_commands.json /// is located. llvm::Optional<Path> CompileCommandsDir; Index: clangd/GlobalCompilationDatabase.cpp =================================================================== --- clangd/GlobalCompilationDatabase.cpp +++ clangd/GlobalCompilationDatabase.cpp @@ -40,6 +40,10 @@ llvm::Optional<tooling::CompileCommand> DirectoryBasedGlobalCompilationDatabase::getCompileCommand(PathRef File) const { + if (auto Command = getOverridenCompilationCommand(File)) { + addExtraFlags(File, *Command); + return std::move(*Command); + } if (auto CDB = getCDBForFile(File)) { auto Candidates = CDB->getCompileCommands(File); if (!Candidates.empty()) { @@ -87,6 +91,24 @@ Args.insert(Args.end() - 1, It->second.begin(), It->second.end()); } +void DirectoryBasedGlobalCompilationDatabase::overrideCompilationCommandForFile( + PathRef File, std::vector<std::string> CompilationCommand) { + std::lock_guard<std::mutex> Lock(Mutex); + OverridenCompilationCommands[File] = std::move(CompilationCommand); +} + +llvm::Optional<tooling::CompileCommand> +DirectoryBasedGlobalCompilationDatabase::getOverridenCompilationCommand( + PathRef File) const { + std::lock_guard<std::mutex> Lock(Mutex); + auto It = OverridenCompilationCommands.find(File); + if (It == OverridenCompilationCommands.end()) + return None; + return tooling::CompileCommand(llvm::sys::path::parent_path(File), + llvm::sys::path::filename(File), It->second, + /*Output=*/""); +} + tooling::CompilationDatabase * DirectoryBasedGlobalCompilationDatabase::getCDBInDirLocked(PathRef Dir) const { // FIXME(ibiryukov): Invalidate cached compilation databases on changes Index: clangd/Protocol.h =================================================================== --- clangd/Protocol.h +++ clangd/Protocol.h @@ -160,6 +160,7 @@ struct Metadata { std::vector<std::string> extraFlags; + std::vector<std::string> compilationCommand; }; bool fromJSON(const llvm::json::Value &, Metadata &); Index: clangd/Protocol.cpp =================================================================== --- clangd/Protocol.cpp +++ clangd/Protocol.cpp @@ -122,6 +122,7 @@ if (!O) return false; O.map("extraFlags", R.extraFlags); + O.map("compilationCommand", R.compilationCommand); return true; } Index: test/clangd/override-compile-command.test =================================================================== --- /dev/null +++ test/clangd/override-compile-command.test @@ -0,0 +1,76 @@ +# RUN: clangd -lit-test < %s | FileCheck -strict-whitespace %s +{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}} +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","version":1,"text":"int main() { int i; return i; }"},"metadata":{"compilationCommand":["clang", "-c", "foo.c", "-Wall", "-Werror"]}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "message": "variable 'i' is uninitialized when used here", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 28, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 27, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 1 +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c" +# CHECK-NEXT: } +--- +{"jsonrpc":"2.0","method":"textDocument/didChange","params":{"textDocument":{"uri":"test:///foo.c","version":2},"contentChanges":[{"text":"int main() { int i; return i; }"}]}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "message": "variable 'i' is uninitialized when used here", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 28, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 27, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 1 +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c" +# CHECK-NEXT: } + +# extraFlags works together with compilationCommand! +--- +{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///bar.c","languageId":"c","version":1,"text":"int main() { int i; return i; }"},"metadata":{"compilationCommand":["clang", "-c", "bar.c", "-Wall", "-Werror"], "extraFlags":["-Wno-error=uninitialized"]}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "message": "variable 'i' is uninitialized when used here", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 28, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 27, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 2 +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "uri": "file://{{.*}}/bar.c" +# CHECK-NEXT: } +--- +{"jsonrpc":"2.0","id":5,"method":"shutdown"} +--- +{"jsonrpc":"2.0":"method":"exit"} + +