Index: clangd/ClangdLSPServer.h =================================================================== --- clangd/ClangdLSPServer.h +++ clangd/ClangdLSPServer.h @@ -38,7 +38,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(); @@ -123,7 +124,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 @@ -705,11 +705,13 @@ } ClangdLSPServer::ClangdLSPServer(class Transport &Transp, + const FileSystemProvider &FSProvider, const clangd::CodeCompleteOptions &CCOpts, 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 @@ -10,6 +10,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" @@ -86,6 +87,9 @@ /// If set, use this index to augment code completion results. SymbolIndex *StaticIndex = nullptr; + /// If set, enable clang-tidy in clangd. + 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. @@ -251,6 +255,9 @@ // Storage for merged views of the various indexes. std::vector> MergedIdx; + // The provider used to provide a clang-tidy options for a specific file. + tidy::ClangTidyOptionsProvider *ClangTidyOptProvider = nullptr; + // GUARDED_BY(CachedCompletionFuzzyFindRequestMutex) llvm::StringMap> CachedCompletionFuzzyFindRequestByFile; Index: clangd/ClangdServer.cpp =================================================================== --- clangd/ClangdServer.cpp +++ clangd/ClangdServer.cpp @@ -110,6 +110,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 @@ -144,9 +145,12 @@ void ClangdServer::addDocument(PathRef File, StringRef Contents, WantDiagnostics WantDiags) { - WorkScheduler.update(File, - ParseInputs{getCompileCommand(File), - FSProvider.getFileSystem(), Contents.str()}, + tidy::ClangTidyOptions Options = tidy::ClangTidyOptions::getDefaults(); + if (ClangTidyOptProvider) + Options = ClangTidyOptProvider->getOptions(File); + WorkScheduler.update(File, ParseInputs{getCompileCommand(File), + FSProvider.getFileSystem(), + Contents.str(), Options}, WantDiags); } Index: clangd/ClangdUnit.h =================================================================== --- clangd/ClangdUnit.h +++ clangd/ClangdUnit.h @@ -10,6 +10,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" @@ -65,6 +66,7 @@ tooling::CompileCommand CompileCommand; IntrusiveRefCntPtr FS; std::string Contents; + tidy::ClangTidyOptions ClangTidyOpts; }; /// Stores and provides access to parsed AST. @@ -77,7 +79,8 @@ std::shared_ptr Preamble, std::unique_ptr Buffer, std::shared_ptr PCHs, - IntrusiveRefCntPtr VFS); + IntrusiveRefCntPtr VFS, + tidy::ClangTidyOptions ClangTidyOpts); ParsedAST(ParsedAST &&Other); ParsedAST &operator=(ParsedAST &&Other); Index: clangd/ClangdUnit.cpp =================================================================== --- clangd/ClangdUnit.cpp +++ clangd/ClangdUnit.cpp @@ -134,6 +134,8 @@ CompilerInstance &Clang) { auto &PP = Clang.getPreprocessor(); auto *ExistingCallbacks = PP.getPPCallbacks(); + if (!ExistingCallbacks) + return; PP.addPPCallbacks(std::unique_ptr( new ReplayPreamble(Includes, ExistingCallbacks, Clang.getSourceManager(), PP, Clang.getLangOpts()))); @@ -229,7 +231,8 @@ std::shared_ptr Preamble, std::unique_ptr Buffer, std::shared_ptr PCHs, - IntrusiveRefCntPtr VFS) { + IntrusiveRefCntPtr VFS, + tidy::ClangTidyOptions ClangTidyOpts) { assert(CI); // Command-line parsing sets DisableFree to true by default, but we don't want // to leak memory in clangd. @@ -266,15 +269,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()); @@ -537,9 +533,10 @@ // dirs. } - return ParsedAST::build( - llvm::make_unique(*Invocation), Preamble, - MemoryBuffer::getMemBufferCopy(Inputs.Contents), PCHs, std::move(VFS)); + return ParsedAST::build(llvm::make_unique(*Invocation), + Preamble, + MemoryBuffer::getMemBufferCopy(Inputs.Contents), 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 @@ -188,6 +188,11 @@ "placeholders for method parameters."), cl::init(clangd::CodeCompleteOptions().EnableFunctionArgSnippets)); +static cl::opt + ClangTidyChecks("clang-tidy-checks", + cl::desc("A list of clang-tidy checks running in clangd"), + cl::init(""), cl::Hidden); + namespace { /// \brief Supports a test URI scheme with relaxed constraints for lit tests. @@ -234,7 +239,6 @@ #else const char TestScheme::TestDir[] = "/clangd-test"; #endif - } int main(int argc, char *argv[]) { @@ -388,8 +392,19 @@ stdin, outs(), InputMirrorStream ? InputMirrorStream.getPointer() : nullptr, PrettyPrint, InputStyle); + RealFileSystemProvider FSProvider; + std::unique_ptr ClangTidyOptProvider; + if (!ClangTidyChecks.empty()) { + // Create an empty option. + auto ClangTidyOptions = tidy::ClangTidyOptions::getDefaults(); + ClangTidyOptions.Checks = ClangTidyChecks; + ClangTidyOptProvider = llvm::make_unique( + tidy::ClangTidyGlobalOptions(), tidy::ClangTidyOptions::getDefaults(), + ClangTidyOptions, FSProvider.getFileSystem()); + } + Opts.ClangTidyOptProvider = ClangTidyOptProvider.get(); ClangdLSPServer LSPServer( - *Transport, CCOpts, CompileCommandsDirPath, + *Transport, FSProvider, CCOpts, CompileCommandsDirPath, /*UseDirBasedCDB=*/CompileArgsFrom == FilesystemCompileArgs, Opts); constexpr int NoShutdownRequestErrorCode = 1; set_thread_name("clangd.main"); Index: unittests/clangd/FileIndexTests.cpp =================================================================== --- unittests/clangd/FileIndexTests.cpp +++ unittests/clangd/FileIndexTests.cpp @@ -364,7 +364,8 @@ auto AST = ParsedAST::build(createInvocationFromCommandLine(Cmd), PreambleData, 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 @@ -34,7 +34,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, StringRef Contents, Index: unittests/clangd/TestTU.cpp =================================================================== --- unittests/clangd/TestTU.cpp +++ unittests/clangd/TestTU.cpp @@ -37,6 +37,10 @@ Inputs.CompileCommand.Directory = testRoot(); Inputs.Contents = Code; Inputs.FS = buildTestFS({{FullFilename, Code}, {FullHeaderName, HeaderCode}}); + Inputs.ClangTidyOpts = tidy::ClangTidyOptions::getDefaults(); + Inputs.ClangTidyOpts.Checks = + "-*, bugprone-sizeof-expression, bugprone-macro-repeated-side-effects, " + "modernize-deprecated-headers"; auto PCHs = std::make_shared(); auto Preamble = buildPreamble(FullFilename, *createInvocationFromCommandLine(Cmd),