diff --git a/clang-tools-extra/clangd/Config.h b/clang-tools-extra/clangd/Config.h --- a/clang-tools-extra/clangd/Config.h +++ b/clang-tools-extra/clangd/Config.h @@ -88,13 +88,21 @@ bool StandardLibrary = true; } Index; - enum UnusedIncludesPolicy { + enum class UnusedIncludesPolicy { /// Diagnose unused includes. Strict, None, /// The same as Strict, but using the include-cleaner library. Experiment, }; + /// Configures whether clangd can emit fast but possibly inaccurate + /// diagnostics. + enum class DiagnosticsMode { + /// Only emit diagnostics with up-to-date analysis. + Strict, + /// Can emit diagnostics with stale analysis. + Fast, + }; /// Controls warnings and errors when parsing code. struct { bool SuppressAll = false; @@ -107,7 +115,9 @@ llvm::StringMap CheckOptions; } ClangTidy; - UnusedIncludesPolicy UnusedIncludes = None; + UnusedIncludesPolicy UnusedIncludes = UnusedIncludesPolicy::None; + + DiagnosticsMode Mode = DiagnosticsMode::Strict; /// IncludeCleaner will not diagnose usages of these headers matched by /// these regexes. diff --git a/clang-tools-extra/clangd/ConfigCompile.cpp b/clang-tools-extra/clangd/ConfigCompile.cpp --- a/clang-tools-extra/clangd/ConfigCompile.cpp +++ b/clang-tools-extra/clangd/ConfigCompile.cpp @@ -441,8 +441,16 @@ Out.Apply.push_back([Val](const Params &, Config &C) { C.Diagnostics.UnusedIncludes = *Val; }); - compile(std::move(F.Includes)); + if (F.Mode) { + if (auto Val = compileEnum("Mode", **F.Mode) + .map("Strict", Config::DiagnosticsMode::Strict) + .map("Fast", Config::DiagnosticsMode::Fast) + .value()) + Out.Apply.push_back( + [Val](const Params &, Config &C) { C.Diagnostics.Mode = *Val; }); + } + compile(std::move(F.Includes)); compile(std::move(F.ClangTidy)); } diff --git a/clang-tools-extra/clangd/ConfigFragment.h b/clang-tools-extra/clangd/ConfigFragment.h --- a/clang-tools-extra/clangd/ConfigFragment.h +++ b/clang-tools-extra/clangd/ConfigFragment.h @@ -232,9 +232,16 @@ /// /// Valid values are: /// - Strict + /// - Experiment /// - None std::optional> UnusedIncludes; + /// Controls whether clangd should emit fast but loose diagnostics. + /// Valid values are: + /// - Fast + /// - Strict + std::optional> Mode; + /// Controls IncludeCleaner diagnostics. struct IncludesBlock { /// Regexes that will be used to avoid diagnosing certain includes as diff --git a/clang-tools-extra/clangd/ConfigYAML.cpp b/clang-tools-extra/clangd/ConfigYAML.cpp --- a/clang-tools-extra/clangd/ConfigYAML.cpp +++ b/clang-tools-extra/clangd/ConfigYAML.cpp @@ -130,6 +130,7 @@ }); Dict.handle("Includes", [&](Node &N) { parse(F.Includes, N); }); Dict.handle("ClangTidy", [&](Node &N) { parse(F.ClangTidy, N); }); + Dict.handle("Mode", [&](Node &N) { F.Mode = scalarValue(N, "Mode"); }); Dict.parse(N); } @@ -268,7 +269,7 @@ // If Key is seen twice, Parse runs only once and an error is reported. void handle(llvm::StringLiteral Key, std::function Parse) { for (const auto &Entry : Keys) { - (void) Entry; + (void)Entry; assert(Entry.first != Key && "duplicate key handler"); } Keys.emplace_back(Key, std::move(Parse)); diff --git a/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp b/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp --- a/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp +++ b/clang-tools-extra/clangd/unittests/ConfigCompileTests.cpp @@ -543,6 +543,23 @@ EXPECT_TRUE(compileAndApply()); EXPECT_THAT(Conf.Style.FullyQualifiedNamespaces, ElementsAre("foo", "bar")); } + +TEST_F(ConfigCompileTests, DiagnosticsMode) { + Frag = {}; + EXPECT_TRUE(compileAndApply()); + // Defaults to Strict. + EXPECT_EQ(Conf.Diagnostics.Mode, Config::DiagnosticsMode::Strict); + + Frag.Diagnostics.Mode.emplace("Fast"); + EXPECT_TRUE(compileAndApply()); + // Defaults to Strict. + EXPECT_EQ(Conf.Diagnostics.Mode, Config::DiagnosticsMode::Fast); + + Frag.Diagnostics.Mode.emplace("Strict"); + EXPECT_TRUE(compileAndApply()); + // Defaults to Strict. + EXPECT_EQ(Conf.Diagnostics.Mode, Config::DiagnosticsMode::Strict); +} } // namespace } // namespace config } // namespace clangd diff --git a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp --- a/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp +++ b/clang-tools-extra/clangd/unittests/ConfigYAMLTests.cpp @@ -273,6 +273,31 @@ EXPECT_THAT(Results[0].Style.FullyQualifiedNamespaces, ElementsAre(val("foo"), val("bar"))); } + +TEST(ParseYAML, DiagnosticsMode) { + CapturedDiags Diags; + { + Annotations YAML(R"yaml( +Diagnostics: + Mode: Fast)yaml"); + auto Results = + Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); + ASSERT_THAT(Diags.Diagnostics, IsEmpty()); + ASSERT_EQ(Results.size(), 1u); + EXPECT_THAT(Results[0].Diagnostics.Mode, llvm::ValueIs(val("Fast"))); + } + + { + Annotations YAML(R"yaml( +Diagnostics: + Mode: Strict)yaml"); + auto Results = + Fragment::parseYAML(YAML.code(), "config.yaml", Diags.callback()); + ASSERT_THAT(Diags.Diagnostics, IsEmpty()); + ASSERT_EQ(Results.size(), 1u); + EXPECT_THAT(Results[0].Diagnostics.Mode, llvm::ValueIs(val("Strict"))); + } +} } // namespace } // namespace config } // namespace clangd diff --git a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp --- a/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp +++ b/clang-tools-extra/clangd/unittests/IncludeCleanerTests.cpp @@ -538,7 +538,7 @@ void foo() {} )cpp"); Config Cfg; - Cfg.Diagnostics.UnusedIncludes = Config::Experiment; + Cfg.Diagnostics.UnusedIncludes = Config::UnusedIncludesPolicy::Experiment; WithContextValue Ctx(Config::Key, std::move(Cfg)); ParsedAST AST = TU.build(); @@ -627,7 +627,7 @@ TU.ExtraArgs.emplace_back("-xobjective-c"); Config Cfg; - Cfg.Diagnostics.UnusedIncludes = Config::Strict; + Cfg.Diagnostics.UnusedIncludes = Config::UnusedIncludesPolicy::Strict; WithContextValue Ctx(Config::Key, std::move(Cfg)); ParsedAST AST = TU.build(); EXPECT_THAT(AST.getDiagnostics(), llvm::ValueIs(IsEmpty()));