diff --git a/clang-tools-extra/clangd/test/clang-tidy-config.test b/clang-tools-extra/clangd/test/clang-tidy-config.test new file mode 100644 --- /dev/null +++ b/clang-tools-extra/clangd/test/clang-tidy-config.test @@ -0,0 +1,40 @@ +# RUN: clangd -lit-test -clang-tidy-config="{CheckOptions: [{key: readability-identifier-naming.FunctionCase, value: camelBack}], Checks: '-*,readability-identifier-naming' }" < %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","text":"void BadName();"}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [ +# CHECK-NEXT: { +# CHECK-NEXT: "code": "readability-identifier-naming", +# CHECK-NEXT: "message": "Invalid case style for function 'BadName' (fix available)", +# CHECK-NEXT: "range": { +# CHECK-NEXT: "end": { +# CHECK-NEXT: "character": 12, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: }, +# CHECK-NEXT: "start": { +# CHECK-NEXT: "character": 5, +# CHECK-NEXT: "line": 0 +# CHECK-NEXT: } +# CHECK-NEXT: }, +# CHECK-NEXT: "severity": 2, +# CHECK-NEXT: "source": "clang-tidy" +# CHECK-NEXT: } +# CHECK-NEXT: ], +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c", +# CHECK-NEXT: "version": 0 +# CHECK-NEXT: } +--- +{"jsonrpc":"2.0","id":2,"method":"sync","params":null} +--- +{"jsonrpc":"2.0","method":"textDocument/didClose","params":{"textDocument":{"uri":"test:///foo.c"}}} +# CHECK: "method": "textDocument/publishDiagnostics", +# CHECK-NEXT: "params": { +# CHECK-NEXT: "diagnostics": [], +# CHECK-NEXT: "uri": "file://{{.*}}/foo.c" +# CHECK-NEXT: } +--- +{"jsonrpc":"2.0","id":5,"method":"shutdown"} +--- +{"jsonrpc":"2.0","method":"exit"} diff --git a/clang-tools-extra/clangd/tool/ClangdMain.cpp b/clang-tools-extra/clangd/tool/ClangdMain.cpp --- a/clang-tools-extra/clangd/tool/ClangdMain.cpp +++ b/clang-tools-extra/clangd/tool/ClangdMain.cpp @@ -149,6 +149,15 @@ init(""), }; +opt ClangTidyConfig{ + "clang-tidy-config", + cat(Features), + desc("Specifies a YAML/JSON configuration for when running clang-tidy " + "checks (this will override clang-tidy files). Only meaningful when " + "-clang-tidy flag is on"), + init(""), +}; + opt CodeCompletionParse{ "completion-parse", cat(Features), @@ -714,10 +723,23 @@ tidy::ClangTidyOptions OverrideClangTidyOptions; if (!ClangTidyChecks.empty()) OverrideClangTidyOptions.Checks = ClangTidyChecks; - ClangTidyOptProvider = std::make_unique( - tidy::ClangTidyGlobalOptions(), - /* Default */ EmptyDefaults, - /* Override */ OverrideClangTidyOptions, FSProvider.getFileSystem()); + if (ClangTidyConfig.empty()) { + ClangTidyOptProvider = std::make_unique( + tidy::ClangTidyGlobalOptions(), + /* Default */ EmptyDefaults, + /* Override */ OverrideClangTidyOptions, FSProvider.getFileSystem()); + } else { + llvm::ErrorOr ParsedConfig = + tidy::parseConfiguration(ClangTidyConfig); + if (!ParsedConfig) { + elog("Invalid -clang-tidy-config: {0}", + ParsedConfig.getError().message()); + return 1; + } + ClangTidyOptProvider = std::make_unique( + tidy::ClangTidyGlobalOptions(), EmptyDefaults, *ParsedConfig, + OverrideClangTidyOptions); + } Opts.GetClangTidyOptions = [&](llvm::vfs::FileSystem &, llvm::StringRef File) { // This function must be thread-safe and tidy option providers are not.