[ELF] Reorder SectionBase/InputSectionBase members
[llvm-project.git] / clang-tools-extra / clang-tidy / ClangTidyOptions.cpp
blob445c7f85c900c66507e271ebabf9c6780fca3bcf
1 //===--- ClangTidyOptions.cpp - clang-tidy ----------------------*- C++ -*-===//
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 //===----------------------------------------------------------------------===//
9 #include "ClangTidyOptions.h"
10 #include "ClangTidyModuleRegistry.h"
11 #include "clang/Basic/LLVM.h"
12 #include "llvm/ADT/SmallString.h"
13 #include "llvm/Support/Debug.h"
14 #include "llvm/Support/Errc.h"
15 #include "llvm/Support/FileSystem.h"
16 #include "llvm/Support/MemoryBufferRef.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/YAMLTraits.h"
19 #include <algorithm>
20 #include <optional>
21 #include <utility>
23 #define DEBUG_TYPE "clang-tidy-options"
25 using clang::tidy::ClangTidyOptions;
26 using clang::tidy::FileFilter;
27 using OptionsSource = clang::tidy::ClangTidyOptionsProvider::OptionsSource;
29 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
30 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
32 namespace llvm::yaml {
34 // Map std::pair<int, int> to a JSON array of size 2.
35 template <> struct SequenceTraits<FileFilter::LineRange> {
36 static size_t size(IO &IO, FileFilter::LineRange &Range) {
37 return Range.first == 0 ? 0 : Range.second == 0 ? 1 : 2;
39 static unsigned &element(IO &IO, FileFilter::LineRange &Range, size_t Index) {
40 if (Index > 1)
41 IO.setError("Too many elements in line range.");
42 return Index == 0 ? Range.first : Range.second;
46 template <> struct MappingTraits<FileFilter> {
47 static void mapping(IO &IO, FileFilter &File) {
48 IO.mapRequired("name", File.Name);
49 IO.mapOptional("lines", File.LineRanges);
51 static std::string validate(IO &Io, FileFilter &File) {
52 if (File.Name.empty())
53 return "No file name specified";
54 for (const FileFilter::LineRange &Range : File.LineRanges) {
55 if (Range.first <= 0 || Range.second <= 0)
56 return "Invalid line range";
58 return "";
62 template <> struct MappingTraits<ClangTidyOptions::StringPair> {
63 static void mapping(IO &IO, ClangTidyOptions::StringPair &KeyValue) {
64 IO.mapRequired("key", KeyValue.first);
65 IO.mapRequired("value", KeyValue.second);
69 struct NOptionMap {
70 NOptionMap(IO &) {}
71 NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap) {
72 Options.reserve(OptionMap.size());
73 for (const auto &KeyValue : OptionMap)
74 Options.emplace_back(std::string(KeyValue.getKey()), KeyValue.getValue().Value);
76 ClangTidyOptions::OptionMap denormalize(IO &) {
77 ClangTidyOptions::OptionMap Map;
78 for (const auto &KeyValue : Options)
79 Map[KeyValue.first] = ClangTidyOptions::ClangTidyValue(KeyValue.second);
80 return Map;
82 std::vector<ClangTidyOptions::StringPair> Options;
85 template <>
86 void yamlize(IO &IO, ClangTidyOptions::OptionMap &Val, bool,
87 EmptyContext &Ctx) {
88 if (IO.outputting()) {
89 // Ensure check options are sorted
90 std::vector<std::pair<StringRef, StringRef>> SortedOptions;
91 SortedOptions.reserve(Val.size());
92 for (auto &Key : Val) {
93 SortedOptions.emplace_back(Key.getKey(), Key.getValue().Value);
95 std::sort(SortedOptions.begin(), SortedOptions.end());
97 IO.beginMapping();
98 // Only output as a map
99 for (auto &Option : SortedOptions) {
100 bool UseDefault = false;
101 void *SaveInfo = nullptr;
102 IO.preflightKey(Option.first.data(), true, false, UseDefault, SaveInfo);
103 IO.scalarString(Option.second, needsQuotes(Option.second));
104 IO.postflightKey(SaveInfo);
106 IO.endMapping();
107 } else {
108 // We need custom logic here to support the old method of specifying check
109 // options using a list of maps containing key and value keys.
110 auto &I = reinterpret_cast<Input &>(IO);
111 if (isa<SequenceNode>(I.getCurrentNode())) {
112 MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(IO,
113 Val);
114 EmptyContext Ctx;
115 yamlize(IO, NOpts->Options, true, Ctx);
116 } else if (isa<MappingNode>(I.getCurrentNode())) {
117 IO.beginMapping();
118 for (StringRef Key : IO.keys()) {
119 IO.mapRequired(Key.data(), Val[Key].Value);
121 IO.endMapping();
122 } else {
123 IO.setError("expected a sequence or map");
128 struct ChecksVariant {
129 std::optional<std::string> AsString;
130 std::optional<std::vector<std::string>> AsVector;
133 template <> void yamlize(IO &IO, ChecksVariant &Val, bool, EmptyContext &Ctx) {
134 if (!IO.outputting()) {
135 // Special case for reading from YAML
136 // Must support reading from both a string or a list
137 auto &I = reinterpret_cast<Input &>(IO);
138 if (isa<ScalarNode, BlockScalarNode>(I.getCurrentNode())) {
139 Val.AsString = std::string();
140 yamlize(IO, *Val.AsString, true, Ctx);
141 } else if (isa<SequenceNode>(I.getCurrentNode())) {
142 Val.AsVector = std::vector<std::string>();
143 yamlize(IO, *Val.AsVector, true, Ctx);
144 } else {
145 IO.setError("expected string or sequence");
150 static void mapChecks(IO &IO, std::optional<std::string> &Checks) {
151 if (IO.outputting()) {
152 // Output always a string
153 IO.mapOptional("Checks", Checks);
154 } else {
155 // Input as either a string or a list
156 ChecksVariant ChecksAsVariant;
157 IO.mapOptional("Checks", ChecksAsVariant);
158 if (ChecksAsVariant.AsString)
159 Checks = ChecksAsVariant.AsString;
160 else if (ChecksAsVariant.AsVector)
161 Checks = llvm::join(*ChecksAsVariant.AsVector, ",");
165 template <> struct MappingTraits<ClangTidyOptions> {
166 static void mapping(IO &IO, ClangTidyOptions &Options) {
167 mapChecks(IO, Options.Checks);
168 IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors);
169 IO.mapOptional("HeaderFileExtensions", Options.HeaderFileExtensions);
170 IO.mapOptional("ImplementationFileExtensions",
171 Options.ImplementationFileExtensions);
172 IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
173 IO.mapOptional("ExcludeHeaderFilterRegex",
174 Options.ExcludeHeaderFilterRegex);
175 IO.mapOptional("FormatStyle", Options.FormatStyle);
176 IO.mapOptional("User", Options.User);
177 IO.mapOptional("CheckOptions", Options.CheckOptions);
178 IO.mapOptional("ExtraArgs", Options.ExtraArgs);
179 IO.mapOptional("ExtraArgsBefore", Options.ExtraArgsBefore);
180 IO.mapOptional("InheritParentConfig", Options.InheritParentConfig);
181 IO.mapOptional("UseColor", Options.UseColor);
182 IO.mapOptional("SystemHeaders", Options.SystemHeaders);
186 } // namespace llvm::yaml
188 namespace clang::tidy {
190 ClangTidyOptions ClangTidyOptions::getDefaults() {
191 ClangTidyOptions Options;
192 Options.Checks = "";
193 Options.WarningsAsErrors = "";
194 Options.HeaderFileExtensions = {"", "h", "hh", "hpp", "hxx"};
195 Options.ImplementationFileExtensions = {"c", "cc", "cpp", "cxx"};
196 Options.HeaderFilterRegex = std::nullopt;
197 Options.ExcludeHeaderFilterRegex = std::nullopt;
198 Options.SystemHeaders = false;
199 Options.FormatStyle = "none";
200 Options.User = std::nullopt;
201 for (const ClangTidyModuleRegistry::entry &Module :
202 ClangTidyModuleRegistry::entries())
203 Options.mergeWith(Module.instantiate()->getModuleOptions(), 0);
204 return Options;
207 template <typename T>
208 static void mergeVectors(std::optional<T> &Dest, const std::optional<T> &Src) {
209 if (Src) {
210 if (Dest)
211 Dest->insert(Dest->end(), Src->begin(), Src->end());
212 else
213 Dest = Src;
217 static void mergeCommaSeparatedLists(std::optional<std::string> &Dest,
218 const std::optional<std::string> &Src) {
219 if (Src)
220 Dest = (Dest && !Dest->empty() ? *Dest + "," : "") + *Src;
223 template <typename T>
224 static void overrideValue(std::optional<T> &Dest, const std::optional<T> &Src) {
225 if (Src)
226 Dest = Src;
229 ClangTidyOptions &ClangTidyOptions::mergeWith(const ClangTidyOptions &Other,
230 unsigned Order) {
231 mergeCommaSeparatedLists(Checks, Other.Checks);
232 mergeCommaSeparatedLists(WarningsAsErrors, Other.WarningsAsErrors);
233 overrideValue(HeaderFileExtensions, Other.HeaderFileExtensions);
234 overrideValue(ImplementationFileExtensions,
235 Other.ImplementationFileExtensions);
236 overrideValue(HeaderFilterRegex, Other.HeaderFilterRegex);
237 overrideValue(ExcludeHeaderFilterRegex, Other.ExcludeHeaderFilterRegex);
238 overrideValue(SystemHeaders, Other.SystemHeaders);
239 overrideValue(FormatStyle, Other.FormatStyle);
240 overrideValue(User, Other.User);
241 overrideValue(UseColor, Other.UseColor);
242 mergeVectors(ExtraArgs, Other.ExtraArgs);
243 mergeVectors(ExtraArgsBefore, Other.ExtraArgsBefore);
245 for (const auto &KeyValue : Other.CheckOptions) {
246 CheckOptions.insert_or_assign(
247 KeyValue.getKey(),
248 ClangTidyValue(KeyValue.getValue().Value,
249 KeyValue.getValue().Priority + Order));
251 return *this;
254 ClangTidyOptions ClangTidyOptions::merge(const ClangTidyOptions &Other,
255 unsigned Order) const {
256 ClangTidyOptions Result = *this;
257 Result.mergeWith(Other, Order);
258 return Result;
261 const char ClangTidyOptionsProvider::OptionsSourceTypeDefaultBinary[] =
262 "clang-tidy binary";
263 const char ClangTidyOptionsProvider::OptionsSourceTypeCheckCommandLineOption[] =
264 "command-line option '-checks'";
265 const char
266 ClangTidyOptionsProvider::OptionsSourceTypeConfigCommandLineOption[] =
267 "command-line option '-config'";
269 ClangTidyOptions
270 ClangTidyOptionsProvider::getOptions(llvm::StringRef FileName) {
271 ClangTidyOptions Result;
272 unsigned Priority = 0;
273 for (auto &Source : getRawOptions(FileName))
274 Result.mergeWith(Source.first, ++Priority);
275 return Result;
278 std::vector<OptionsSource>
279 DefaultOptionsProvider::getRawOptions(llvm::StringRef FileName) {
280 std::vector<OptionsSource> Result;
281 Result.emplace_back(DefaultOptions, OptionsSourceTypeDefaultBinary);
282 return Result;
285 ConfigOptionsProvider::ConfigOptionsProvider(
286 ClangTidyGlobalOptions GlobalOptions, ClangTidyOptions DefaultOptions,
287 ClangTidyOptions ConfigOptions, ClangTidyOptions OverrideOptions,
288 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> FS)
289 : FileOptionsBaseProvider(std::move(GlobalOptions),
290 std::move(DefaultOptions),
291 std::move(OverrideOptions), std::move(FS)),
292 ConfigOptions(std::move(ConfigOptions)) {}
294 std::vector<OptionsSource>
295 ConfigOptionsProvider::getRawOptions(llvm::StringRef FileName) {
296 std::vector<OptionsSource> RawOptions =
297 DefaultOptionsProvider::getRawOptions(FileName);
298 if (ConfigOptions.InheritParentConfig.value_or(false)) {
299 LLVM_DEBUG(llvm::dbgs()
300 << "Getting options for file " << FileName << "...\n");
301 assert(FS && "FS must be set.");
303 llvm::SmallString<128> AbsoluteFilePath(FileName);
305 if (!FS->makeAbsolute(AbsoluteFilePath)) {
306 addRawFileOptions(AbsoluteFilePath, RawOptions);
309 RawOptions.emplace_back(ConfigOptions,
310 OptionsSourceTypeConfigCommandLineOption);
311 RawOptions.emplace_back(OverrideOptions,
312 OptionsSourceTypeCheckCommandLineOption);
313 return RawOptions;
316 FileOptionsBaseProvider::FileOptionsBaseProvider(
317 ClangTidyGlobalOptions GlobalOptions, ClangTidyOptions DefaultOptions,
318 ClangTidyOptions OverrideOptions,
319 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
320 : DefaultOptionsProvider(std::move(GlobalOptions),
321 std::move(DefaultOptions)),
322 OverrideOptions(std::move(OverrideOptions)), FS(std::move(VFS)) {
323 if (!FS)
324 FS = llvm::vfs::getRealFileSystem();
325 ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
328 FileOptionsBaseProvider::FileOptionsBaseProvider(
329 ClangTidyGlobalOptions GlobalOptions, ClangTidyOptions DefaultOptions,
330 ClangTidyOptions OverrideOptions,
331 FileOptionsBaseProvider::ConfigFileHandlers ConfigHandlers)
332 : DefaultOptionsProvider(std::move(GlobalOptions),
333 std::move(DefaultOptions)),
334 OverrideOptions(std::move(OverrideOptions)),
335 ConfigHandlers(std::move(ConfigHandlers)) {}
337 void FileOptionsBaseProvider::addRawFileOptions(
338 llvm::StringRef AbsolutePath, std::vector<OptionsSource> &CurOptions) {
339 auto CurSize = CurOptions.size();
341 // Look for a suitable configuration file in all parent directories of the
342 // file. Start with the immediate parent directory and move up.
343 StringRef Path = llvm::sys::path::parent_path(AbsolutePath);
344 for (StringRef CurrentPath = Path; !CurrentPath.empty();
345 CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
346 std::optional<OptionsSource> Result;
348 auto Iter = CachedOptions.find(CurrentPath);
349 if (Iter != CachedOptions.end())
350 Result = Iter->second;
352 if (!Result)
353 Result = tryReadConfigFile(CurrentPath);
355 if (Result) {
356 // Store cached value for all intermediate directories.
357 while (Path != CurrentPath) {
358 LLVM_DEBUG(llvm::dbgs()
359 << "Caching configuration for path " << Path << ".\n");
360 if (!CachedOptions.count(Path))
361 CachedOptions[Path] = *Result;
362 Path = llvm::sys::path::parent_path(Path);
364 CachedOptions[Path] = *Result;
366 CurOptions.push_back(*Result);
367 if (!Result->first.InheritParentConfig.value_or(false))
368 break;
371 // Reverse order of file configs because closer configs should have higher
372 // priority.
373 std::reverse(CurOptions.begin() + CurSize, CurOptions.end());
376 FileOptionsProvider::FileOptionsProvider(
377 ClangTidyGlobalOptions GlobalOptions, ClangTidyOptions DefaultOptions,
378 ClangTidyOptions OverrideOptions,
379 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
380 : FileOptionsBaseProvider(std::move(GlobalOptions),
381 std::move(DefaultOptions),
382 std::move(OverrideOptions), std::move(VFS)) {}
384 FileOptionsProvider::FileOptionsProvider(
385 ClangTidyGlobalOptions GlobalOptions, ClangTidyOptions DefaultOptions,
386 ClangTidyOptions OverrideOptions,
387 FileOptionsBaseProvider::ConfigFileHandlers ConfigHandlers)
388 : FileOptionsBaseProvider(
389 std::move(GlobalOptions), std::move(DefaultOptions),
390 std::move(OverrideOptions), std::move(ConfigHandlers)) {}
392 // FIXME: This method has some common logic with clang::format::getStyle().
393 // Consider pulling out common bits to a findParentFileWithName function or
394 // similar.
395 std::vector<OptionsSource>
396 FileOptionsProvider::getRawOptions(StringRef FileName) {
397 LLVM_DEBUG(llvm::dbgs() << "Getting options for file " << FileName
398 << "...\n");
399 assert(FS && "FS must be set.");
401 llvm::SmallString<128> AbsoluteFilePath(FileName);
403 if (FS->makeAbsolute(AbsoluteFilePath))
404 return {};
406 std::vector<OptionsSource> RawOptions =
407 DefaultOptionsProvider::getRawOptions(AbsoluteFilePath.str());
408 addRawFileOptions(AbsoluteFilePath, RawOptions);
409 OptionsSource CommandLineOptions(OverrideOptions,
410 OptionsSourceTypeCheckCommandLineOption);
412 RawOptions.push_back(CommandLineOptions);
413 return RawOptions;
416 std::optional<OptionsSource>
417 FileOptionsBaseProvider::tryReadConfigFile(StringRef Directory) {
418 assert(!Directory.empty());
420 llvm::ErrorOr<llvm::vfs::Status> DirectoryStatus = FS->status(Directory);
422 if (!DirectoryStatus || !DirectoryStatus->isDirectory()) {
423 llvm::errs() << "Error reading configuration from " << Directory
424 << ": directory doesn't exist.\n";
425 return std::nullopt;
428 for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
429 SmallString<128> ConfigFile(Directory);
430 llvm::sys::path::append(ConfigFile, ConfigHandler.first);
431 LLVM_DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
433 llvm::ErrorOr<llvm::vfs::Status> FileStatus = FS->status(ConfigFile);
435 if (!FileStatus || !FileStatus->isRegularFile())
436 continue;
438 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
439 FS->getBufferForFile(ConfigFile);
440 if (std::error_code EC = Text.getError()) {
441 llvm::errs() << "Can't read " << ConfigFile << ": " << EC.message()
442 << "\n";
443 continue;
446 // Skip empty files, e.g. files opened for writing via shell output
447 // redirection.
448 if ((*Text)->getBuffer().empty())
449 continue;
450 llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
451 ConfigHandler.second({(*Text)->getBuffer(), ConfigFile});
452 if (!ParsedOptions) {
453 if (ParsedOptions.getError())
454 llvm::errs() << "Error parsing " << ConfigFile << ": "
455 << ParsedOptions.getError().message() << "\n";
456 continue;
458 return OptionsSource(*ParsedOptions, std::string(ConfigFile));
460 return std::nullopt;
463 /// Parses -line-filter option and stores it to the \c Options.
464 std::error_code parseLineFilter(StringRef LineFilter,
465 clang::tidy::ClangTidyGlobalOptions &Options) {
466 llvm::yaml::Input Input(LineFilter);
467 Input >> Options.LineFilter;
468 return Input.error();
471 llvm::ErrorOr<ClangTidyOptions>
472 parseConfiguration(llvm::MemoryBufferRef Config) {
473 llvm::yaml::Input Input(Config);
474 ClangTidyOptions Options;
475 Input >> Options;
476 if (Input.error())
477 return Input.error();
478 return Options;
481 static void diagHandlerImpl(const llvm::SMDiagnostic &Diag, void *Ctx) {
482 (*reinterpret_cast<DiagCallback *>(Ctx))(Diag);
485 llvm::ErrorOr<ClangTidyOptions>
486 parseConfigurationWithDiags(llvm::MemoryBufferRef Config,
487 DiagCallback Handler) {
488 llvm::yaml::Input Input(Config, nullptr, Handler ? diagHandlerImpl : nullptr,
489 &Handler);
490 ClangTidyOptions Options;
491 Input >> Options;
492 if (Input.error())
493 return Input.error();
494 return Options;
497 std::string configurationAsText(const ClangTidyOptions &Options) {
498 std::string Text;
499 llvm::raw_string_ostream Stream(Text);
500 llvm::yaml::Output Output(Stream);
501 // We use the same mapping method for input and output, so we need a non-const
502 // reference here.
503 ClangTidyOptions NonConstValue = Options;
504 Output << NonConstValue;
505 return Stream.str();
508 } // namespace clang::tidy