clang-tools  3.9.0
ClangTidyOptions.cpp
Go to the documentation of this file.
1 //===--- ClangTidyOptions.cpp - clang-tidy ----------------------*- C++ -*-===//
2 //
3 // The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 
10 #include "ClangTidyOptions.h"
12 #include "clang/Basic/LLVM.h"
13 #include "llvm/ADT/SmallString.h"
14 #include "llvm/Support/Debug.h"
15 #include "llvm/Support/Errc.h"
16 #include "llvm/Support/FileSystem.h"
17 #include "llvm/Support/Path.h"
18 #include "llvm/Support/YAMLTraits.h"
19 #include "llvm/Support/raw_ostream.h"
20 #include <utility>
21 
22 #define DEBUG_TYPE "clang-tidy-options"
23 
27 
28 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
29 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
30 LLVM_YAML_IS_SEQUENCE_VECTOR(ClangTidyOptions::StringPair)
31 LLVM_YAML_IS_SEQUENCE_VECTOR(std::string)
32 
33 namespace llvm {
34 namespace yaml {
35 
36 // Map std::pair<int, int> to a JSON array of size 2.
37 template <> struct SequenceTraits<FileFilter::LineRange> {
38  static size_t size(IO &IO, FileFilter::LineRange &Range) {
39  return Range.first == 0 ? 0 : Range.second == 0 ? 1 : 2;
40  }
41  static unsigned &element(IO &IO, FileFilter::LineRange &Range, size_t Index) {
42  if (Index > 1)
43  IO.setError("Too many elements in line range.");
44  return Index == 0 ? Range.first : Range.second;
45  }
46 };
47 
48 template <> struct MappingTraits<FileFilter> {
49  static void mapping(IO &IO, FileFilter &File) {
50  IO.mapRequired("name", File.Name);
51  IO.mapOptional("lines", File.LineRanges);
52  }
53  static StringRef validate(IO &io, FileFilter &File) {
54  if (File.Name.empty())
55  return "No file name specified";
56  for (const FileFilter::LineRange &Range : File.LineRanges) {
57  if (Range.first <= 0 || Range.second <= 0)
58  return "Invalid line range";
59  }
60  return StringRef();
61  }
62 };
63 
64 template <> struct MappingTraits<ClangTidyOptions::StringPair> {
65  static void mapping(IO &IO, ClangTidyOptions::StringPair &KeyValue) {
66  IO.mapRequired("key", KeyValue.first);
67  IO.mapRequired("value", KeyValue.second);
68  }
69 };
70 
71 struct NOptionMap {
72  NOptionMap(IO &) {}
73  NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap)
74  : Options(OptionMap.begin(), OptionMap.end()) {}
75  ClangTidyOptions::OptionMap denormalize(IO &) {
76  ClangTidyOptions::OptionMap Map;
77  for (const auto &KeyValue : Options)
78  Map[KeyValue.first] = KeyValue.second;
79  return Map;
80  }
81  std::vector<ClangTidyOptions::StringPair> Options;
82 };
83 
84 template <> struct MappingTraits<ClangTidyOptions> {
85  static void mapping(IO &IO, ClangTidyOptions &Options) {
86  MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
87  IO, Options.CheckOptions);
88  IO.mapOptional("Checks", Options.Checks);
89  IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors);
90  IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
91  IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
92  IO.mapOptional("User", Options.User);
93  IO.mapOptional("CheckOptions", NOpts->Options);
94  IO.mapOptional("ExtraArgs", Options.ExtraArgs);
95  IO.mapOptional("ExtraArgsBefore", Options.ExtraArgsBefore);
96  }
97 };
98 
99 } // namespace yaml
100 } // namespace llvm
101 
102 namespace clang {
103 namespace tidy {
104 
106  ClangTidyOptions Options;
107  Options.Checks = "";
108  Options.WarningsAsErrors = "";
109  Options.HeaderFilterRegex = "";
110  Options.SystemHeaders = false;
111  Options.AnalyzeTemporaryDtors = false;
112  Options.User = llvm::None;
113  for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
114  E = ClangTidyModuleRegistry::end();
115  I != E; ++I)
116  Options = Options.mergeWith(I->instantiate()->getModuleOptions());
117  return Options;
118 }
119 
122  ClangTidyOptions Result = *this;
123 
124  // Merge comma-separated glob lists by appending the new value after a comma.
125  if (Other.Checks)
126  Result.Checks =
127  (Result.Checks && !Result.Checks->empty() ? *Result.Checks + "," : "") +
128  *Other.Checks;
129  if (Other.WarningsAsErrors)
130  Result.WarningsAsErrors =
131  (Result.WarningsAsErrors && !Result.WarningsAsErrors->empty()
132  ? *Result.WarningsAsErrors + ","
133  : "") +
134  *Other.WarningsAsErrors;
135 
136  if (Other.HeaderFilterRegex)
137  Result.HeaderFilterRegex = Other.HeaderFilterRegex;
138  if (Other.SystemHeaders)
139  Result.SystemHeaders = Other.SystemHeaders;
140  if (Other.AnalyzeTemporaryDtors)
142  if (Other.User)
143  Result.User = Other.User;
144  if (Other.ExtraArgs)
145  Result.ExtraArgs = Other.ExtraArgs;
146  if (Other.ExtraArgsBefore)
147  Result.ExtraArgsBefore = Other.ExtraArgsBefore;
148 
149  for (const auto &KeyValue : Other.CheckOptions)
150  Result.CheckOptions[KeyValue.first] = KeyValue.second;
151 
152  return Result;
153 }
154 
156  "clang-tidy binary";
158  "command-line option '-checks'";
159 const char
161  "command-line option '-config'";
162 
164 ClangTidyOptionsProvider::getOptions(llvm::StringRef FileName) {
166  for (const auto &Source : getRawOptions(FileName))
167  Result = Result.mergeWith(Source.first);
168  return Result;
169 }
170 
171 std::vector<OptionsSource>
172 DefaultOptionsProvider::getRawOptions(llvm::StringRef FileName) {
173  std::vector<OptionsSource> Result;
174  Result.emplace_back(DefaultOptions, OptionsSourceTypeDefaultBinary);
175  return Result;
176 }
177 
179  const ClangTidyGlobalOptions &GlobalOptions,
180  const ClangTidyOptions &DefaultOptions,
181  const ClangTidyOptions &ConfigOptions,
182  const ClangTidyOptions &OverrideOptions)
183  : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
184  ConfigOptions(ConfigOptions), OverrideOptions(OverrideOptions) {}
185 
186 std::vector<OptionsSource>
187 ConfigOptionsProvider::getRawOptions(llvm::StringRef FileName) {
188  std::vector<OptionsSource> RawOptions =
190  RawOptions.emplace_back(ConfigOptions,
192  RawOptions.emplace_back(OverrideOptions,
194  return RawOptions;
195 }
196 
198  const ClangTidyGlobalOptions &GlobalOptions,
199  const ClangTidyOptions &DefaultOptions,
200  const ClangTidyOptions &OverrideOptions)
201  : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
202  OverrideOptions(OverrideOptions) {
203  ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
204 }
205 
207  const ClangTidyGlobalOptions &GlobalOptions,
208  const ClangTidyOptions &DefaultOptions,
209  const ClangTidyOptions &OverrideOptions,
210  const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
211  : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
212  OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {
213 }
214 
215 // FIXME: This method has some common logic with clang::format::getStyle().
216 // Consider pulling out common bits to a findParentFileWithName function or
217 // similar.
218 std::vector<OptionsSource>
220  DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n");
221 
222  std::vector<OptionsSource> RawOptions =
224  OptionsSource CommandLineOptions(OverrideOptions,
226  // Look for a suitable configuration file in all parent directories of the
227  // file. Start with the immediate parent directory and move up.
228  StringRef Path = llvm::sys::path::parent_path(FileName);
229  for (StringRef CurrentPath = Path; !CurrentPath.empty();
230  CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
231  llvm::Optional<OptionsSource> Result;
232 
233  auto Iter = CachedOptions.find(CurrentPath);
234  if (Iter != CachedOptions.end())
235  Result = Iter->second;
236 
237  if (!Result)
238  Result = tryReadConfigFile(CurrentPath);
239 
240  if (Result) {
241  // Store cached value for all intermediate directories.
242  while (Path != CurrentPath) {
243  DEBUG(llvm::dbgs() << "Caching configuration for path " << Path
244  << ".\n");
246  Path = llvm::sys::path::parent_path(Path);
247  }
249 
250  RawOptions.push_back(*Result);
251  break;
252  }
253  }
254  RawOptions.push_back(CommandLineOptions);
255  return RawOptions;
256 }
257 
258 llvm::Optional<OptionsSource>
260  assert(!Directory.empty());
261 
262  if (!llvm::sys::fs::is_directory(Directory)) {
263  llvm::errs() << "Error reading configuration from " << Directory
264  << ": directory doesn't exist.\n";
265  return llvm::None;
266  }
267 
268  for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
269  SmallString<128> ConfigFile(Directory);
270  llvm::sys::path::append(ConfigFile, ConfigHandler.first);
271  DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
272 
273  bool IsFile = false;
274  // Ignore errors from is_regular_file: we only need to know if we can read
275  // the file or not.
276  llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
277  if (!IsFile)
278  continue;
279 
280  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
281  llvm::MemoryBuffer::getFile(ConfigFile.c_str());
282  if (std::error_code EC = Text.getError()) {
283  llvm::errs() << "Can't read " << ConfigFile << ": " << EC.message()
284  << "\n";
285  continue;
286  }
287 
288  // Skip empty files, e.g. files opened for writing via shell output
289  // redirection.
290  if ((*Text)->getBuffer().empty())
291  continue;
292  llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
293  ConfigHandler.second((*Text)->getBuffer());
294  if (!ParsedOptions) {
295  if (ParsedOptions.getError())
296  llvm::errs() << "Error parsing " << ConfigFile << ": "
297  << ParsedOptions.getError().message() << "\n";
298  continue;
299  }
300  return OptionsSource(*ParsedOptions, ConfigFile.c_str());
301  }
302  return llvm::None;
303 }
304 
305 /// \brief Parses -line-filter option and stores it to the \c Options.
306 std::error_code parseLineFilter(StringRef LineFilter,
308  llvm::yaml::Input Input(LineFilter);
309  Input >> Options.LineFilter;
310  return Input.error();
311 }
312 
313 llvm::ErrorOr<ClangTidyOptions> parseConfiguration(StringRef Config) {
314  llvm::yaml::Input Input(Config);
315  ClangTidyOptions Options;
316  Input >> Options;
317  if (Input.error())
318  return Input.error();
319  return Options;
320 }
321 
322 std::string configurationAsText(const ClangTidyOptions &Options) {
323  std::string Text;
324  llvm::raw_string_ostream Stream(Text);
325  llvm::yaml::Output Output(Stream);
326  // We use the same mapping method for input and output, so we need a non-const
327  // reference here.
328  ClangTidyOptions NonConstValue = Options;
329  Output << NonConstValue;
330  return Stream.str();
331 }
332 
333 } // namespace tidy
334 } // namespace clang
llvm::Optional< std::string > Checks
Checks filter.
std::vector< OptionsSource > getRawOptions(llvm::StringRef FileName) override
Returns an ordered vector of OptionsSources, in order of increasing priority.
llvm::Optional< ArgList > ExtraArgs
Add extra compilation arguments to the end of the list.
llvm::Optional< std::string > User
Specifies the name or e-mail of the user running clang-tidy.
virtual std::vector< OptionsSource > getRawOptions(llvm::StringRef FileName)=0
Returns an ordered vector of OptionsSources, in order of increasing priority.
HeaderHandle File
llvm::Optional< std::string > HeaderFilterRegex
Output warnings from headers matching this filter.
static void mapping(IO &IO, FileFilter &File)
static const char OptionsSourceTypeCheckCommandLineOption[]
Contains options for clang-tidy.
static const char OptionsSourceTypeConfigCommandLineOption[]
std::vector< OptionsSource > getRawOptions(llvm::StringRef FileName) override
Returns an ordered vector of OptionsSources, in order of increasing priority.
std::error_code parseLineFilter(StringRef LineFilter, clang::tidy::ClangTidyGlobalOptions &Options)
Parses -line-filter option and stores it to the Options.
std::vector< HeaderHandle > Path
llvm::ErrorOr< ClangTidyOptions > parseConfiguration(StringRef Config)
static StringRef validate(IO &io, FileFilter &File)
NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap)
OptionMap CheckOptions
Key-value mapping used to store check-specific options.
llvm::Optional< bool > SystemHeaders
Output warnings from system headers matching HeaderFilterRegex.
llvm::Optional< ArgList > ExtraArgsBefore
Add extra compilation arguments to the start of the list.
static cl::opt< std::string > Directory(cl::Positional, cl::Required, cl::desc("<Search Root Directory>"))
static cl::opt< std::string > LineFilter("line-filter", cl::desc(R"( List of files with line ranges to filter the warnings. Can be used together with -header-filter. The format of the list is a JSON array of objects: [ {"name":"file1.cpp","lines":[[1,3],[5,7]]}, {"name":"file2.h"} ] )"), cl::init(""), cl::cat(ClangTidyCategory))
std::vector< OptionsSource > getRawOptions(llvm::StringRef FileName) override
Returns an ordered vector of OptionsSources, in order of increasing priority.
std::string configurationAsText(const ClangTidyOptions &Options)
Serializes configuration to a YAML-encoded string.
static unsigned & element(IO &IO, FileFilter::LineRange &Range, size_t Index)
llvm::StringMap< OptionsSource > CachedOptions
static void mapping(IO &IO, ClangTidyOptions &Options)
std::vector< FileFilter > LineFilter
Output warnings from certain line ranges of certain files only.
ClangTidyOptions mergeWith(const ClangTidyOptions &Other) const
Creates a new ClangTidyOptions instance combined from all fields of this instance overridden by the f...
static void mapping(IO &IO, ClangTidyOptions::StringPair &KeyValue)
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))
std::pair< std::string, std::function< llvm::ErrorOr< ClangTidyOptions > llvm::StringRef)> > ConfigFileHandler
llvm::Optional< std::string > WarningsAsErrors
WarningsAsErrors filter.
std::vector< ConfigFileHandler > ConfigFileHandlers
Configuration file handlers listed in the order of priority.
Contains a list of line ranges in a single file.
llvm::Optional< bool > AnalyzeTemporaryDtors
Turns on temporary destructor-based analysis.
CharSourceRange Range
SourceRange for the file name.
std::vector< ClangTidyOptions::StringPair > Options
static size_t size(IO &IO, FileFilter::LineRange &Range)
std::pair< ClangTidyOptions, std::string > OptionsSource
ClangTidyOptions and its source.
ClangTidyOptions::OptionMap denormalize(IO &)
std::vector< LineRange > LineRanges
A list of line ranges in this file, for which we show warnings.
static ClangTidyOptions getDefaults()
These options are used for all settings that haven't been overridden by the OptionsProvider.
ClangTidyOptions getOptions(llvm::StringRef FileName)
Returns options applying to a specific translation unit with the specified FileName.
clang::tidy::ClangTidyOptionsProvider::OptionsSource OptionsSource
FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions, const ClangTidyOptions &DefaultOptions, const ClangTidyOptions &OverrideOptions)
Initializes the FileOptionsProvider instance.
std::string Name
File name.
llvm::Optional< OptionsSource > tryReadConfigFile(llvm::StringRef Directory)
Try to read configuration files from Directory using registered ConfigHandlers.
ConfigOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions, const ClangTidyOptions &DefaultOptions, const ClangTidyOptions &ConfigOptions, const ClangTidyOptions &OverrideOptions)
Implementation of the ClangTidyOptionsProvider interface, which returns the same options for all file...
const NamedDecl * Result
Definition: USRFinder.cpp:137
static const char OptionsSourceTypeDefaultBinary[]