clang-tools  3.8.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 
26 
27 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
28 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
29 LLVM_YAML_IS_SEQUENCE_VECTOR(ClangTidyOptions::StringPair)
30 LLVM_YAML_IS_SEQUENCE_VECTOR(std::string)
31 
32 namespace llvm {
33 namespace yaml {
34 
35 // Map std::pair<int, int> to a JSON array of size 2.
36 template <> struct SequenceTraits<FileFilter::LineRange> {
37  static size_t size(IO &IO, FileFilter::LineRange &Range) {
38  return Range.first == 0 ? 0 : Range.second == 0 ? 1 : 2;
39  }
40  static unsigned &element(IO &IO, FileFilter::LineRange &Range, size_t Index) {
41  if (Index > 1)
42  IO.setError("Too many elements in line range.");
43  return Index == 0 ? Range.first : Range.second;
44  }
45 };
46 
47 template <> struct MappingTraits<FileFilter> {
48  static void mapping(IO &IO, FileFilter &File) {
49  IO.mapRequired("name", File.Name);
50  IO.mapOptional("lines", File.LineRanges);
51  }
52  static StringRef validate(IO &io, FileFilter &File) {
53  if (File.Name.empty())
54  return "No file name specified";
55  for (const FileFilter::LineRange &Range : File.LineRanges) {
56  if (Range.first <= 0 || Range.second <= 0)
57  return "Invalid line range";
58  }
59  return StringRef();
60  }
61 };
62 
63 template <> struct MappingTraits<ClangTidyOptions::StringPair> {
64  static void mapping(IO &IO, ClangTidyOptions::StringPair &KeyValue) {
65  IO.mapRequired("key", KeyValue.first);
66  IO.mapRequired("value", KeyValue.second);
67  }
68 };
69 
70 struct NOptionMap {
71  NOptionMap(IO &) {}
72  NOptionMap(IO &, const ClangTidyOptions::OptionMap &OptionMap)
73  : Options(OptionMap.begin(), OptionMap.end()) {}
74  ClangTidyOptions::OptionMap denormalize(IO &) {
75  ClangTidyOptions::OptionMap Map;
76  for (const auto &KeyValue : Options)
77  Map[KeyValue.first] = KeyValue.second;
78  return Map;
79  }
80  std::vector<ClangTidyOptions::StringPair> Options;
81 };
82 
83 template <> struct MappingTraits<ClangTidyOptions> {
84  static void mapping(IO &IO, ClangTidyOptions &Options) {
85  MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
86  IO, Options.CheckOptions);
87  IO.mapOptional("Checks", Options.Checks);
88  IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
89  IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
90  IO.mapOptional("User", Options.User);
91  IO.mapOptional("CheckOptions", NOpts->Options);
92  IO.mapOptional("ExtraArgs", Options.ExtraArgs);
93  IO.mapOptional("ExtraArgsBefore", Options.ExtraArgsBefore);
94  }
95 };
96 
97 } // namespace yaml
98 } // namespace llvm
99 
100 namespace clang {
101 namespace tidy {
102 
104  ClangTidyOptions Options;
105  Options.Checks = "";
106  Options.HeaderFilterRegex = "";
107  Options.SystemHeaders = false;
108  Options.AnalyzeTemporaryDtors = false;
109  Options.User = llvm::None;
110  for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
111  E = ClangTidyModuleRegistry::end();
112  I != E; ++I)
113  Options = Options.mergeWith(I->instantiate()->getModuleOptions());
114  return Options;
115 }
116 
119  ClangTidyOptions Result = *this;
120 
121  // Merge comma-separated glob lists by appending the new value after a comma.
122  if (Other.Checks)
123  Result.Checks =
124  (Result.Checks && !Result.Checks->empty() ? *Result.Checks + "," : "") +
125  *Other.Checks;
126 
127  if (Other.HeaderFilterRegex)
128  Result.HeaderFilterRegex = Other.HeaderFilterRegex;
129  if (Other.SystemHeaders)
130  Result.SystemHeaders = Other.SystemHeaders;
131  if (Other.AnalyzeTemporaryDtors)
133  if (Other.User)
134  Result.User = Other.User;
135  if (Other.ExtraArgs)
136  Result.ExtraArgs = Other.ExtraArgs;
137  if (Other.ExtraArgsBefore)
138  Result.ExtraArgsBefore = Other.ExtraArgsBefore;
139 
140  for (const auto &KeyValue : Other.CheckOptions)
141  Result.CheckOptions[KeyValue.first] = KeyValue.second;
142 
143  return Result;
144 }
145 
147  const ClangTidyGlobalOptions &GlobalOptions,
148  const ClangTidyOptions &DefaultOptions,
149  const ClangTidyOptions &OverrideOptions)
150  : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
151  OverrideOptions(OverrideOptions) {
152  ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
153  CachedOptions[""] = DefaultOptions.mergeWith(OverrideOptions);
154 }
155 
157  const ClangTidyGlobalOptions &GlobalOptions,
158  const ClangTidyOptions &DefaultOptions,
159  const ClangTidyOptions &OverrideOptions,
160  const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
161  : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
162  OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {
163  CachedOptions[""] = DefaultOptions.mergeWith(OverrideOptions);
164 }
165 
166 // FIXME: This method has some common logic with clang::format::getStyle().
167 // Consider pulling out common bits to a findParentFileWithName function or
168 // similar.
170  DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n");
171  SmallString<256> FilePath(FileName);
172 
173  if (std::error_code EC = llvm::sys::fs::make_absolute(FilePath)) {
174  llvm::errs() << "Can't make absolute path from " << FileName << ": "
175  << EC.message() << "\n";
176  // FIXME: Figure out what to do.
177  } else {
178  FileName = FilePath;
179  }
180 
181  // Look for a suitable configuration file in all parent directories of the
182  // file. Start with the immediate parent directory and move up.
183  StringRef Path = llvm::sys::path::parent_path(FileName);
184  for (StringRef CurrentPath = Path;;
185  CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
186  llvm::Optional<ClangTidyOptions> Result;
187 
188  auto Iter = CachedOptions.find(CurrentPath);
189  if (Iter != CachedOptions.end())
190  Result = Iter->second;
191 
192  if (!Result)
193  Result = TryReadConfigFile(CurrentPath);
194 
195  if (Result) {
196  // Store cached value for all intermediate directories.
197  while (Path != CurrentPath) {
198  DEBUG(llvm::dbgs() << "Caching configuration for path " << Path
199  << ".\n");
201  Path = llvm::sys::path::parent_path(Path);
202  }
203  return CachedOptions[Path] = *Result;
204  }
205  }
206 }
207 
208 llvm::Optional<ClangTidyOptions>
210  assert(!Directory.empty());
211 
212  if (!llvm::sys::fs::is_directory(Directory)) {
213  llvm::errs() << "Error reading configuration from " << Directory
214  << ": directory doesn't exist.\n";
215  return llvm::None;
216  }
217 
218  for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
219  SmallString<128> ConfigFile(Directory);
220  llvm::sys::path::append(ConfigFile, ConfigHandler.first);
221  DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
222 
223  bool IsFile = false;
224  // Ignore errors from is_regular_file: we only need to know if we can read
225  // the file or not.
226  llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
227  if (!IsFile)
228  continue;
229 
230  llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
231  llvm::MemoryBuffer::getFile(ConfigFile.c_str());
232  if (std::error_code EC = Text.getError()) {
233  llvm::errs() << "Can't read " << ConfigFile << ": " << EC.message()
234  << "\n";
235  continue;
236  }
237 
238  // Skip empty files, e.g. files opened for writing via shell output
239  // redirection.
240  if ((*Text)->getBuffer().empty())
241  continue;
242  llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
243  ConfigHandler.second((*Text)->getBuffer());
244  if (!ParsedOptions) {
245  if (ParsedOptions.getError())
246  llvm::errs() << "Error parsing " << ConfigFile << ": "
247  << ParsedOptions.getError().message() << "\n";
248  continue;
249  }
250 
251  return DefaultOptionsProvider::getOptions(Directory)
252  .mergeWith(*ParsedOptions)
254  }
255  return llvm::None;
256 }
257 
258 /// \brief Parses -line-filter option and stores it to the \c Options.
259 std::error_code parseLineFilter(StringRef LineFilter,
261  llvm::yaml::Input Input(LineFilter);
262  Input >> Options.LineFilter;
263  return Input.error();
264 }
265 
266 llvm::ErrorOr<ClangTidyOptions> parseConfiguration(StringRef Config) {
267  llvm::yaml::Input Input(Config);
268  ClangTidyOptions Options;
269  Input >> Options;
270  if (Input.error())
271  return Input.error();
272  return Options;
273 }
274 
275 std::string configurationAsText(const ClangTidyOptions &Options) {
276  std::string Text;
277  llvm::raw_string_ostream Stream(Text);
278  llvm::yaml::Output Output(Stream);
279  // We use the same mapping method for input and output, so we need a non-const
280  // reference here.
281  ClangTidyOptions NonConstValue = Options;
282  Output << NonConstValue;
283  return Stream.str();
284 }
285 
286 } // namespace tidy
287 } // namespace clang
llvm::Optional< std::string > Checks
Checks filter.
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.
HeaderHandle File
llvm::Optional< std::string > HeaderFilterRegex
Output warnings from headers matching this filter.
static void mapping(IO &IO, FileFilter &File)
Contains options for clang-tidy.
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 > LineFilter("line-filter", cl::desc("List of files with line ranges to filter the\n""warnings. Can be used together with\n""-header-filter. The format of the list is a JSON\n""array of objects:\n"" [\n"" {\"name\":\"file1.cpp\",\"lines\":[[1,3],[5,7]]},\n"" {\"name\":\"file2.h\"}\n"" ]"), cl::init(""), cl::cat(ClangTidyCategory))
static cl::opt< std::string > Directory(cl::Positional, cl::Required, cl::desc("<Search Root Directory>"))
std::string configurationAsText(const ClangTidyOptions &Options)
Serializes configuration to a YAML-encoded string.
static unsigned & element(IO &IO, FileFilter::LineRange &Range, size_t Index)
static void mapping(IO &IO, ClangTidyOptions &Options)
std::vector< FileFilter > LineFilter
Output warnings from certain line ranges of certain files only.
ClangTidyOptions getOptions(llvm::StringRef) override
Returns options applying to a specific translation unit with the specified FileName.
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)
std::pair< std::string, std::function< llvm::ErrorOr< ClangTidyOptions > llvm::StringRef)> > ConfigFileHandler
std::vector< ConfigFileHandler > ConfigFileHandlers
Configuration file handlers listed in the order of priority.
llvm::Optional< ClangTidyOptions > TryReadConfigFile(llvm::StringRef Directory)
Try to read configuration files from Directory using registered ConfigHandlers.
Contains a list of line ranges in a single file.
static cl::opt< std::string > Config("config", cl::desc("Specifies a configuration in YAML/JSON format:\n"" -config=\"{Checks: '*', CheckOptions: [{key: x, value: y}]}\"\n""When the value is empty, clang-tidy will attempt to find\n""a file named .clang-tidy for each source file in its parent\n""directories."), cl::init(""), cl::cat(ClangTidyCategory))
llvm::Optional< bool > AnalyzeTemporaryDtors
Turns on temporary destructor-based analysis.
llvm::StringMap< ClangTidyOptions > CachedOptions
CharSourceRange Range
SourceRange for the file name.
std::vector< ClangTidyOptions::StringPair > Options
static size_t size(IO &IO, FileFilter::LineRange &Range)
ClangTidyOptions getOptions(llvm::StringRef FileName) override
Returns options applying to a specific translation unit with the specified FileName.
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.
FileOptionsProvider(const ClangTidyGlobalOptions &GlobalOptions, const ClangTidyOptions &DefaultOptions, const ClangTidyOptions &OverrideOptions)
Initializes the FileOptionsProvider instance.
std::string Name
File name.
Implementation of the ClangTidyOptionsProvider interface, which returns the same options for all file...
const NamedDecl * Result
Definition: USRFinder.cpp:121