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