clang-tools  14.0.0git
TidyProvider.cpp
Go to the documentation of this file.
1 //===--- TidyProvider.cpp - create options for running clang-tidy----------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "TidyProvider.h"
10 #include "../clang-tidy/ClangTidyModuleRegistry.h"
11 #include "Config.h"
12 #include "support/FileCache.h"
13 #include "support/Logger.h"
14 #include "support/Path.h"
15 #include "support/ThreadsafeFS.h"
16 #include "llvm/ADT/STLExtras.h"
17 #include "llvm/ADT/SmallString.h"
18 #include "llvm/ADT/StringExtras.h"
19 #include "llvm/ADT/StringSet.h"
20 #include "llvm/Support/Allocator.h"
21 #include "llvm/Support/ErrorHandling.h"
22 #include "llvm/Support/Process.h"
23 #include "llvm/Support/SourceMgr.h"
24 #include "llvm/Support/VirtualFileSystem.h"
25 #include <memory>
26 
27 namespace clang {
28 namespace clangd {
29 namespace {
30 
31 // Access to config from a .clang-tidy file, caching IO and parsing.
32 class DotClangTidyCache : private FileCache {
33  // We cache and expose shared_ptr to avoid copying the value on every lookup
34  // when we're ultimately just going to pass it to mergeWith.
35  mutable std::shared_ptr<const tidy::ClangTidyOptions> Value;
36 
37 public:
38  DotClangTidyCache(PathRef Path) : FileCache(Path) {}
39 
40  std::shared_ptr<const tidy::ClangTidyOptions>
41  get(const ThreadsafeFS &TFS,
42  std::chrono::steady_clock::time_point FreshTime) const {
43  std::shared_ptr<const tidy::ClangTidyOptions> Result;
44  read(
45  TFS, FreshTime,
46  [this](llvm::Optional<llvm::StringRef> Data) {
47  Value.reset();
48  if (Data && !Data->empty()) {
49  tidy::DiagCallback Diagnostics = [](const llvm::SMDiagnostic &D) {
50  switch (D.getKind()) {
51  case llvm::SourceMgr::DK_Error:
52  elog("tidy-config error at {0}:{1}:{2}: {3}", D.getFilename(),
53  D.getLineNo(), D.getColumnNo(), D.getMessage());
54  break;
55  case llvm::SourceMgr::DK_Warning:
56  log("tidy-config warning at {0}:{1}:{2}: {3}", D.getFilename(),
57  D.getLineNo(), D.getColumnNo(), D.getMessage());
58  break;
59  case llvm::SourceMgr::DK_Note:
60  case llvm::SourceMgr::DK_Remark:
61  vlog("tidy-config note at {0}:{1}:{2}: {3}", D.getFilename(),
62  D.getLineNo(), D.getColumnNo(), D.getMessage());
63  break;
64  }
65  };
67  llvm::MemoryBufferRef(*Data, path()), Diagnostics))
68  Value = std::make_shared<const tidy::ClangTidyOptions>(
69  std::move(*Parsed));
70  else
71  elog("Error parsing clang-tidy configuration in {0}: {1}", path(),
72  Parsed.getError().message());
73  }
74  },
75  [&]() { Result = Value; });
76  return Result;
77  }
78 };
79 
80 // Access to combined config from .clang-tidy files governing a source file.
81 // Each config file is cached and the caches are shared for affected sources.
82 //
83 // FIXME: largely duplicates config::Provider::fromAncestorRelativeYAMLFiles.
84 // Potentially useful for compile_commands.json too. Extract?
85 class DotClangTidyTree {
86  const ThreadsafeFS &FS;
87  std::string RelPath;
88  std::chrono::steady_clock::duration MaxStaleness;
89 
90  mutable std::mutex Mu;
91  // Keys are the ancestor directory, not the actual config path within it.
92  // We only insert into this map, so pointers to values are stable forever.
93  // Mutex guards the map itself, not the values (which are threadsafe).
94  mutable llvm::StringMap<DotClangTidyCache> Cache;
95 
96 public:
97  DotClangTidyTree(const ThreadsafeFS &FS)
98  : FS(FS), RelPath(".clang-tidy"), MaxStaleness(std::chrono::seconds(5)) {}
99 
100  void apply(tidy::ClangTidyOptions &Result, PathRef AbsPath) {
101  namespace path = llvm::sys::path;
102  assert(path::is_absolute(AbsPath));
103 
104  // Compute absolute paths to all ancestors (substrings of P.Path).
105  // Ensure cache entries for each ancestor exist in the map.
106  llvm::SmallVector<DotClangTidyCache *> Caches;
107  {
108  std::lock_guard<std::mutex> Lock(Mu);
109  for (auto Ancestor = absoluteParent(AbsPath); !Ancestor.empty();
110  Ancestor = absoluteParent(Ancestor)) {
111  auto It = Cache.find(Ancestor);
112  // Assemble the actual config file path only if needed.
113  if (It == Cache.end()) {
114  llvm::SmallString<256> ConfigPath = Ancestor;
115  path::append(ConfigPath, RelPath);
116  It = Cache.try_emplace(Ancestor, ConfigPath.str()).first;
117  }
118  Caches.push_back(&It->second);
119  }
120  }
121  // Finally query each individual file.
122  // This will take a (per-file) lock for each file that actually exists.
123  std::chrono::steady_clock::time_point FreshTime =
124  std::chrono::steady_clock::now() - MaxStaleness;
125  llvm::SmallVector<std::shared_ptr<const tidy::ClangTidyOptions>>
126  OptionStack;
127  for (const DotClangTidyCache *Cache : Caches)
128  if (auto Config = Cache->get(FS, FreshTime)) {
129  OptionStack.push_back(std::move(Config));
130  if (!OptionStack.back()->InheritParentConfig.getValueOr(false))
131  break;
132  }
133  unsigned Order = 1u;
134  for (auto &Option : llvm::reverse(OptionStack))
135  Result.mergeWith(*Option, Order++);
136  }
137 };
138 
139 } // namespace
140 
141 static void mergeCheckList(llvm::Optional<std::string> &Checks,
142  llvm::StringRef List) {
143  if (List.empty())
144  return;
145  if (!Checks || Checks->empty()) {
146  Checks.emplace(List);
147  return;
148  }
149  *Checks = llvm::join_items(",", *Checks, List);
150 }
151 
153  static const llvm::Optional<std::string> User = [] {
154  llvm::Optional<std::string> Ret = llvm::sys::Process::GetEnv("USER");
155 #ifdef _WIN32
156  if (!Ret)
157  return llvm::sys::Process::GetEnv("USERNAME");
158 #endif
159  return Ret;
160  }();
161 
162  if (User)
163  return
164  [](tidy::ClangTidyOptions &Opts, llvm::StringRef) { Opts.User = User; };
165  // FIXME: Once function_ref and unique_function operator= operators handle
166  // null values, this can return null.
167  return [](tidy::ClangTidyOptions &, llvm::StringRef) {};
168 }
169 
171  // These default checks are chosen for:
172  // - low false-positive rate
173  // - providing a lot of value
174  // - being reasonably efficient
175  static const std::string DefaultChecks = llvm::join_items(
176  ",", "readability-misleading-indentation", "readability-deleted-default",
177  "bugprone-integer-division", "bugprone-sizeof-expression",
178  "bugprone-suspicious-missing-comma", "bugprone-unused-raii",
179  "bugprone-unused-return-value", "misc-unused-using-decls",
180  "misc-unused-alias-decls", "misc-definitions-in-headers");
181  return [](tidy::ClangTidyOptions &Opts, llvm::StringRef) {
182  if (!Opts.Checks || Opts.Checks->empty())
183  Opts.Checks = DefaultChecks;
184  };
185 }
186 
188  llvm::StringRef WarningsAsErrors) {
189  return [Checks = std::string(Checks),
190  WarningsAsErrors = std::string(WarningsAsErrors)](
191  tidy::ClangTidyOptions &Opts, llvm::StringRef) {
192  mergeCheckList(Opts.Checks, Checks);
193  mergeCheckList(Opts.WarningsAsErrors, WarningsAsErrors);
194  };
195 }
196 
197 TidyProvider disableUnusableChecks(llvm::ArrayRef<std::string> ExtraBadChecks) {
198  constexpr llvm::StringLiteral Seperator(",");
199  static const std::string BadChecks =
200  llvm::join_items(Seperator,
201  // We want this list to start with a seperator to
202  // simplify appending in the lambda. So including an
203  // empty string here will force that.
204  "",
205  // ----- False Positives -----
206 
207  // Check relies on seeing ifndef/define/endif directives,
208  // clangd doesn't replay those when using a preamble.
209  "-llvm-header-guard",
210 
211  // ----- Crashing Checks -----
212 
213  // Check can choke on invalid (intermediate) c++
214  // code, which is often the case when clangd
215  // tries to build an AST.
216  "-bugprone-use-after-move");
217 
218  size_t Size = BadChecks.size();
219  for (const std::string &Str : ExtraBadChecks) {
220  if (Str.empty())
221  continue;
222  Size += Seperator.size();
223  if (LLVM_LIKELY(Str.front() != '-'))
224  ++Size;
225  Size += Str.size();
226  }
227  std::string DisableGlob;
228  DisableGlob.reserve(Size);
229  DisableGlob += BadChecks;
230  for (const std::string &Str : ExtraBadChecks) {
231  if (Str.empty())
232  continue;
233  DisableGlob += Seperator;
234  if (LLVM_LIKELY(Str.front() != '-'))
235  DisableGlob.push_back('-');
236  DisableGlob += Str;
237  }
238 
239  return [DisableList(std::move(DisableGlob))](tidy::ClangTidyOptions &Opts,
240  llvm::StringRef) {
241  if (Opts.Checks && !Opts.Checks->empty())
242  Opts.Checks->append(DisableList);
243  };
244 }
245 
247  return [](tidy::ClangTidyOptions &Opts, llvm::StringRef) {
248  const auto &CurTidyConfig = Config::current().Diagnostics.ClangTidy;
249  if (!CurTidyConfig.Checks.empty())
250  mergeCheckList(Opts.Checks, CurTidyConfig.Checks);
251 
252  for (const auto &CheckOption : CurTidyConfig.CheckOptions)
253  Opts.CheckOptions.insert_or_assign(CheckOption.getKey(),
255  CheckOption.getValue(), 10000U));
256  };
257 }
258 
260  return [Tree = std::make_unique<DotClangTidyTree>(TFS)](
261  tidy::ClangTidyOptions &Opts, llvm::StringRef Filename) {
262  Tree->apply(Opts, Filename);
263  };
264 }
265 
266 TidyProvider combine(std::vector<TidyProvider> Providers) {
267  // FIXME: Once function_ref and unique_function operator= operators handle
268  // null values, we should filter out any Providers that are null. Right now we
269  // have to ensure we dont pass any providers that are null.
270  return [Providers(std::move(Providers))](tidy::ClangTidyOptions &Opts,
271  llvm::StringRef Filename) {
272  for (const auto &Provider : Providers)
273  Provider(Opts, Filename);
274  };
275 }
276 
278  llvm::StringRef Filename) {
280  Opts.Checks->clear();
281  if (Provider)
282  Provider(Opts, Filename);
283  return Opts;
284 }
285 
286 bool isRegisteredTidyCheck(llvm::StringRef Check) {
287  assert(!Check.empty());
288  assert(!Check.contains('*') && !Check.contains(',') &&
289  "isRegisteredCheck doesn't support globs");
290  assert(Check.ltrim().front() != '-');
291 
292  static const llvm::StringSet<llvm::BumpPtrAllocator> AllChecks = [] {
293  llvm::StringSet<llvm::BumpPtrAllocator> Result;
295  for (tidy::ClangTidyModuleRegistry::entry E :
296  tidy::ClangTidyModuleRegistry::entries())
297  E.instantiate()->addCheckFactories(Factories);
298  for (const auto &Factory : Factories)
299  Result.insert(Factory.getKey());
300  return Result;
301  }();
302 
303  return AllChecks.contains(Check);
304 }
305 } // namespace clangd
306 } // namespace clang
clang::clangd::provideClangdConfig
TidyProviderRef provideClangdConfig()
Definition: TidyProvider.cpp:246
E
const Expr * E
Definition: AvoidBindCheck.cpp:88
clang::clangd::Path
std::string Path
A typedef to represent a file path.
Definition: Path.h:26
Path.h
Checks
static cl::opt< std::string > Checks("checks", cl::desc(R"( Comma-separated list of globs with optional '-' prefix. Globs are processed in order of appearance in the list. Globs without '-' prefix add checks with matching names to the set, globs with the '-' prefix remove checks with matching names from the set of enabled checks. This option's value is appended to the value of the 'Checks' option in .clang-tidy file, if any. )"), cl::init(""), cl::cat(ClangTidyCategory))
Diagnostics
WantDiagnostics Diagnostics
Definition: TUScheduler.cpp:561
clang::clangd::getTidyOptionsForFile
tidy::ClangTidyOptions getTidyOptionsForFile(TidyProviderRef Provider, llvm::StringRef Filename)
Definition: TidyProvider.cpp:277
FileCache.h
clang::clangd::provideClangTidyFiles
TidyProvider provideClangTidyFiles(ThreadsafeFS &TFS)
Provider that searches for .clang-tidy configuration files in the directory tree.
Definition: TidyProvider.cpp:259
Filename
std::string Filename
Filename as a string.
Definition: IncludeOrderCheck.cpp:39
clang::tidy::ClangTidyOptions::ClangTidyValue
Helper structure for storing option value with priority of the value.
Definition: ClangTidyOptions.h:103
clang::tidy::parseConfigurationWithDiags
llvm::ErrorOr< ClangTidyOptions > parseConfigurationWithDiags(llvm::MemoryBufferRef Config, DiagCallback Handler)
Definition: ClangTidyOptions.cpp:398
clang::tidy::ClangTidyOptions
Contains options for clang-tidy.
Definition: ClangTidyOptions.h:50
clang::tidy::ClangTidyCheckFactories
A collection of ClangTidyCheckFactory instances.
Definition: ClangTidyModule.h:28
ThreadsafeFS.h
TidyProvider.h
Logger.h
clang::clangd::TidyProvider
llvm::unique_function< void(tidy::ClangTidyOptions &, llvm::StringRef) const > TidyProvider
A factory to modify a tidy::ClangTidyOptions.
Definition: TidyProvider.h:24
clang::clangd::provideEnvironment
TidyProviderRef provideEnvironment()
Provider that just sets the defaults.
Definition: TidyProvider.cpp:152
if
if(CLANGD_ENABLE_REMOTE) generate_protos(RemoteIndexProto "Index.proto") generate_protos(MonitoringServiceProto "MonitoringService.proto" GRPC) generate_protos(RemoteIndexServiceProto "Service.proto" DEPENDS "Index.proto" GRPC) target_link_libraries(RemoteIndexServiceProto PRIVATE RemoteIndexProto MonitoringServiceProto) include_directories($
Definition: clangd/index/remote/CMakeLists.txt:1
clang::clangd::isRegisteredTidyCheck
bool isRegisteredTidyCheck(llvm::StringRef Check)
Returns if Check is a registered clang-tidy check.
Definition: TidyProvider.cpp:286
DefaultChecks
const char DefaultChecks[]
Definition: ClangTidyMain.cpp:36
Config
static cl::opt< std::string > Config("config", cl::desc(R"( Specifies a configuration in YAML/JSON format: -config="{Checks:' *', CheckOptions:[{key:x, value:y}]}" When the value is empty, clang-tidy will attempt to find a file named .clang-tidy for each source file in its parent directories. )"), cl::init(""), cl::cat(ClangTidyCategory))
Config.h
WarningsAsErrors
static cl::opt< std::string > WarningsAsErrors("warnings-as-errors", cl::desc(R"( Upgrades warnings to errors. Same format as '-checks'. This option's value is appended to the value of the 'WarningsAsErrors' option in .clang-tidy file, if any. )"), cl::init(""), cl::cat(ClangTidyCategory))
clang::clangd::PathRef
llvm::StringRef PathRef
A typedef to represent a ref to file path.
Definition: Path.h:29
clang::clangd::ThreadsafeFS
Wrapper for vfs::FileSystem for use in multithreaded programs like clangd.
Definition: ThreadsafeFS.h:28
clang::clangd::absoluteParent
PathRef absoluteParent(PathRef Path)
Variant of parent_path that operates only on absolute paths.
Definition: Path.cpp:22
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
clang::clangd::disableUnusableChecks
TidyProvider disableUnusableChecks(llvm::ArrayRef< std::string > ExtraBadChecks)
Provider that will disable checks known to not work with clangd.
Definition: TidyProvider.cpp:197
clang::clangd::provideDefaultChecks
TidyProviderRef provideDefaultChecks()
Provider that will enable a nice set of default checks if none are specified.
Definition: TidyProvider.cpp:170
clang::clangd::TidyProviderRef
llvm::function_ref< void(tidy::ClangTidyOptions &, llvm::StringRef)> TidyProviderRef
A factory to modify a tidy::ClangTidyOptions that doesn't hold any state.
Definition: TidyProvider.h:29
clang::clangd::elog
void elog(const char *Fmt, Ts &&... Vals)
Definition: Logger.h:62
clang::clangd::addTidyChecks
TidyProvider addTidyChecks(llvm::StringRef Checks, llvm::StringRef WarningsAsErrors)
Provider the enables a specific set of checks and warnings as errors.
Definition: TidyProvider.cpp:187
clang::clangd::combine
TidyProvider combine(std::vector< TidyProvider > Providers)
Definition: TidyProvider.cpp:266
clang::tidy::ClangTidyOptions::getDefaults
static ClangTidyOptions getDefaults()
These options are used for all settings that haven't been overridden by the OptionsProvider.
Definition: ClangTidyOptions.cpp:109
clang::clangd::mergeCheckList
static void mergeCheckList(llvm::Optional< std::string > &Checks, llvm::StringRef List)
Definition: TidyProvider.cpp:141