sammccall updated this revision to Diff 478274.
sammccall retitled this revision from "[clangd] Don't run slow clang-tidy 
checks" to "[clangd] Don't run slow clang-tidy checks by default".
sammccall edited the summary of this revision.
sammccall added a comment.

Add tests of isFast
Make isFast a tristate (fast/unknown/slow)
Make the config option a tristate filter (strict/loose/none)
Emit diagnostics when enabling a (maybe)slow check in config


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D138505/new/

https://reviews.llvm.org/D138505

Files:
  clang-tools-extra/clangd/Config.h
  clang-tools-extra/clangd/ConfigCompile.cpp
  clang-tools-extra/clangd/ConfigFragment.h
  clang-tools-extra/clangd/ConfigYAML.cpp
  clang-tools-extra/clangd/ParsedAST.cpp
  clang-tools-extra/clangd/TidyProvider.cpp
  clang-tools-extra/clangd/TidyProvider.h
  clang-tools-extra/clangd/tool/Check.cpp
  clang-tools-extra/clangd/unittests/ParsedASTTests.cpp
  clang-tools-extra/clangd/unittests/TidyProviderTests.cpp

Index: clang-tools-extra/clangd/unittests/TidyProviderTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/TidyProviderTests.cpp
+++ clang-tools-extra/clangd/unittests/TidyProviderTests.cpp
@@ -6,8 +6,10 @@
 //
 //===----------------------------------------------------------------------===//
 
+#include "Feature.h"
 #include "TestFS.h"
 #include "TidyProvider.h"
+#include "llvm/Testing/Support/SupportHelpers.h"
 #include "gtest/gtest.h"
 
 namespace clang {
@@ -52,6 +54,22 @@
   EXPECT_EQ(*Sub2Options.Checks, "misc-*,bugprone-*");
   EXPECT_EQ(Sub2Options.CheckOptions.lookup("TestKey").Value, "3");
 }
+
+TEST(TidyProvider, IsFastTidyCheck) {
+  EXPECT_THAT(isFastTidyCheck("misc-const-correctness"), llvm::ValueIs(false));
+  EXPECT_THAT(isFastTidyCheck("bugprone-suspicious-include"),
+              llvm::ValueIs(true));
+  // Linked in (ParsedASTTests.cpp) but not measured.
+  EXPECT_EQ(isFastTidyCheck("replay-preamble-check"), llvm::None);
+}
+
+#if CLANGD_TIDY_CHECKS
+TEST(TidyProvider, IsValidCheck) {
+  EXPECT_TRUE(isRegisteredTidyCheck("bugprone-argument-comment"));
+  EXPECT_FALSE(isRegisteredTidyCheck("bugprone-argument-clinic"));
+}
+#endif
+
 } // namespace
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/unittests/ParsedASTTests.cpp
===================================================================
--- clang-tools-extra/clangd/unittests/ParsedASTTests.cpp
+++ clang-tools-extra/clangd/unittests/ParsedASTTests.cpp
@@ -17,6 +17,7 @@
 #include "AST.h"
 #include "Annotations.h"
 #include "Compiler.h"
+#include "Config.h"
 #include "Diagnostics.h"
 #include "Headers.h"
 #include "ParsedAST.h"
@@ -25,6 +26,7 @@
 #include "TestFS.h"
 #include "TestTU.h"
 #include "TidyProvider.h"
+#include "support/Context.h"
 #include "clang/AST/DeclTemplate.h"
 #include "clang/Basic/SourceLocation.h"
 #include "clang/Basic/SourceManager.h"
@@ -427,6 +429,9 @@
   // sense in c++ (also they actually break on windows), just set language to
   // obj-c.
   TU.ExtraArgs = {"-isystem.", "-xobjective-c"};
+  Config Cfg;
+  Cfg.Diagnostics.ClangTidy.FastCheckFilter = Config::FastCheckPolicy::Loose;
+  WithContextValue WithCfg(Config::Key, std::move(Cfg));
 
   const auto &AST = TU.build();
   const auto &SM = AST.getSourceManager();
Index: clang-tools-extra/clangd/tool/Check.cpp
===================================================================
--- clang-tools-extra/clangd/tool/Check.cpp
+++ clang-tools-extra/clangd/tool/Check.cpp
@@ -30,6 +30,8 @@
 #include "CodeComplete.h"
 #include "CompileCommands.h"
 #include "Config.h"
+#include "ConfigFragment.h"
+#include "ConfigProvider.h"
 #include "Feature.h"
 #include "GlobalCompilationDatabase.h"
 #include "Hover.h"
@@ -82,15 +84,19 @@
     "check-completion",
     llvm::cl::desc("Run code-completion at each point (slow)"),
     llvm::cl::init(false)};
+llvm::cl::opt<bool> CheckWarnings{
+    "check-warnings",
+    llvm::cl::desc("Print warnings as well as errors"),
+    llvm::cl::init(false)};
 
 // Print (and count) the error-level diagnostics (warnings are ignored).
 unsigned showErrors(llvm::ArrayRef<Diag> Diags) {
   unsigned ErrCount = 0;
   for (const auto &D : Diags) {
-    if (D.Severity >= DiagnosticsEngine::Error) {
+    if (D.Severity >= DiagnosticsEngine::Error || CheckWarnings)
       elog("[{0}] Line {1}: {2}", D.Name, D.Range.start.line + 1, D.Message);
+    if (D.Severity >= DiagnosticsEngine::Error)
       ++ErrCount;
-    }
   }
   return ErrCount;
 }
@@ -452,8 +458,23 @@
   }
   log("Testing on source file {0}", File);
 
+  class OverrideConfigProvider : public config::Provider {
+    std::vector<config::CompiledFragment>
+    getFragments(const config::Params &,
+                 config::DiagnosticCallback Diag) const override {
+      config::Fragment F;
+      // If we're timing clang-tidy checks, implicitly disabling the slow ones
+      // is counterproductive! 
+      if (CheckTidyTime.getNumOccurrences())
+        F.Diagnostics.ClangTidy.FastCheckFilter.emplace("None");
+      return {std::move(F).compile(Diag)};
+    }
+  } OverrideConfig;
+  auto ConfigProvider =
+      config::Provider::combine({Opts.ConfigProvider, &OverrideConfig});
+
   auto ContextProvider = ClangdServer::createConfiguredContextProvider(
-      Opts.ConfigProvider, nullptr);
+      ConfigProvider.get(), nullptr);
   WithContext Ctx(ContextProvider(
       FakeFile.empty()
           ? File
Index: clang-tools-extra/clangd/TidyProvider.h
===================================================================
--- clang-tools-extra/clangd/TidyProvider.h
+++ clang-tools-extra/clangd/TidyProvider.h
@@ -60,6 +60,10 @@
 /// \pre \p must not be empty, must not contain '*' or ',' or start with '-'.
 bool isRegisteredTidyCheck(llvm::StringRef Check);
 
+/// Returns if \p Check is known-fast, known-slow, or its speed is unknown.
+/// By default, only fast checks will run in clangd.
+llvm::Optional<bool> isFastTidyCheck(llvm::StringRef Check);
+
 } // namespace clangd
 } // namespace clang
 
Index: clang-tools-extra/clangd/TidyProvider.cpp
===================================================================
--- clang-tools-extra/clangd/TidyProvider.cpp
+++ clang-tools-extra/clangd/TidyProvider.cpp
@@ -213,13 +213,7 @@
                        // tries to build an AST.
                        "-bugprone-use-after-move",
                        // Alias for bugprone-use-after-move.
-                       "-hicpp-invalid-access-moved",
-
-                       // ----- Performance problems -----
-
-                       // This check runs expensive analysis for each variable.
-                       // It has been observed to increase reparse time by 10x.
-                       "-misc-const-correctness");
+                       "-hicpp-invalid-access-moved");
 
   size_t Size = BadChecks.size();
   for (const std::string &Str : ExtraBadChecks) {
@@ -308,5 +302,18 @@
 
   return AllChecks.contains(Check);
 }
+
+llvm::Optional<bool> isFastTidyCheck(llvm::StringRef Check) {
+  static auto &Fast = *new llvm::StringMap<bool>{
+#define FAST(CHECK, TIME) {#CHECK,true},
+#define SLOW(CHECK, TIME) {#CHECK,false},
+#include "TidyFastChecks.inc"
+  };
+  auto It = Fast.find(Check);
+  if (It == Fast.end())
+    return llvm::None;
+  return It->second;
+}
+
 } // namespace clangd
 } // namespace clang
Index: clang-tools-extra/clangd/ParsedAST.cpp
===================================================================
--- clang-tools-extra/clangd/ParsedAST.cpp
+++ clang-tools-extra/clangd/ParsedAST.cpp
@@ -345,6 +345,7 @@
                  std::shared_ptr<const PreambleData> Preamble) {
   trace::Span Tracer("BuildAST");
   SPAN_ATTACH(Tracer, "File", Filename);
+  const Config &Cfg = Config::current();
 
   auto VFS = Inputs.TFS->view(Inputs.CompileCommand.Directory);
   if (Preamble && Preamble->StatCache)
@@ -476,6 +477,18 @@
     tidy::ClangTidyCheckFactories CTFactories;
     for (const auto &E : tidy::ClangTidyModuleRegistry::entries())
       E.instantiate()->addCheckFactories(CTFactories);
+    if (Cfg.Diagnostics.ClangTidy.FastCheckFilter !=
+        Config::FastCheckPolicy::None) {
+      // Can't remove checks from a factories container, so create a new one.
+      bool AllowUnknown = Cfg.Diagnostics.ClangTidy.FastCheckFilter ==
+                          Config::FastCheckPolicy::Loose;
+      tidy::ClangTidyCheckFactories FastFactories;
+      for (const auto &Factory : CTFactories) {
+        if (isFastTidyCheck(Factory.getKey()).value_or(AllowUnknown))
+          FastFactories.registerCheckFactory(Factory.first(), Factory.second);
+      }
+      CTFactories = std::move(FastFactories);
+    }
     CTContext.emplace(std::make_unique<tidy::DefaultOptionsProvider>(
         tidy::ClangTidyGlobalOptions(), ClangTidyOpts));
     CTContext->setDiagnosticsEngine(&Clang->getDiagnostics());
@@ -504,7 +517,6 @@
                                           SourceLocation());
     }
 
-    const Config &Cfg = Config::current();
     ASTDiags.setLevelAdjuster([&](DiagnosticsEngine::Level DiagLevel,
                                   const clang::Diagnostic &Info) {
       if (Cfg.Diagnostics.SuppressAll ||
Index: clang-tools-extra/clangd/ConfigYAML.cpp
===================================================================
--- clang-tools-extra/clangd/ConfigYAML.cpp
+++ clang-tools-extra/clangd/ConfigYAML.cpp
@@ -152,6 +152,10 @@
       });
       CheckOptDict.parse(N);
     });
+    Dict.handle("FastCheckFilter", [&](Node &N) {
+      if (auto FastCheckFilter = scalarValue(N, "FastCheckFilter"))
+        F.FastCheckFilter = *FastCheckFilter;
+    });
     Dict.parse(N);
   }
 
Index: clang-tools-extra/clangd/ConfigFragment.h
===================================================================
--- clang-tools-extra/clangd/ConfigFragment.h
+++ clang-tools-extra/clangd/ConfigFragment.h
@@ -264,6 +264,13 @@
       ///     readability-braces-around-statements.ShortStatementLines: 2
       std::vector<std::pair<Located<std::string>, Located<std::string>>>
           CheckOptions;
+
+      /// Whether to run checks that may slow down clangd.
+      ///   Strict: Run only checks measured to be fast. (Default)
+      ///           This excludes recently-added checks we have not timed yet.
+      ///   Loose: Run checks unless they are known to be slow.
+      ///   None: Run checks regardless of their speed.
+      llvm::Optional<Located<std::string>> FastCheckFilter;
     };
     ClangTidyBlock ClangTidy;
   };
Index: clang-tools-extra/clangd/ConfigCompile.cpp
===================================================================
--- clang-tools-extra/clangd/ConfigCompile.cpp
+++ clang-tools-extra/clangd/ConfigCompile.cpp
@@ -323,11 +323,11 @@
 
   void compile(Fragment::IndexBlock &&F) {
     if (F.Background) {
-      if (auto Val = compileEnum<Config::BackgroundPolicy>("Background",
-                                                           **F.Background)
-                         .map("Build", Config::BackgroundPolicy::Build)
-                         .map("Skip", Config::BackgroundPolicy::Skip)
-                         .value())
+      if (auto Val =
+              compileEnum<Config::BackgroundPolicy>("Background", *F.Background)
+                  .map("Build", Config::BackgroundPolicy::Build)
+                  .map("Skip", Config::BackgroundPolicy::Skip)
+                  .value())
         Out.Apply.push_back(
             [Val](const Params &, Config &C) { C.Index.Background = *Val; });
     }
@@ -433,7 +433,7 @@
 
     if (F.UnusedIncludes)
       if (auto Val = compileEnum<Config::UnusedIncludesPolicy>(
-                         "UnusedIncludes", **F.UnusedIncludes)
+                         "UnusedIncludes", *F.UnusedIncludes)
                          .map("Strict", Config::UnusedIncludesPolicy::Strict)
                          .map("None", Config::UnusedIncludesPolicy::None)
                          .value())
@@ -474,11 +474,31 @@
       diag(Error, "Invalid clang-tidy check name", Arg.Range);
       return;
     }
-    if (!Str.contains('*') && !isRegisteredTidyCheck(Str)) {
-      diag(Warning,
-           llvm::formatv("clang-tidy check '{0}' was not found", Str).str(),
-           Arg.Range);
-      return;
+    if (!Str.contains('*')) {
+      if (!isRegisteredTidyCheck(Str)) {
+        diag(Warning,
+             llvm::formatv("clang-tidy check '{0}' was not found", Str).str(),
+             Arg.Range);
+        return;
+      }
+      auto Fast = isFastTidyCheck(Str);
+      if (!Fast.has_value()) {
+        diag(Warning,
+             llvm::formatv(
+                 "Speed of clang-tidy check '{0}' is not known. "
+                 "It will only run if ClangTidy.FastCheckFilter is Loose or None",
+                 Str)
+                 .str(),
+             Arg.Range);
+      } else if (!*Fast) {
+        diag(Warning,
+             llvm::formatv(
+                 "clang-tidy check '{0}' is slow. "
+                 "It will only run if ClangTidy.FastCheckFilter is None",
+                 Str)
+                 .str(),
+             Arg.Range);
+      }
     }
     CurSpec += ',';
     if (!IsPositive)
@@ -514,6 +534,16 @@
                   StringPair.first, StringPair.second);
           });
     }
+    if (F.FastCheckFilter.has_value())
+      if (auto Val = compileEnum<Config::FastCheckPolicy>("FastCheckFilter",
+                                                          *F.FastCheckFilter)
+                         .map("Strict", Config::FastCheckPolicy::Strict)
+                         .map("Loose", Config::FastCheckPolicy::Loose)
+                         .map("None", Config::FastCheckPolicy::None)
+                         .value())
+        Out.Apply.push_back([Val](const Params &, Config &C) {
+          C.Diagnostics.ClangTidy.FastCheckFilter = *Val;
+        });
   }
 
   void compile(Fragment::DiagnosticsBlock::IncludesBlock &&F) {
Index: clang-tools-extra/clangd/Config.h
===================================================================
--- clang-tools-extra/clangd/Config.h
+++ clang-tools-extra/clangd/Config.h
@@ -89,6 +89,7 @@
   } Index;
 
   enum UnusedIncludesPolicy { Strict, None };
+  enum class FastCheckPolicy { Strict, Loose, None };
   /// Controls warnings and errors when parsing code.
   struct {
     bool SuppressAll = false;
@@ -99,6 +100,7 @@
       // A comma-seperated list of globs specify which clang-tidy checks to run.
       std::string Checks;
       llvm::StringMap<std::string> CheckOptions;
+      FastCheckPolicy FastCheckFilter = FastCheckPolicy::Strict;
     } ClangTidy;
 
     UnusedIncludesPolicy UnusedIncludes = None;
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
  • [PATCH] D138505: [clangd] ... Sam McCall via Phabricator via cfe-commits

Reply via email to