clang-tools  10.0.0svn
ClangTidyOptions.cpp
Go to the documentation of this file.
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"
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/Path.h"
17 #include "llvm/Support/YAMLTraits.h"
18 #include "llvm/Support/raw_ostream.h"
19 #include <utility>
20 
21 #define DEBUG_TYPE "clang-tidy-options"
22 
26 
27 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
28 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
29 
30 namespace llvm {
31 namespace yaml {
32 
33 // Map std::pair<int, int> to a JSON array of size 2.
34 template <> struct SequenceTraits<FileFilter::LineRange> {
35  static size_t size(IO &IO, FileFilter::LineRange &Range) {
36  return Range.first == 0 ? 0 : Range.second == 0 ? 1 : 2;
37  }
38  static unsigned &element(IO &IO, FileFilter::LineRange &Range, size_t Index) {
39  if (Index > 1)
40  IO.setError("Too many elements in line range.");
41  return Index == 0 ? Range.first : Range.second;
42  }
43 };
44 
45 template <> struct MappingTraits<FileFilter> {
46  static void mapping(IO &IO, FileFilter &File) {
47  IO.mapRequired("name", File.Name);
48  IO.mapOptional("lines", File.LineRanges);
49  }
50  static StringRef validate(IO &io, FileFilter &File) {
51  if (File.Name.empty())
52  return "No file name specified";
53  for (const FileFilter::LineRange &Range : File.LineRanges) {
54  if (Range.first <= 0 || Range.second <= 0)
55  return "Invalid line range";
56  }
57  return StringRef();
58  }
59 };
60 
61 template <> struct MappingTraits<ClangTidyOptions::StringPair> {
62  static void mapping(IO &IO, ClangTidyOptions::StringPair &KeyValue) {
63  IO.mapRequired("key", KeyValue.first);
64  IO.mapRequired("value", KeyValue.second);
65  }
66 };
67 
68 struct NOptionMap {
69  NOptionMap(IO &) {}
70  NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap)
71  : Options(OptionMap.begin(), OptionMap.end()) {}
72  ClangTidyOptions::OptionMap denormalize(IO &) {
73  ClangTidyOptions::OptionMap Map;
74  for (const auto &KeyValue : Options)
75  Map[KeyValue.first] = KeyValue.second;
76  return Map;
77  }
78  std::vector<ClangTidyOptions::StringPair> Options;
79 };
80 
81 template <> struct MappingTraits<ClangTidyOptions> {
82  static void mapping(IO &IO, ClangTidyOptions &Options) {
83  MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
84  IO, Options.CheckOptions);
85  bool Ignored = false;
86  IO.mapOptional("Checks", Options.Checks);
87  IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors);
88  IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
89  IO.mapOptional("AnalyzeTemporaryDtors", Ignored); // legacy compatibility
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 
104 ClangTidyOptions ClangTidyOptions::getDefaults() {
105  ClangTidyOptions Options;
106  Options.Checks = "";
107  Options.WarningsAsErrors = "";
108  Options.HeaderFilterRegex = "";
109  Options.SystemHeaders = false;
110  Options.FormatStyle = "none";
111  Options.User = llvm::None;
112  for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
113  E = ClangTidyModuleRegistry::end();
114  I != E; ++I)
115  Options = Options.mergeWith(I->instantiate()->getModuleOptions());
116  return Options;
117 }
118 
119 template <typename T>
120 static void mergeVectors(Optional<T> &Dest, const Optional<T> &Src) {
121  if (Src) {
122  if (Dest)
123  Dest->insert(Dest->end(), Src->begin(), Src->end());
124  else
125  Dest = Src;
126  }
127 }
128 
129 static void mergeCommaSeparatedLists(Optional<std::string> &Dest,
130  const Optional<std::string> &Src) {
131  if (Src)
132  Dest = (Dest && !Dest->empty() ? *Dest + "," : "") + *Src;
133 }
134 
135 template <typename T>
136 static void overrideValue(Optional<T> &Dest, const Optional<T> &Src) {
137  if (Src)
138  Dest = Src;
139 }
140 
142 ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
143  ClangTidyOptions Result = *this;
144 
145  mergeCommaSeparatedLists(Result.Checks, Other.Checks);
149  overrideValue(Result.FormatStyle, Other.FormatStyle);
150  overrideValue(Result.User, Other.User);
151  mergeVectors(Result.ExtraArgs, Other.ExtraArgs);
153 
154  for (const auto &KeyValue : Other.CheckOptions)
155  Result.CheckOptions[KeyValue.first] = KeyValue.second;
156 
157  return Result;
158 }
159 
160 const char ClangTidyOptionsProvider::OptionsSourceTypeDefaultBinary[] =
161  "clang-tidy binary";
162 const char ClangTidyOptionsProvider::OptionsSourceTypeCheckCommandLineOption[] =
163  "command-line option '-checks'";
164 const char
165  ClangTidyOptionsProvider::OptionsSourceTypeConfigCommandLineOption[] =
166  "command-line option '-config'";
167 
169 ClangTidyOptionsProvider::getOptions(llvm::StringRef FileName) {
170  ClangTidyOptions Result;
171  for (const auto &Source : getRawOptions(FileName))
172  Result = Result.mergeWith(Source.first);
173  return Result;
174 }
175 
176 std::vector<OptionsSource>
177 DefaultOptionsProvider::getRawOptions(llvm::StringRef FileName) {
178  std::vector<OptionsSource> Result;
179  Result.emplace_back(DefaultOptions, OptionsSourceTypeDefaultBinary);
180  return Result;
181 }
182 
183 ConfigOptionsProvider::ConfigOptionsProvider(
184  const ClangTidyGlobalOptions &GlobalOptions,
185  const ClangTidyOptions &DefaultOptions,
186  const ClangTidyOptions &ConfigOptions,
187  const ClangTidyOptions &OverrideOptions)
188  : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
189  ConfigOptions(ConfigOptions), OverrideOptions(OverrideOptions) {}
190 
191 std::vector<OptionsSource>
193  std::vector<OptionsSource> RawOptions =
195  RawOptions.emplace_back(ConfigOptions,
197  RawOptions.emplace_back(OverrideOptions,
199  return RawOptions;
200 }
201 
203  const ClangTidyGlobalOptions &GlobalOptions,
204  const ClangTidyOptions &DefaultOptions,
205  const ClangTidyOptions &OverrideOptions,
206  llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
207  : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
208  OverrideOptions(OverrideOptions), FS(std::move(VFS)) {
209  if (!FS)
210  FS = llvm::vfs::getRealFileSystem();
211  ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
212 }
213 
215  const ClangTidyGlobalOptions &GlobalOptions,
216  const ClangTidyOptions &DefaultOptions,
219  : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
220  OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {}
221 
222 // FIXME: This method has some common logic with clang::format::getStyle().
223 // Consider pulling out common bits to a findParentFileWithName function or
224 // similar.
225 std::vector<OptionsSource>
227  LLVM_DEBUG(llvm::dbgs() << "Getting options for file " << FileName
228  << "...\n");
229  assert(FS && "FS must be set.");
230 
231  llvm::SmallString<128> AbsoluteFilePath(FileName);
232 
233  if (FS->makeAbsolute(AbsoluteFilePath))
234  return {};
235 
236  std::vector<OptionsSource> RawOptions =
237  DefaultOptionsProvider::getRawOptions(AbsoluteFilePath.str());
238  OptionsSource CommandLineOptions(OverrideOptions,
240  // Look for a suitable configuration file in all parent directories of the
241  // file. Start with the immediate parent directory and move up.
242  StringRef Path = llvm::sys::path::parent_path(AbsoluteFilePath.str());
243  for (StringRef CurrentPath = Path; !CurrentPath.empty();
244  CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
245  llvm::Optional<OptionsSource> Result;
246 
247  auto Iter = CachedOptions.find(CurrentPath);
248  if (Iter != CachedOptions.end())
249  Result = Iter->second;
250 
251  if (!Result)
252  Result = tryReadConfigFile(CurrentPath);
253 
254  if (Result) {
255  // Store cached value for all intermediate directories.
256  while (Path != CurrentPath) {
257  LLVM_DEBUG(llvm::dbgs()
258  << "Caching configuration for path " << Path << ".\n");
259  CachedOptions[Path] = *Result;
260  Path = llvm::sys::path::parent_path(Path);
261  }
262  CachedOptions[Path] = *Result;
263 
264  RawOptions.push_back(*Result);
265  break;
266  }
267  }
268  RawOptions.push_back(CommandLineOptions);
269  return RawOptions;
270 }
271 
272 llvm::Optional<OptionsSource>
274  assert(!Directory.empty());
275 
276  if (!llvm::sys::fs::is_directory(Directory)) {
277  llvm::errs() << "Error reading configuration from " << Directory
278  << ": directory doesn't exist.\n";
279  return llvm::None;
280  }
281 
282  for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
283  SmallString<128> ConfigFile(Directory);
284  llvm::sys::path::append(ConfigFile, ConfigHandler.first);
285  LLVM_DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
286 
287  bool IsFile = false;
288  // Ignore errors from is_regular_file: we only need to know if we can read
289  // the file or not.
290  llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
291  if (!IsFile)
292  continue;
293 
294  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
295  llvm::MemoryBuffer::getFile(ConfigFile.c_str());
296  if (std::error_code EC = Text.getError()) {
297  llvm::errs() << "Can't read " << ConfigFile << ": " << EC.message()
298  << "\n";
299  continue;
300  }
301 
302  // Skip empty files, e.g. files opened for writing via shell output
303  // redirection.
304  if ((*Text)->getBuffer().empty())
305  continue;
306  llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
307  ConfigHandler.second((*Text)->getBuffer());
308  if (!ParsedOptions) {
309  if (ParsedOptions.getError())
310  llvm::errs() << "Error parsing " << ConfigFile << ": "
311  << ParsedOptions.getError().message() << "\n";
312  continue;
313  }
314  return OptionsSource(*ParsedOptions, ConfigFile.c_str());
315  }
316  return llvm::None;
317 }
318 
319 /// Parses -line-filter option and stores it to the \c Options.
320 std::error_code parseLineFilter(StringRef LineFilter,
322  llvm::yaml::Input Input(LineFilter);
323  Input >> Options.LineFilter;
324  return Input.error();
325 }
326 
327 llvm::ErrorOr<ClangTidyOptions> parseConfiguration(StringRef Config) {
328  llvm::yaml::Input Input(Config);
329  ClangTidyOptions Options;
330  Input >> Options;
331  if (Input.error())
332  return Input.error();
333  return Options;
334 }
335 
336 std::string configurationAsText(const ClangTidyOptions &Options) {
337  std::string Text;
338  llvm::raw_string_ostream Stream(Text);
339  llvm::yaml::Output Output(Stream);
340  // We use the same mapping method for input and output, so we need a non-const
341  // reference here.
342  ClangTidyOptions NonConstValue = Options;
343  Output << NonConstValue;
344  return Stream.str();
345 }
346 
347 } // namespace tidy
348 } // namespace clang
llvm::Optional< std::string > Checks
Checks filter.
ClangTidyOptions mergeWith(const ClangTidyOptions &Other) const
Creates a new ClangTidyOptions instance combined from all fields of this instance overridden by the f...
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.
Some operations such as code completion produce a set of candidates.
llvm::Optional< std::string > User
Specifies the name or e-mail of the user running clang-tidy.
std::string Text
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > VFS
static void mergeVectors(Optional< T > &Dest, const Optional< T > &Src)
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))
llvm::Optional< std::string > HeaderFilterRegex
Output warnings from headers matching this filter.
static void mapping(IO &IO, FileFilter &File)
static const char OptionsSourceTypeCheckCommandLineOption[]
MockFSProvider FS
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.
FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions, const ClangTidyOptions &DefaultOptions, const ClangTidyOptions &OverrideOptions, llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS=nullptr)
Initializes the FileOptionsProvider instance.
llvm::StringRef Src
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 > 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))
llvm::IntrusiveRefCntPtr< llvm::vfs::FileSystem > FS
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.
static void mapping(IO &IO, ClangTidyOptions::StringPair &KeyValue)
PathRef FileName
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.
llvm::StringRef Directory
Contains a list of line ranges in a single file.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
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.
clang::tidy::ClangTidyOptionsProvider::OptionsSource OptionsSource
std::string Name
File name.
llvm::Optional< OptionsSource > tryReadConfigFile(llvm::StringRef Directory)
Try to read configuration files from Directory using registered ConfigHandlers.
Implementation of the ClangTidyOptionsProvider interface, which returns the same options for all file...
const SymbolIndex * Index
Definition: Dexp.cpp:84