[PATCH] D91029: [clangd] Implement clang-tidy options from config

Nathan James via Phabricator via cfe-commits cfe-commits at lists.llvm.org
Tue Nov 24 11:33:21 PST 2020


njames93 marked 12 inline comments as done and an inline comment as not done.
njames93 added inline comments.


================
Comment at: clang-tools-extra/clangd/ParsedAST.cpp:241
+/// Empty clang tidy provider, using this as a provider will disable clang-tidy.
+static void emptyTidyProvider(tidy::ClangTidyOptions &, llvm::StringRef,
+                              unsigned &, PathRef) {}
----------------
sammccall wrote:
> or just use fixedTidyProvider("-*")
Issue with that approach is fixedTidyProvider has state so that would need a stable storage when passing to `asClangTidyOptionsProvider`.


================
Comment at: clang-tools-extra/clangd/TidyProvider.cpp:74
+    auto EmptyDefaults = tidy::ClangTidyOptions::getDefaults();
+    EmptyDefaults.Checks.reset(); // So we can tell if checks were ever set.
+    EmptyDefaults.User = llvm::sys::Process::GetEnv("USER");
----------------
sammccall wrote:
> I'm not sure if this applies anymore - it's going to be the bottom of the stack, and nullopt is the default
This is just incase `ClangTidyOptions::getDefaults()` ever changes.


================
Comment at: clang-tools-extra/clangd/TidyProvider.cpp:75
+    EmptyDefaults.Checks.reset(); // So we can tell if checks were ever set.
+    EmptyDefaults.User = llvm::sys::Process::GetEnv("USER");
+#ifdef _WIN32
----------------
sammccall wrote:
> sammccall wrote:
> > nit: hoist the getenv out of the lambda?
> seems like we can just assign to Opts.User directly?
We can't as `EmptyDefaults` also contains the Options clang tidy modules may want to set.


================
Comment at: clang-tools-extra/clangd/TidyProvider.cpp:167
+
+    if (FS->makeAbsolute(AbsolutePath))
+      return;
----------------
sammccall wrote:
> (as noted elsewhere, the path is always absolute in clangd and we should make this an interface requiremnet)
See previous comment about clang-tidy checks not adhering to this requirement.


================
Comment at: clang-tools-extra/clangd/TidyProvider.cpp:170
+
+    llvm::sys::path::remove_dots(AbsolutePath, true);
+    llvm::StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
----------------
sammccall wrote:
> I don't think we need to try to stat the directory here.
> 
> FileOptionsProvider does, but looking at the history, this seems related to some combination of:
>  - arg validation (the parameter there is a directory name, rather than a filename)
>  - trying to return an appropriate error_code (original return type was ErrorOr<Options>)
>  - iterating over multiple possible config files
>  - maybe caching
Ditto


================
Comment at: clang-tools-extra/clangd/TidyProvider.cpp:194
+    for (auto &Option : llvm::reverse(OptionStack))
+      Opts.mergeWith(Option, ++Order);
+  };
----------------
sammccall wrote:
> This order business is a real tangle, and looks like legacy to me (qualified vs unqualified names).
> 
> I don't really think we want to use/expose it beyond the requirement that we don't change the meaning of existing sets of `.clang-tidy` config files.
> And I don't think we want to bother teaching clangd's config system about it.
> 
> So I'd suggest a hack/simplification:
>  - we remove the `order` parameter from TidyProvider
>  - here in provideClangTidyFiles, we just use a local Order which counts up from 1. This preserves the relative precedence of .clang-tidy files
>  - in provideClangdConfig, we always set the order to max_uint (always wins)
>  - if we ever add checkoptions to any other providers, we set the order to 0 (always loses)
>  - combine() doesn't touch order
> 
> This *does* duplicate the fact that .clangd config is stacked on top of .clang-tidy in ClangdMain, but in a way that only matters with disputes between qualified/unqualified names.
That approach is a simplification but it only makes sense in how we are using the providers in clangd. Downstream someone may want to add another provider that may get ran after `.clangd` providers. Forcing `.clangd` to have the highest priority will result in unexpected behaviour in that instance.
I'm happy to go with your approach if you don't see it as a blocking issue.


================
Comment at: clang-tools-extra/clangd/TidyProvider.h:25
+                                   /*Priority=*/unsigned &,
+                                   /*CWD*/ PathRef);
+} // namespace detail
----------------
sammccall wrote:
> we should always be passing an absolute path, so CWD shouldn't be needed.
> 
> I'm a bit surprised that ParsedAST::build doesn't assert that (though it calls PreamblePatch::create, which does). Feel free to add it!
While clangd may always use absolute paths, clang-tidy may not. Checks are able to query the context and get options for other files.
When I put asserts on the Filename being absolute, things started failing.


================
Comment at: clang-tools-extra/clangd/tool/ClangdMain.cpp:850
+    Providers.push_back(provideEnvironment());
+    Providers.push_back(provideClangTidyFiles(TFS));
+    if (EnableConfig)
----------------
sammccall wrote:
> if -clang-tidy-checks is provided, we don't want to parse .clang-tidy files, respect clangd config, or disable unusable checks - just environment + the flag.
> The main purpose of that flag is to debug e.g. isolate crashes down to individual checks, so full control is best.
> 
> (I believe this matches the old behavior, but it's really hard to tell from the code - the new code is much nicer!)
The old behaviour, and the behaviour of the `-checks` option in clang-tidy is to append the checks specified on the command line to whats found in .clang-tidy files. This is so that the checks will respect other fields in the configuration.


================
Comment at: clang-tools-extra/clangd/tool/ClangdMain.cpp:853
+      Providers.push_back(provideClangdConfig());
+    if (ClangTidyChecks.empty())
+      Providers.push_back(fixedTidyProvider(ClangTidyChecks));
----------------
sammccall wrote:
> I think the condition is backwards
Yes it was, good spot.


================
Comment at: clang-tools-extra/clangd/unittests/ClangdTests.cpp:1221
+  // run.
+  FS.Files[testPath(".clang-tidy")] = R"(
+    Checks: -*,bugprone-use-after-move,llvm-header-guard
----------------
sammccall wrote:
> This is confusing... I think these are now disabled twice (by disableUnusableChecks(), and via provideClangTidyFiles).
> 
> (I've got no objection to checking that elements of the standard tidy config stack work together properly, but I don't think that's what's happening in this test)
`provideClangTidyFiles` here will enable use-after-move/header-guard but disable all other checks, which is what we want.
Hopefully `disableUnusableChecks` will then disable those 2 checks and all will work well.


================
Comment at: clang-tools-extra/clangd/unittests/ClangdTests.cpp:1226
+  std::vector<TidyProvider> Stack;
+  Stack.push_back(provideEnvironment());
+  Stack.push_back(provideClangTidyFiles(FS));
----------------
sammccall wrote:
> I think we probably don't want the environment here, if it does anything at all it probably makes the test non-hermetic
I agree, can trim this down a little.


================
Comment at: clang-tools-extra/clangd/unittests/TestTU.cpp:62
   Inputs.Opts = ParseOptions();
-  Inputs.Opts.ClangTidyOpts.Checks = ClangTidyChecks;
-  Inputs.Opts.ClangTidyOpts.WarningsAsErrors = ClangTidyWarningsAsErrors;
+  if (ClangTidyProvider)
+    Inputs.ClangTidyProvider = ClangTidyProvider;
----------------
sammccall wrote:
> I think the condition is not needed
I think it is, the operator= for function_ref doesn't seem to correctly handle the case when the argument doesn't hold a callable. Well asserts were failing before I added the condition, may be a bug somewhere else.


================
Comment at: clang-tools-extra/clangd/unittests/TestTU.h:62
 
-  llvm::Optional<std::string> ClangTidyChecks;
-  llvm::Optional<std::string> ClangTidyWarningsAsErrors;
+  mutable TidyProvider ClangTidyProvider = {};
   // Index to use when building AST.
----------------
sammccall wrote:
> mutable here smells funny, can we do `using TidyProvider = unique_function<void(args) const>` instead? (so it's const-callable)
I was debating between the two but that does look a little nicer.


Repository:
  rG LLVM Github Monorepo

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

https://reviews.llvm.org/D91029



More information about the cfe-commits mailing list