clang-tools  5.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 
31 namespace llvm {
32 namespace 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("name", File.Name);
49  IO.mapOptional("lines", File.LineRanges);
50  }
51  static StringRef 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 StringRef();
59  }
60 };
61 
62 template <> struct MappingTraits<ClangTidyOptions::StringPair> {
63  static void mapping(IO &IO, ClangTidyOptions::StringPair &KeyValue) {
64  IO.mapRequired("key", KeyValue.first);
65  IO.mapRequired("value", KeyValue.second);
66  }
67 };
68 
69 struct NOptionMap {
70  NOptionMap(IO &) {}
71  NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap)
72  : Options(OptionMap.begin(), OptionMap.end()) {}
73  ClangTidyOptions::OptionMap denormalize(IO &) {
74  ClangTidyOptions::OptionMap Map;
75  for (const auto &KeyValue : Options)
76  Map[KeyValue.first] = KeyValue.second;
77  return Map;
78  }
79  std::vector<ClangTidyOptions::StringPair> Options;
80 };
81 
82 template <> struct MappingTraits<ClangTidyOptions> {
83  static void mapping(IO &IO, ClangTidyOptions &Options) {
84  MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
85  IO, Options.CheckOptions);
86  IO.mapOptional("Checks", Options.Checks);
87  IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors);
88  IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
89  IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
90  IO.mapOptional("FormatStyle", Options.FormatStyle);
91  IO.mapOptional("User", Options.User);
92  IO.mapOptional("CheckOptions", NOpts->Options);
93  IO.mapOptional("ExtraArgs", Options.ExtraArgs);
94  IO.mapOptional("ExtraArgsBefore", Options.ExtraArgsBefore);
95  }
96 };
97 
98 } // namespace yaml
99 } // namespace llvm
100 
101 namespace clang {
102 namespace tidy {
103 
105  ClangTidyOptions Options;
106  Options.Checks = "";
107  Options.WarningsAsErrors = "";
108  Options.HeaderFilterRegex = "";
109  Options.SystemHeaders = false;
110  Options.AnalyzeTemporaryDtors = false;
111  Options.FormatStyle = "none";
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.FormatStyle, Other.FormatStyle);
152  overrideValue(Result.User, Other.User);
153  mergeVectors(Result.ExtraArgs, Other.ExtraArgs);
155 
156  for (const auto &KeyValue : Other.CheckOptions)
157  Result.CheckOptions[KeyValue.first] = KeyValue.second;
158 
159  return Result;
160 }
161 
163  "clang-tidy binary";
165  "command-line option '-checks'";
166 const char
168  "command-line option '-config'";
169 
171 ClangTidyOptionsProvider::getOptions(llvm::StringRef FileName) {
172  ClangTidyOptions Result;
173  for (const auto &Source : getRawOptions(FileName))
174  Result = Result.mergeWith(Source.first);
175  return Result;
176 }
177 
178 std::vector<OptionsSource>
179 DefaultOptionsProvider::getRawOptions(llvm::StringRef FileName) {
180  std::vector<OptionsSource> Result;
181  Result.emplace_back(DefaultOptions, OptionsSourceTypeDefaultBinary);
182  return Result;
183 }
184 
186  const ClangTidyGlobalOptions &GlobalOptions,
187  const ClangTidyOptions &DefaultOptions,
188  const ClangTidyOptions &ConfigOptions,
189  const ClangTidyOptions &OverrideOptions)
190  : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
191  ConfigOptions(ConfigOptions), OverrideOptions(OverrideOptions) {}
192 
193 std::vector<OptionsSource>
194 ConfigOptionsProvider::getRawOptions(llvm::StringRef FileName) {
195  std::vector<OptionsSource> RawOptions =
197  RawOptions.emplace_back(ConfigOptions,
199  RawOptions.emplace_back(OverrideOptions,
201  return RawOptions;
202 }
203 
205  const ClangTidyGlobalOptions &GlobalOptions,
206  const ClangTidyOptions &DefaultOptions,
207  const ClangTidyOptions &OverrideOptions)
208  : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
209  OverrideOptions(OverrideOptions) {
210  ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
211 }
212 
214  const ClangTidyGlobalOptions &GlobalOptions,
215  const ClangTidyOptions &DefaultOptions,
216  const ClangTidyOptions &OverrideOptions,
217  const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
218  : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
219  OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {}
220 
221 // FIXME: This method has some common logic with clang::format::getStyle().
222 // Consider pulling out common bits to a findParentFileWithName function or
223 // similar.
224 std::vector<OptionsSource>
226  DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n");
227 
228  std::vector<OptionsSource> RawOptions =
230  OptionsSource CommandLineOptions(OverrideOptions,
232  // Look for a suitable configuration file in all parent directories of the
233  // file. Start with the immediate parent directory and move up.
234  StringRef Path = llvm::sys::path::parent_path(FileName);
235  for (StringRef CurrentPath = Path; !CurrentPath.empty();
236  CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
237  llvm::Optional<OptionsSource> Result;
238 
239  auto Iter = CachedOptions.find(CurrentPath);
240  if (Iter != CachedOptions.end())
241  Result = Iter->second;
242 
243  if (!Result)
244  Result = tryReadConfigFile(CurrentPath);
245 
246  if (Result) {
247  // Store cached value for all intermediate directories.
248  while (Path != CurrentPath) {
249  DEBUG(llvm::dbgs() << "Caching configuration for path " << Path
250  << ".\n");
251  CachedOptions[Path] = *Result;
252  Path = llvm::sys::path::parent_path(Path);
253  }
254  CachedOptions[Path] = *Result;
255 
256  RawOptions.push_back(*Result);
257  break;
258  }
259  }
260  RawOptions.push_back(CommandLineOptions);
261  return RawOptions;
262 }
263 
264 llvm::Optional<OptionsSource>
266  assert(!Directory.empty());
267 
268  if (!llvm::sys::fs::is_directory(Directory)) {
269  llvm::errs() << "Error reading configuration from " << Directory
270  << ": directory doesn't exist.\n";
271  return llvm::None;
272  }
273 
274  for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
275  SmallString<128> ConfigFile(Directory);
276  llvm::sys::path::append(ConfigFile, ConfigHandler.first);
277  DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
278 
279  bool IsFile = false;
280  // Ignore errors from is_regular_file: we only need to know if we can read
281  // the file or not.
282  llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
283  if (!IsFile)
284  continue;
285 
286  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
287  llvm::MemoryBuffer::getFile(ConfigFile.c_str());
288  if (std::error_code EC = Text.getError()) {
289  llvm::errs() << "Can't read " << ConfigFile << ": " << EC.message()
290  << "\n";
291  continue;
292  }
293 
294  // Skip empty files, e.g. files opened for writing via shell output
295  // redirection.
296  if ((*Text)->getBuffer().empty())
297  continue;
298  llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
299  ConfigHandler.second((*Text)->getBuffer());
300  if (!ParsedOptions) {
301  if (ParsedOptions.getError())
302  llvm::errs() << "Error parsing " << ConfigFile << ": "
303  << ParsedOptions.getError().message() << "\n";
304  continue;
305  }
306  return OptionsSource(*ParsedOptions, ConfigFile.c_str());
307  }
308  return llvm::None;
309 }
310 
311 /// \brief Parses -line-filter option and stores it to the \c Options.
312 std::error_code parseLineFilter(StringRef LineFilter,
314  llvm::yaml::Input Input(LineFilter);
315  Input >> Options.LineFilter;
316  return Input.error();
317 }
318 
319 llvm::ErrorOr<ClangTidyOptions> parseConfiguration(StringRef Config) {
320  llvm::yaml::Input Input(Config);
321  ClangTidyOptions Options;
322  Input >> Options;
323  if (Input.error())
324  return Input.error();
325  return Options;
326 }
327 
328 std::string configurationAsText(const ClangTidyOptions &Options) {
329  std::string Text;
330  llvm::raw_string_ostream Stream(Text);
331  llvm::yaml::Output Output(Stream);
332  // We use the same mapping method for input and output, so we need a non-const
333  // reference here.
334  ClangTidyOptions NonConstValue = Options;
335  Output << NonConstValue;
336  return Stream.str();
337 }
338 
339 } // namespace tidy
340 } // 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.
llvm::Optional< std::string > FormatStyle
Format code around applied fixes with clang-format using this style.
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.
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...
static const char OptionsSourceTypeDefaultBinary[]