11#include "clang/Basic/DiagnosticIDs.h"
12#include "clang/Basic/LLVM.h"
13#include "llvm/ADT/SmallString.h"
14#include "llvm/ADT/StringExtras.h"
15#include "llvm/Support/Debug.h"
16#include "llvm/Support/ErrorOr.h"
17#include "llvm/Support/MemoryBufferRef.h"
18#include "llvm/Support/Path.h"
19#include "llvm/Support/YAMLTraits.h"
24#define DEBUG_TYPE "clang-tidy-options"
36template <>
struct SequenceTraits<
FileFilter::LineRange> {
38 return Range.first == 0 ? 0 : Range.second == 0 ? 1 : 2;
42 IO.setError(
"Too many elements in line range.");
43 return Index == 0 ? Range.first : Range.second;
49 IO.mapRequired(
"name", File.Name);
50 IO.mapOptional(
"lines", File.LineRanges);
53 if (File.Name.empty())
54 return "No file name specified";
56 if (Range.first <= 0 || Range.second <= 0)
57 return "Invalid line range";
65 IO.mapRequired(
"key", KeyValue.first);
66 IO.mapRequired(
"value", KeyValue.second);
73 Options.reserve(OptionMap.size());
74 for (
const auto &KeyValue : OptionMap)
75 Options.emplace_back(std::string(KeyValue.getKey()),
76 KeyValue.getValue().Value);
80 for (
const auto &KeyValue :
Options)
84 std::vector<ClangTidyOptions::StringPair>
Options;
90 if (IO.outputting()) {
92 std::vector<std::pair<StringRef, StringRef>> SortedOptions;
93 SortedOptions.reserve(Val.size());
94 for (
auto &Key : Val) {
95 SortedOptions.emplace_back(Key.getKey(), Key.getValue().Value);
97 std::sort(SortedOptions.begin(), SortedOptions.end());
101 for (
auto &Option : SortedOptions) {
102 bool UseDefault =
false;
103 void *SaveInfo =
nullptr;
106 IO.preflightKey(Option.first.data(),
true,
false, UseDefault, SaveInfo);
107 IO.scalarString(Option.second, needsQuotes(Option.second));
108 IO.postflightKey(SaveInfo);
114 auto &I =
reinterpret_cast<Input &
>(IO);
115 if (isa<SequenceNode>(I.getCurrentNode())) {
116 MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(IO,
119 yamlize(IO, NOpts->Options,
true, Ctx);
120 }
else if (isa<MappingNode>(I.getCurrentNode())) {
122 for (StringRef Key : IO.keys()) {
125 IO.mapRequired(Key.data(), Val[Key].Value);
129 IO.setError(
"expected a sequence or map");
135struct MultiLineString {
140template <>
struct BlockScalarTraits<MultiLineString> {
141 static void output(
const MultiLineString &S,
void *Ctxt, raw_ostream &OS) {
144 static StringRef
input(StringRef Str,
void *Ctxt, MultiLineString &S) {
150template <>
struct ScalarEnumerationTraits<
clang::DiagnosticIDs::Level> {
151 static void enumeration(IO &IO, clang::DiagnosticIDs::Level &Level) {
152 IO.enumCase(Level,
"Warning", clang::DiagnosticIDs::Level::Warning);
153 IO.enumCase(Level,
"Note", clang::DiagnosticIDs::Level::Note);
158 static const bool flow =
false;
162 IO.mapRequired(
"BindName", D.BindName);
163 MultiLineString MLS{D.Message};
164 IO.mapRequired(
"Message", MLS);
165 IO.mapOptional(
"Level", D.Level);
170 static const bool flow =
false;
174 IO.mapRequired(
"Name", V.
Name);
175 MultiLineString MLS{V.
Query};
176 IO.mapRequired(
"Query", MLS);
177 IO.mapRequired(
"Diagnostic", V.
Diags);
187 if (!IO.outputting()) {
190 auto &I =
reinterpret_cast<Input &
>(IO);
191 if (isa<ScalarNode, BlockScalarNode>(I.getCurrentNode())) {
194 }
else if (isa<SequenceNode>(I.getCurrentNode())) {
195 Val.
AsVector = std::vector<std::string>();
198 IO.setError(
"expected string or sequence");
204 if (IO.outputting()) {
206 IO.mapOptional(
"Checks",
Checks);
210 IO.mapOptional(
"Checks", ChecksAsVariant);
223 IO.mapOptional(
"ImplementationFileExtensions",
226 IO.mapOptional(
"ExcludeHeaderFilterRegex",
228 IO.mapOptional(
"FormatStyle", Options.
FormatStyle);
229 IO.mapOptional(
"User", Options.
User);
231 IO.mapOptional(
"ExtraArgs", Options.
ExtraArgs);
234 IO.mapOptional(
"UseColor", Options.
UseColor);
254 Options.
User = std::nullopt;
255 for (
const ClangTidyModuleRegistry::entry &Module :
256 ClangTidyModuleRegistry::entries())
257 Options.
mergeWith(Module.instantiate()->getModuleOptions(), 0);
262static void mergeVectors(std::optional<T> &Dest,
const std::optional<T> &Src) {
265 Dest->insert(Dest->end(), Src->begin(), Src->end());
272 const std::optional<std::string> &Src) {
274 Dest = (Dest && !Dest->empty() ? *Dest +
"," :
"") + *Src;
278static void overrideValue(std::optional<T> &Dest,
const std::optional<T> &Src) {
304 KeyValue.getValue().Priority + Order));
310 unsigned Order)
const {
319 "command-line option '-checks'";
322 "command-line option '-config'";
327 unsigned Priority = 0;
329 Result.
mergeWith(Source.first, ++Priority);
333std::vector<OptionsSource>
335 std::vector<OptionsSource> Result;
343 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem>
FS)
345 std::
move(DefaultOptions),
347 ConfigOptions(std::
move(ConfigOptions)) {}
349std::vector<OptionsSource>
351 std::vector<OptionsSource> RawOptions =
353 if (ConfigOptions.InheritParentConfig.value_or(
false)) {
354 LLVM_DEBUG(llvm::dbgs()
355 <<
"Getting options for file " << FileName <<
"...\n");
357 llvm::ErrorOr<llvm::SmallString<128>> AbsoluteFilePath =
359 if (AbsoluteFilePath) {
363 RawOptions.emplace_back(ConfigOptions,
373 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
375 std::
move(DefaultOptions)),
378 FS = llvm::vfs::getRealFileSystem();
387 std::
move(DefaultOptions)),
391llvm::ErrorOr<llvm::SmallString<128>>
393 assert(
FS &&
"FS must be set.");
394 llvm::SmallString<128> NormalizedAbsolutePath = {Path};
395 std::error_code Err =
FS->makeAbsolute(NormalizedAbsolutePath);
398 llvm::sys::path::remove_dots(NormalizedAbsolutePath,
true);
399 return NormalizedAbsolutePath;
403 llvm::StringRef AbsolutePath, std::vector<OptionsSource> &CurOptions) {
404 auto CurSize = CurOptions.size();
407 StringRef RootPath = llvm::sys::path::parent_path(AbsolutePath);
408 auto MemorizedConfigFile =
409 [
this, &RootPath](StringRef CurrentPath) -> std::optional<OptionsSource> {
417 while (RootPath != CurrentPath) {
418 LLVM_DEBUG(llvm::dbgs()
419 <<
"Caching configuration for path " << RootPath <<
".\n");
421 RootPath = llvm::sys::path::parent_path(RootPath);
424 RootPath = llvm::sys::path::parent_path(CurrentPath);
428 for (StringRef CurrentPath = RootPath; !CurrentPath.empty();
429 CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
430 if (std::optional<OptionsSource> Result =
431 MemorizedConfigFile(CurrentPath)) {
432 CurOptions.emplace_back(Result.value());
433 if (!Result->first.InheritParentConfig.value_or(
false))
439 std::reverse(CurOptions.begin() + CurSize, CurOptions.end());
445 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
447 std::
move(DefaultOptions),
455 std::
move(GlobalOptions), std::
move(DefaultOptions),
461std::vector<OptionsSource>
463 LLVM_DEBUG(llvm::dbgs() <<
"Getting options for file " << FileName
466 llvm::ErrorOr<llvm::SmallString<128>> AbsoluteFilePath =
468 if (!AbsoluteFilePath)
471 std::vector<OptionsSource> RawOptions =
477 RawOptions.push_back(CommandLineOptions);
481std::optional<OptionsSource>
485 llvm::ErrorOr<llvm::vfs::Status> DirectoryStatus =
FS->status(
Directory);
487 if (!DirectoryStatus || !DirectoryStatus->isDirectory()) {
488 llvm::errs() <<
"Error reading configuration from " <<
Directory
489 <<
": directory doesn't exist.\n";
495 llvm::sys::path::append(
ConfigFile, ConfigHandler.first);
496 LLVM_DEBUG(llvm::dbgs() <<
"Trying " <<
ConfigFile <<
"...\n");
498 llvm::ErrorOr<llvm::vfs::Status> FileStatus =
FS->status(
ConfigFile);
500 if (!FileStatus || !FileStatus->isRegularFile())
503 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
505 if (std::error_code EC = Text.getError()) {
506 llvm::errs() <<
"Can't read " <<
ConfigFile <<
": " << EC.message()
513 if ((*Text)->getBuffer().empty())
515 llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
516 ConfigHandler.second({(*Text)->getBuffer(),
ConfigFile});
517 if (!ParsedOptions) {
518 if (ParsedOptions.getError())
519 llvm::errs() <<
"Error parsing " <<
ConfigFile <<
": "
520 << ParsedOptions.getError().message() <<
"\n";
533 return Input.error();
536llvm::ErrorOr<ClangTidyOptions>
538 llvm::yaml::Input Input(
Config);
542 return Input.error();
550llvm::ErrorOr<ClangTidyOptions>
558 return Input.error();
564 llvm::raw_string_ostream Stream(Text);
565 llvm::yaml::Output Output(Stream);
569 Output << NonConstValue;
static cl::opt< std::string > Directory(cl::Positional, cl::Required, cl::desc("<Search Root Directory>"))
static cl::opt< std::string > ConfigFile("config-file", desc(R"(
Specify the path of .clang-tidy or custom config file:
e.g. --config-file=/some/path/myTidyConfigFile
This option internally works exactly the same way as
--config option after reading specified config file.
Use either --config-file or --config, not both.
)"), cl::init(""), cl::cat(ClangTidyCategory))
static cl::opt< std::string > Config("config", desc(R"(
Specifies a configuration in YAML/JSON format:
-config="{Checks:' *', CheckOptions:{x: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))
static cl::opt< std::string > LineFilter("line-filter", desc(R"(
List of files and line ranges to output diagnostics from.
The range is inclusive on both ends. Can be used together
with -header-filter. The format of the list is a JSON
array of objects. For example:
[
{"name":"file1.cpp","lines":[[1,3],[5,7]]},
{"name":"file2.h"}
]
This will output diagnostics from 'file1.cpp' only for
the line ranges [1,3] and [5,7], as well as all from the
entire 'file2.h'.
)"), cl::init(""), cl::cat(ClangTidyCategory))
static cl::opt< std::string > Checks("checks", 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))
clang::tidy::ClangTidyOptionsProvider::OptionsSource OptionsSource
ClangTidyOptions getOptions(llvm::StringRef FileName)
Returns options applying to a specific translation unit with the specified FileName.
static const char OptionsSourceTypeCheckCommandLineOption[]
virtual std::vector< OptionsSource > getRawOptions(llvm::StringRef FileName)=0
Returns an ordered vector of OptionsSources, in order of increasing priority.
std::pair< ClangTidyOptions, std::string > OptionsSource
ClangTidyOptions and its source.
static const char OptionsSourceTypeConfigCommandLineOption[]
static const char OptionsSourceTypeDefaultBinary[]
ConfigOptionsProvider(ClangTidyGlobalOptions GlobalOptions, ClangTidyOptions DefaultOptions, ClangTidyOptions ConfigOptions, ClangTidyOptions OverrideOptions, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS=nullptr)
std::vector< OptionsSource > getRawOptions(llvm::StringRef FileName) override
Returns an ordered vector of OptionsSources, in order of increasing priority.
DefaultOptionsProvider(ClangTidyGlobalOptions GlobalOptions, ClangTidyOptions Options)
std::vector< OptionsSource > getRawOptions(llvm::StringRef FileName) override
Returns an ordered vector of OptionsSources, in order of increasing priority.
struct clang::tidy::FileOptionsBaseProvider::OptionsCache CachedOptions
std::pair< std::string, std::function< llvm::ErrorOr< ClangTidyOptions >( llvm::MemoryBufferRef)> > ConfigFileHandler
ClangTidyOptions OverrideOptions
llvm::ErrorOr< llvm::SmallString< 128 > > getNormalizedAbsolutePath(llvm::StringRef AbsolutePath)
std::optional< OptionsSource > tryReadConfigFile(llvm::StringRef Directory)
Try to read configuration files from Directory using registered ConfigHandlers.
ConfigFileHandlers ConfigHandlers
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS
FileOptionsBaseProvider(ClangTidyGlobalOptions GlobalOptions, ClangTidyOptions DefaultOptions, ClangTidyOptions OverrideOptions, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS)
std::vector< ConfigFileHandler > ConfigFileHandlers
Configuration file handlers listed in the order of priority.
void addRawFileOptions(llvm::StringRef AbsolutePath, std::vector< OptionsSource > &CurOptions)
std::vector< OptionsSource > getRawOptions(llvm::StringRef FileName) override
Returns an ordered vector of OptionsSources, in order of increasing priority.
FileOptionsProvider(ClangTidyGlobalOptions GlobalOptions, ClangTidyOptions DefaultOptions, ClangTidyOptions OverrideOptions, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS=nullptr)
Initializes the FileOptionsProvider instance.
std::error_code parseLineFilter(StringRef LineFilter, clang::tidy::ClangTidyGlobalOptions &Options)
Parses -line-filter option and stores it to the Options.
static void diagHandlerImpl(const llvm::SMDiagnostic &Diag, void *Ctx)
static void mergeVectors(std::optional< T > &Dest, const std::optional< T > &Src)
llvm::ErrorOr< ClangTidyOptions > parseConfigurationWithDiags(llvm::MemoryBufferRef Config, DiagCallback Handler)
llvm::function_ref< void(const llvm::SMDiagnostic &)> DiagCallback
static void mergeCommaSeparatedLists(std::optional< std::string > &Dest, const std::optional< std::string > &Src)
std::string configurationAsText(const ClangTidyOptions &Options)
Serializes configuration to a YAML-encoded string.
llvm::ErrorOr< ClangTidyOptions > parseConfiguration(llvm::MemoryBufferRef Config)
Parses configuration from JSON and returns ClangTidyOptions or an error.
static void overrideValue(std::optional< T > &Dest, const std::optional< T > &Src)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
static void mapChecks(IO &IO, std::optional< std::string > &Checks)
void yamlize(IO &IO, ClangTidyOptions::OptionMap &Val, bool, EmptyContext &Ctx)
Helper structure for storing option value with priority of the value.
llvm::SmallVector< CustomCheckDiag > Diags
std::pair< std::string, std::string > StringPair
llvm::StringMap< ClangTidyValue > OptionMap
std::pair< unsigned int, unsigned int > LineRange
LineRange is a pair<start, end> (inclusive).
std::vector< FileFilter > LineFilter
Output warnings from certain line ranges of certain files only.
Helper structure for storing option value with priority of the value.
Contains options for clang-tidy.
OptionMap CheckOptions
Key-value mapping used to store check-specific options.
ClangTidyOptions merge(const ClangTidyOptions &Other, unsigned Order) const
Creates a new ClangTidyOptions instance combined from all fields of this instance overridden by the f...
std::optional< bool > InheritParentConfig
Only used in the FileOptionsProvider and ConfigOptionsProvider.
std::optional< std::string > HeaderFilterRegex
Output warnings from headers matching this filter.
std::optional< std::string > Checks
Checks filter.
std::optional< std::string > WarningsAsErrors
WarningsAsErrors filter.
std::optional< std::vector< std::string > > ImplementationFileExtensions
File extensions to consider to determine if a given diagnostic is located is located in an implementa...
ClangTidyOptions & mergeWith(const ClangTidyOptions &Other, unsigned Order)
Overwrites all fields in here by the fields of Other that have a value.
std::optional< std::string > User
Specifies the name or e-mail of the user running clang-tidy.
std::optional< std::vector< std::string > > HeaderFileExtensions
File extensions to consider to determine if a given diagnostic is located in a header file.
std::optional< bool > UseColor
Use colors in diagnostics. If missing, it will be auto detected.
std::optional< bool > SystemHeaders
Output warnings from system headers matching HeaderFilterRegex.
static ClangTidyOptions getDefaults()
These options are used for all settings that haven't been overridden by the OptionsProvider.
std::optional< CustomCheckValueList > CustomChecks
std::optional< std::string > ExcludeHeaderFilterRegex
Exclude warnings from headers matching this filter, even if they match HeaderFilterRegex.
std::optional< ArgList > ExtraArgsBefore
Add extra compilation arguments to the start of the list.
std::optional< std::string > FormatStyle
Format code around applied fixes with clang-format using this style.
std::optional< ArgList > ExtraArgs
Add extra compilation arguments to the end of the list.
Contains a list of line ranges in a single file.
static void output(const MultiLineString &S, void *Ctxt, raw_ostream &OS)
static StringRef input(StringRef Str, void *Ctxt, MultiLineString &S)
std::optional< std::string > AsString
std::optional< std::vector< std::string > > AsVector
static void mapping(IO &IO, ClangTidyOptions &Options)
static void mapping(IO &IO, ClangTidyOptions::CustomCheckDiag &D)
static void mapping(IO &IO, ClangTidyOptions::CustomCheckValue &V)
static void mapping(IO &IO, ClangTidyOptions::StringPair &KeyValue)
static std::string validate(IO &Io, FileFilter &File)
static void mapping(IO &IO, FileFilter &File)
ClangTidyOptions::OptionMap denormalize(IO &)
NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap)
std::vector< ClangTidyOptions::StringPair > Options
static void enumeration(IO &IO, clang::DiagnosticIDs::Level &Level)
static unsigned & element(IO &IO, FileFilter::LineRange &Range, size_t Index)
static size_t size(IO &IO, FileFilter::LineRange &Range)