clang-tools  4.0.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 
120 template <typename T>
121 static void mergeVectors(Optional<T> &Dest, const Optional<T> &Src) {
122  if (Src) {
123  if (Dest)
124  Dest->insert(Dest->end(), Src->begin(), Src->end());
125  else
126  Dest = Src;
127  }
128 }
129 
130 static void mergeCommaSeparatedLists(Optional<std::string> &Dest,
131  const Optional<std::string> &Src) {
132  if (Src)
133  Dest = (Dest && !Dest->empty() ? *Dest + "," : "") + *Src;
134 }
135 
136 template <typename T>
137 static void overrideValue(Optional<T> &Dest, const Optional<T> &Src) {
138  if (Src)
139  Dest = Src;
140 }
141 
144  ClangTidyOptions Result = *this;
145 
146  mergeCommaSeparatedLists(Result.Checks, Other.Checks);
151  overrideValue(Result.User, Other.User);
152  mergeVectors(Result.ExtraArgs, Other.ExtraArgs);
154 
155  for (const auto &KeyValue : Other.CheckOptions)
156  Result.CheckOptions[KeyValue.first] = KeyValue.second;
157 
158  return Result;
159 }
160 
162  "clang-tidy binary";
164  "command-line option '-checks'";
165 const char
167  "command-line option '-config'";
168 
170 ClangTidyOptionsProvider::getOptions(llvm::StringRef FileName) {
172  for (const auto &Source : getRawOptions(FileName))
173  Result = Result.mergeWith(Source.first);
174  return Result;
175 }
176 
177 std::vector<OptionsSource>
178 DefaultOptionsProvider::getRawOptions(llvm::StringRef FileName) {
179  std::vector<OptionsSource> Result;
180  Result.emplace_back(DefaultOptions, OptionsSourceTypeDefaultBinary);
181  return Result;
182 }
183 
185  const ClangTidyGlobalOptions &GlobalOptions,
186  const ClangTidyOptions &DefaultOptions,
187  const ClangTidyOptions &ConfigOptions,
188  const ClangTidyOptions &OverrideOptions)
189  : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
190  ConfigOptions(ConfigOptions), OverrideOptions(OverrideOptions) {}
191 
192 std::vector<OptionsSource>
193 ConfigOptionsProvider::getRawOptions(llvm::StringRef FileName) {
194  std::vector<OptionsSource> RawOptions =
196  RawOptions.emplace_back(ConfigOptions,
198  RawOptions.emplace_back(OverrideOptions,
200  return RawOptions;
201 }
202 
204  const ClangTidyGlobalOptions &GlobalOptions,
205  const ClangTidyOptions &DefaultOptions,
206  const ClangTidyOptions &OverrideOptions)
207  : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
208  OverrideOptions(OverrideOptions) {
209  ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
210 }
211 
213  const ClangTidyGlobalOptions &GlobalOptions,
214  const ClangTidyOptions &DefaultOptions,
215  const ClangTidyOptions &OverrideOptions,
216  const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
217  : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
218  OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {}
219 
220 // FIXME: This method has some common logic with clang::format::getStyle().
221 // Consider pulling out common bits to a findParentFileWithName function or
222 // similar.
223 std::vector<OptionsSource>
225  DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n");
226 
227  std::vector<OptionsSource> RawOptions =
229  OptionsSource CommandLineOptions(OverrideOptions,
231  // Look for a suitable configuration file in all parent directories of the
232  // file. Start with the immediate parent directory and move up.
233  StringRef Path = llvm::sys::path::parent_path(FileName);
234  for (StringRef CurrentPath = Path; !CurrentPath.empty();
235  CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
236  llvm::Optional<OptionsSource> Result;
237 
238  auto Iter = CachedOptions.find(CurrentPath);
239  if (Iter != CachedOptions.end())
240  Result = Iter->second;
241 
242  if (!Result)
243  Result = tryReadConfigFile(CurrentPath);
244 
245  if (Result) {
246  // Store cached value for all intermediate directories.
247  while (Path != CurrentPath) {
248  DEBUG(llvm::dbgs() << "Caching configuration for path " << Path
249  << ".\n");
251  Path = llvm::sys::path::parent_path(Path);
252  }
254 
255  RawOptions.push_back(*Result);
256  break;
257  }
258  }
259  RawOptions.push_back(CommandLineOptions);
260  return RawOptions;
261 }
262 
263 llvm::Optional<OptionsSource>
265  assert(!Directory.empty());
266 
267  if (!llvm::sys::fs::is_directory(Directory)) {
268  llvm::errs() << "Error reading configuration from " << Directory
269  << ": directory doesn't exist.\n";
270  return llvm::None;
271  }
272 
273  for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
274  SmallString<128> ConfigFile(Directory);
275  llvm::sys::path::append(ConfigFile, ConfigHandler.first);
276  DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
277 
278  bool IsFile = false;
279  // Ignore errors from is_regular_file: we only need to know if we can read
280  // the file or not.
281  llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
282  if (!IsFile)
283  continue;
284 
285  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
286  llvm::MemoryBuffer::getFile(ConfigFile.c_str());
287  if (std::error_code EC = Text.getError()) {
288  llvm::errs() << "Can't read " << ConfigFile << ": " << EC.message()
289  << "\n";
290  continue;
291  }
292 
293  // Skip empty files, e.g. files opened for writing via shell output
294  // redirection.
295  if ((*Text)->getBuffer().empty())
296  continue;
297  llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
298  ConfigHandler.second((*Text)->getBuffer());
299  if (!ParsedOptions) {
300  if (ParsedOptions.getError())
301  llvm::errs() << "Error parsing " << ConfigFile << ": "
302  << ParsedOptions.getError().message() << "\n";
303  continue;
304  }
305  return OptionsSource(*ParsedOptions, ConfigFile.c_str());
306  }
307  return llvm::None;
308 }
309 
310 /// \brief Parses -line-filter option and stores it to the \c Options.
311 std::error_code parseLineFilter(StringRef LineFilter,
313  llvm::yaml::Input Input(LineFilter);
314  Input >> Options.LineFilter;
315  return Input.error();
316 }
317 
318 llvm::ErrorOr<ClangTidyOptions> parseConfiguration(StringRef Config) {
319  llvm::yaml::Input Input(Config);
320  ClangTidyOptions Options;
321  Input >> Options;
322  if (Input.error())
323  return Input.error();
324  return Options;
325 }
326 
327 std::string configurationAsText(const ClangTidyOptions &Options) {
328  std::string Text;
329  llvm::raw_string_ostream Stream(Text);
330  llvm::yaml::Output Output(Stream);
331  // We use the same mapping method for input and output, so we need a non-const
332  // reference here.
333  ClangTidyOptions NonConstValue = Options;
334  Output << NonConstValue;
335  return Stream.str();
336 }
337 
338 } // namespace tidy
339 } // 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.
static void mergeVectors(Optional< T > &Dest, const Optional< T > &Src)
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))
static cl::opt< std::string > Input("input", cl::desc("YAML file to load oldname-newname pairs from."), cl::Optional, cl::cat(ClangRenameOptions))
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.
static void mergeCommaSeparatedLists(Optional< std::string > &Dest, const Optional< std::string > &Src)
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)
static void overrideValue(Optional< T > &Dest, const Optional< T > &Src)
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:162
static const char OptionsSourceTypeDefaultBinary[]