Index: clangd/ClangdLSPServer.h =================================================================== --- clangd/ClangdLSPServer.h +++ clangd/ClangdLSPServer.h @@ -37,7 +37,8 @@ /// for compile_commands.json in all parent directories of each file. /// If UseDirBasedCDB is false, compile commands are not read from disk. // FIXME: Clean up signature around CDBs. - ClangdLSPServer(Transport &Transp, const clangd::CodeCompleteOptions &CCOpts, + ClangdLSPServer(Transport &Transp, const FileSystemProvider &FSProvider, + const clangd::CodeCompleteOptions &CCOpts, llvm::Optional CompileCommandsDir, bool UseDirBasedCDB, const ClangdServer::Options &Opts); ~ClangdLSPServer(); @@ -128,7 +129,7 @@ void call(StringRef Method, llvm::json::Value Params); void notify(StringRef Method, llvm::json::Value Params); - RealFileSystemProvider FSProvider; + const FileSystemProvider &FSProvider; /// Options used for code completion clangd::CodeCompleteOptions CCOpts; /// Options used for diagnostics. Index: clangd/ClangdLSPServer.cpp =================================================================== --- clangd/ClangdLSPServer.cpp +++ clangd/ClangdLSPServer.cpp @@ -719,11 +719,13 @@ } ClangdLSPServer::ClangdLSPServer(class Transport &Transp, + const FileSystemProvider &FSProvider, const clangd::CodeCompleteOptions &CCOpts, llvm::Optional CompileCommandsDir, bool UseDirBasedCDB, const ClangdServer::Options &Opts) - : Transp(Transp), MsgHandler(new MessageHandler(*this)), CCOpts(CCOpts), + : Transp(Transp), MsgHandler(new MessageHandler(*this)), + FSProvider(FSProvider), CCOpts(CCOpts), SupportedSymbolKinds(defaultSymbolKinds()), SupportedCompletionItemKinds(defaultCompletionItemKinds()), UseDirBasedCDB(UseDirBasedCDB), Index: clangd/ClangdServer.h =================================================================== --- clangd/ClangdServer.h +++ clangd/ClangdServer.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDSERVER_H +#include "../clang-tidy/ClangTidyOptions.h" #include "Cancellation.h" #include "ClangdUnit.h" #include "CodeComplete.h" @@ -92,6 +93,13 @@ /// If set, use this index to augment code completion results. SymbolIndex *StaticIndex = nullptr; + /// If set, enable clang-tidy in clangd, used to get clang-tidy + /// configurations for a particular file. + /// Clangd supports only a small subset of ClangTidyOptions, these options + /// (Checks, CheckOptions) are about which clang-tidy checks will be + /// enabled. + tidy::ClangTidyOptionsProvider *ClangTidyOptProvider = nullptr; + /// Clangd's workspace root. Relevant for "workspace" operations not bound /// to a particular file. /// FIXME: If not set, should use the current working directory. @@ -257,6 +265,9 @@ // Storage for merged views of the various indexes. std::vector> MergedIdx; + // The provider used to provide a clang-tidy option for a specific file. + tidy::ClangTidyOptionsProvider *ClangTidyOptProvider = nullptr; + // GUARDED_BY(CachedCompletionFuzzyFindRequestMutex) llvm::StringMap> CachedCompletionFuzzyFindRequestByFile; Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -112,6 +112,7 @@ DynamicIdx(Opts.BuildDynamicSymbolIndex ? new FileIndex(Opts.HeavyweightDynamicSymbolIndex) : nullptr), + ClangTidyOptProvider(Opts.ClangTidyOptProvider), WorkspaceRoot(Opts.WorkspaceRoot), PCHs(std::make_shared()), // Pass a callback into `WorkScheduler` to extract symbols from a newly @@ -147,13 +148,16 @@ void ClangdServer::addDocument(PathRef File, llvm::StringRef Contents, WantDiagnostics WantDiags) { + tidy::ClangTidyOptions Options = tidy::ClangTidyOptions::getDefaults(); + if (ClangTidyOptProvider) + Options = ClangTidyOptProvider->getOptions(File); // FIXME: some build systems like Bazel will take time to preparing // environment to build the file, it would be nice if we could emit a // "PreparingBuild" status to inform users, it is non-trivial given the // current implementation. - WorkScheduler.update(File, - ParseInputs{getCompileCommand(File), - FSProvider.getFileSystem(), Contents.str()}, + WorkScheduler.update(File, ParseInputs{getCompileCommand(File), + FSProvider.getFileSystem(), + Contents.str(), Options}, WantDiags); } Index: clangd/ClangdUnit.h =================================================================== --- clangd/ClangdUnit.h +++ clangd/ClangdUnit.h @@ -9,6 +9,7 @@ #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H #define LLVM_CLANG_TOOLS_EXTRA_CLANGD_CLANGDUNIT_H +#include "../clang-tidy/ClangTidyOptions.h" #include "Diagnostics.h" #include "FS.h" #include "Function.h" @@ -64,6 +65,7 @@ tooling::CompileCommand CompileCommand; IntrusiveRefCntPtr FS; std::string Contents; + tidy::ClangTidyOptions ClangTidyOpts; }; /// Stores and provides access to parsed AST. @@ -76,7 +78,8 @@ std::shared_ptr Preamble, std::unique_ptr Buffer, std::shared_ptr PCHs, - IntrusiveRefCntPtr VFS); + IntrusiveRefCntPtr VFS, + const tidy::ClangTidyOptions &ClangTidyOpts); ParsedAST(ParsedAST &&Other); ParsedAST &operator=(ParsedAST &&Other); Index: clangd/ClangdUnit.cpp =================================================================== --- clangd/ClangdUnit.cpp +++ clangd/ClangdUnit.cpp @@ -132,6 +132,9 @@ CompilerInstance &Clang) { auto &PP = Clang.getPreprocessor(); auto *ExistingCallbacks = PP.getPPCallbacks(); + // No need to replay events if nobody is listening. + if (!ExistingCallbacks) + return; PP.addPPCallbacks(std::unique_ptr( new ReplayPreamble(Includes, ExistingCallbacks, Clang.getSourceManager(), PP, Clang.getLangOpts()))); @@ -227,7 +230,8 @@ std::shared_ptr Preamble, std::unique_ptr Buffer, std::shared_ptr PCHs, - llvm::IntrusiveRefCntPtr VFS) { + llvm::IntrusiveRefCntPtr VFS, + const tidy::ClangTidyOptions &ClangTidyOpts) { assert(CI); // Command-line parsing sets DisableFree to true by default, but we don't want // to leak memory in clangd. @@ -264,15 +268,8 @@ tidy::ClangTidyCheckFactories CTFactories; for (const auto &E : tidy::ClangTidyModuleRegistry::entries()) E.instantiate()->addCheckFactories(CTFactories); - auto CTOpts = tidy::ClangTidyOptions::getDefaults(); - // FIXME: this needs to be configurable, and we need to support .clang-tidy - // files and other options providers. - // These checks exercise the matcher- and preprocessor-based hooks. - CTOpts.Checks = "bugprone-sizeof-expression," - "bugprone-macro-repeated-side-effects," - "modernize-deprecated-headers"; CTContext.emplace(llvm::make_unique( - tidy::ClangTidyGlobalOptions(), CTOpts)); + tidy::ClangTidyGlobalOptions(), ClangTidyOpts)); CTContext->setDiagnosticsEngine(&Clang->getDiagnostics()); CTContext->setASTContext(&Clang->getASTContext()); CTContext->setCurrentFile(MainInput.getFile()); @@ -538,7 +535,7 @@ return ParsedAST::build(llvm::make_unique(*Invocation), Preamble, llvm::MemoryBuffer::getMemBufferCopy(Inputs.Contents), - PCHs, std::move(VFS)); + PCHs, std::move(VFS), Inputs.ClangTidyOpts); } SourceLocation getBeginningOfIdentifier(ParsedAST &Unit, const Position &Pos, Index: clangd/tool/ClangdMain.cpp =================================================================== --- clangd/tool/ClangdMain.cpp +++ clangd/tool/ClangdMain.cpp @@ -201,6 +201,12 @@ "placeholders for method parameters."), llvm::cl::init(CodeCompleteOptions().EnableFunctionArgSnippets)); +static llvm::cl::opt ClangTidyChecks( + "clang-tidy-checks", + llvm::cl::desc("List of clang-tidy checks to run (this will overrides " + ".clang-tidy files)"), + llvm::cl::init(""), llvm::cl::Hidden); + namespace { /// \brief Supports a test URI scheme with relaxed constraints for lit tests. @@ -408,6 +414,7 @@ CCOpts.EnableFunctionArgSnippets = EnableFunctionArgSnippets; CCOpts.AllScopes = AllScopesCompletion; + RealFileSystemProvider FSProvider; // Initialize and run ClangdLSPServer. // Change stdin to binary to not lose \r\n on windows. llvm::sys::ChangeStdinToBinary(); @@ -427,8 +434,16 @@ PrettyPrint, InputStyle); } + // Create an empty clang-tidy option. + auto OverrideClangTidyOptions = tidy::ClangTidyOptions::getDefaults(); + OverrideClangTidyOptions.Checks = ClangTidyChecks; + tidy::FileOptionsProvider ClangTidyOptProvider( + tidy::ClangTidyGlobalOptions(), + /* Default */ tidy::ClangTidyOptions::getDefaults(), + /* Override */ OverrideClangTidyOptions, FSProvider.getFileSystem()); + Opts.ClangTidyOptProvider = &ClangTidyOptProvider; ClangdLSPServer LSPServer( - *TransportLayer, CCOpts, CompileCommandsDirPath, + *TransportLayer, FSProvider, CCOpts, CompileCommandsDirPath, /*UseDirBasedCDB=*/CompileArgsFrom == FilesystemCompileArgs, Opts); llvm::set_thread_name("clangd.main"); return LSPServer.run() ? 0 Index: unittests/clangd/ClangdUnitTests.cpp =================================================================== --- unittests/clangd/ClangdUnitTests.cpp +++ unittests/clangd/ClangdUnitTests.cpp @@ -141,6 +141,9 @@ )cpp"); auto TU = TestTU::withCode(Test.code()); TU.HeaderFilename = "assert.h"; // Suppress "not found" error. + TU.ClangTidyChecks = + "-*, bugprone-sizeof-expression, bugprone-macro-repeated-side-effects, " + "modernize-deprecated-headers"; EXPECT_THAT( TU.build().getDiagnostics(), UnorderedElementsAre( Index: unittests/clangd/FileIndexTests.cpp =================================================================== --- unittests/clangd/FileIndexTests.cpp +++ unittests/clangd/FileIndexTests.cpp @@ -363,7 +363,8 @@ auto AST = ParsedAST::build(createInvocationFromCommandLine(Cmd), PreambleData, llvm::MemoryBuffer::getMemBufferCopy(Main.code()), - std::make_shared(), PI.FS); + std::make_shared(), PI.FS, + tidy::ClangTidyOptions::getDefaults()); ASSERT_TRUE(AST); FileIndex Index; Index.updateMain(MainFile, *AST); Index: unittests/clangd/TUSchedulerTests.cpp =================================================================== --- unittests/clangd/TUSchedulerTests.cpp +++ unittests/clangd/TUSchedulerTests.cpp @@ -38,7 +38,8 @@ protected: ParseInputs getInputs(PathRef File, std::string Contents) { return ParseInputs{*CDB.getCompileCommand(File), - buildTestFS(Files, Timestamps), std::move(Contents)}; + buildTestFS(Files, Timestamps), std::move(Contents), + tidy::ClangTidyOptions::getDefaults()}; } void updateWithCallback(TUScheduler &S, PathRef File, Index: unittests/clangd/TestTU.h =================================================================== --- unittests/clangd/TestTU.h +++ unittests/clangd/TestTU.h @@ -48,6 +48,8 @@ // Extra arguments for the compiler invocation. std::vector ExtraArgs; + llvm::Optional ClangTidyChecks; + ParsedAST build() const; SymbolSlab headerSymbols() const; std::unique_ptr index() const; Index: unittests/clangd/TestTU.cpp =================================================================== --- unittests/clangd/TestTU.cpp +++ unittests/clangd/TestTU.cpp @@ -35,6 +35,8 @@ Inputs.CompileCommand.Directory = testRoot(); Inputs.Contents = Code; Inputs.FS = buildTestFS({{FullFilename, Code}, {FullHeaderName, HeaderCode}}); + Inputs.ClangTidyOpts = tidy::ClangTidyOptions::getDefaults(); + Inputs.ClangTidyOpts.Checks = ClangTidyChecks; auto PCHs = std::make_shared(); auto CI = buildCompilerInvocation(Inputs); assert(CI && "Failed to build compilation invocation.");