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