blob: c40e97c5fb6dfb76bbb362d0b4f225f8eb6eb206 [file] [log] [blame]
Alexander Kornienkodad4acb2014-05-22 16:07:11 +00001//===--- 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"
Alexander Kornienko1efc4252014-10-16 11:27:57 +000011#include "ClangTidyModuleRegistry.h"
Alexander Kornienkod53d2682014-09-04 14:23:36 +000012#include "clang/Basic/LLVM.h"
13#include "llvm/ADT/SmallString.h"
14#include "llvm/Support/Debug.h"
Chandler Carruth3cbd71c2015-01-14 11:24:38 +000015#include "llvm/Support/Errc.h"
Alexander Kornienkod53d2682014-09-04 14:23:36 +000016#include "llvm/Support/FileSystem.h"
17#include "llvm/Support/Path.h"
Alexander Kornienkodad4acb2014-05-22 16:07:11 +000018#include "llvm/Support/YAMLTraits.h"
Chandler Carruth3cbd71c2015-01-14 11:24:38 +000019#include "llvm/Support/raw_ostream.h"
Alexander Kornienkod53d2682014-09-04 14:23:36 +000020#include <utility>
21
22#define DEBUG_TYPE "clang-tidy-options"
Alexander Kornienkodad4acb2014-05-22 16:07:11 +000023
Alexander Kornienkoa4695222014-06-05 13:31:45 +000024using clang::tidy::ClangTidyOptions;
Alexander Kornienkodad4acb2014-05-22 16:07:11 +000025using clang::tidy::FileFilter;
Haojian Wu12e6b8f2016-04-27 09:15:01 +000026using OptionsSource = clang::tidy::ClangTidyOptionsProvider::OptionsSource;
Alexander Kornienkodad4acb2014-05-22 16:07:11 +000027
28LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
29LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
30
31namespace llvm {
32namespace yaml {
33
34// Map std::pair<int, int> to a JSON array of size 2.
35template <> 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
46template <> 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
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +000062template <> 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
69struct 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
Alexander Kornienkoa4695222014-06-05 13:31:45 +000082template <> struct MappingTraits<ClangTidyOptions> {
83 static void mapping(IO &IO, ClangTidyOptions &Options) {
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +000084 MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
85 IO, Options.CheckOptions);
Roman Lebedev6e76a1b2018-05-08 23:15:58 +000086 bool Ignored = false;
Alexander Kornienkoa4695222014-06-05 13:31:45 +000087 IO.mapOptional("Checks", Options.Checks);
Jonathan Roelofsd60388a2016-01-13 17:36:41 +000088 IO.mapOptional("WarningsAsErrors", Options.WarningsAsErrors);
Alexander Kornienkoa4695222014-06-05 13:31:45 +000089 IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
Roman Lebedev6e76a1b2018-05-08 23:15:58 +000090 IO.mapOptional("AnalyzeTemporaryDtors", Ignored); // legacy compatibility
Alexander Kornienko25613202017-04-06 13:41:29 +000091 IO.mapOptional("FormatStyle", Options.FormatStyle);
Alexander Kornienkoe9951542014-09-24 18:36:03 +000092 IO.mapOptional("User", Options.User);
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +000093 IO.mapOptional("CheckOptions", NOpts->Options);
Alexander Kornienko64956b52015-11-09 16:28:11 +000094 IO.mapOptional("ExtraArgs", Options.ExtraArgs);
95 IO.mapOptional("ExtraArgsBefore", Options.ExtraArgsBefore);
Alexander Kornienkoa4695222014-06-05 13:31:45 +000096 }
97};
98
Alexander Kornienkodad4acb2014-05-22 16:07:11 +000099} // namespace yaml
100} // namespace llvm
101
102namespace clang {
103namespace tidy {
104
Alexander Kornienko1efc4252014-10-16 11:27:57 +0000105ClangTidyOptions ClangTidyOptions::getDefaults() {
106 ClangTidyOptions Options;
107 Options.Checks = "";
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000108 Options.WarningsAsErrors = "";
Alexander Kornienko1efc4252014-10-16 11:27:57 +0000109 Options.HeaderFilterRegex = "";
Alexander Kornienko37f7abe2014-10-28 22:16:13 +0000110 Options.SystemHeaders = false;
Alexander Kornienko25613202017-04-06 13:41:29 +0000111 Options.FormatStyle = "none";
Alexander Kornienko1efc4252014-10-16 11:27:57 +0000112 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
Alexander Kornienkobb560522016-08-23 14:48:29 +0000120template <typename T>
121static 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
130static void mergeCommaSeparatedLists(Optional<std::string> &Dest,
131 const Optional<std::string> &Src) {
132 if (Src)
133 Dest = (Dest && !Dest->empty() ? *Dest + "," : "") + *Src;
134}
135
136template <typename T>
137static void overrideValue(Optional<T> &Dest, const Optional<T> &Src) {
138 if (Src)
139 Dest = Src;
140}
141
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000142ClangTidyOptions
143ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
144 ClangTidyOptions Result = *this;
145
Alexander Kornienkobb560522016-08-23 14:48:29 +0000146 mergeCommaSeparatedLists(Result.Checks, Other.Checks);
147 mergeCommaSeparatedLists(Result.WarningsAsErrors, Other.WarningsAsErrors);
148 overrideValue(Result.HeaderFilterRegex, Other.HeaderFilterRegex);
149 overrideValue(Result.SystemHeaders, Other.SystemHeaders);
Alexander Kornienko25613202017-04-06 13:41:29 +0000150 overrideValue(Result.FormatStyle, Other.FormatStyle);
Alexander Kornienkobb560522016-08-23 14:48:29 +0000151 overrideValue(Result.User, Other.User);
152 mergeVectors(Result.ExtraArgs, Other.ExtraArgs);
153 mergeVectors(Result.ExtraArgsBefore, Other.ExtraArgsBefore);
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000154
155 for (const auto &KeyValue : Other.CheckOptions)
156 Result.CheckOptions[KeyValue.first] = KeyValue.second;
157
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000158 return Result;
159}
160
Haojian Wu12e6b8f2016-04-27 09:15:01 +0000161const char ClangTidyOptionsProvider::OptionsSourceTypeDefaultBinary[] =
162 "clang-tidy binary";
163const char ClangTidyOptionsProvider::OptionsSourceTypeCheckCommandLineOption[] =
164 "command-line option '-checks'";
165const char
166 ClangTidyOptionsProvider::OptionsSourceTypeConfigCommandLineOption[] =
167 "command-line option '-config'";
168
169ClangTidyOptions
170ClangTidyOptionsProvider::getOptions(llvm::StringRef FileName) {
171 ClangTidyOptions Result;
172 for (const auto &Source : getRawOptions(FileName))
173 Result = Result.mergeWith(Source.first);
174 return Result;
175}
176
177std::vector<OptionsSource>
178DefaultOptionsProvider::getRawOptions(llvm::StringRef FileName) {
179 std::vector<OptionsSource> Result;
180 Result.emplace_back(DefaultOptions, OptionsSourceTypeDefaultBinary);
181 return Result;
182}
183
184ConfigOptionsProvider::ConfigOptionsProvider(
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
192std::vector<OptionsSource>
193ConfigOptionsProvider::getRawOptions(llvm::StringRef FileName) {
194 std::vector<OptionsSource> RawOptions =
195 DefaultOptionsProvider::getRawOptions(FileName);
196 RawOptions.emplace_back(ConfigOptions,
197 OptionsSourceTypeConfigCommandLineOption);
198 RawOptions.emplace_back(OverrideOptions,
199 OptionsSourceTypeCheckCommandLineOption);
200 return RawOptions;
201}
202
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000203FileOptionsProvider::FileOptionsProvider(
204 const ClangTidyGlobalOptions &GlobalOptions,
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000205 const ClangTidyOptions &DefaultOptions,
Haojian Wu10e50c82018-04-18 08:54:28 +0000206 const ClangTidyOptions &OverrideOptions,
Jonas Devliegherefc514902018-10-10 13:27:25 +0000207 llvm::IntrusiveRefCntPtr<llvm::vfs::FileSystem> VFS)
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000208 : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
Haojian Wu10e50c82018-04-18 08:54:28 +0000209 OverrideOptions(OverrideOptions), FS(std::move(VFS)) {
210 if (!FS)
Jonas Devliegherefc514902018-10-10 13:27:25 +0000211 FS = llvm::vfs::getRealFileSystem();
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000212 ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000213}
214
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000215FileOptionsProvider::FileOptionsProvider(
216 const ClangTidyGlobalOptions &GlobalOptions,
217 const ClangTidyOptions &DefaultOptions,
218 const ClangTidyOptions &OverrideOptions,
219 const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
220 : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
Mandeep Singh Grang7c7ea7d2016-11-08 07:50:19 +0000221 OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {}
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000222
223// FIXME: This method has some common logic with clang::format::getStyle().
224// Consider pulling out common bits to a findParentFileWithName function or
225// similar.
Haojian Wu12e6b8f2016-04-27 09:15:01 +0000226std::vector<OptionsSource>
227FileOptionsProvider::getRawOptions(StringRef FileName) {
Nicola Zaghen0efd5242018-05-15 16:37:45 +0000228 LLVM_DEBUG(llvm::dbgs() << "Getting options for file " << FileName
229 << "...\n");
Haojian Wu10e50c82018-04-18 08:54:28 +0000230 assert(FS && "FS must be set.");
231
232 llvm::SmallString<128> AbsoluteFilePath(FileName);
Haojian Wuf8a92c12018-04-19 13:34:03 +0000233
234 if (FS->makeAbsolute(AbsoluteFilePath))
Haojian Wu10e50c82018-04-18 08:54:28 +0000235 return {};
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000236
Haojian Wu12e6b8f2016-04-27 09:15:01 +0000237 std::vector<OptionsSource> RawOptions =
Haojian Wu10e50c82018-04-18 08:54:28 +0000238 DefaultOptionsProvider::getRawOptions(AbsoluteFilePath.str());
Haojian Wu12e6b8f2016-04-27 09:15:01 +0000239 OptionsSource CommandLineOptions(OverrideOptions,
240 OptionsSourceTypeCheckCommandLineOption);
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000241 // Look for a suitable configuration file in all parent directories of the
242 // file. Start with the immediate parent directory and move up.
Haojian Wu10e50c82018-04-18 08:54:28 +0000243 StringRef Path = llvm::sys::path::parent_path(AbsoluteFilePath.str());
Haojian Wu12e6b8f2016-04-27 09:15:01 +0000244 for (StringRef CurrentPath = Path; !CurrentPath.empty();
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000245 CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
Haojian Wu12e6b8f2016-04-27 09:15:01 +0000246 llvm::Optional<OptionsSource> Result;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000247
248 auto Iter = CachedOptions.find(CurrentPath);
249 if (Iter != CachedOptions.end())
250 Result = Iter->second;
251
252 if (!Result)
Haojian Wu12e6b8f2016-04-27 09:15:01 +0000253 Result = tryReadConfigFile(CurrentPath);
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000254
255 if (Result) {
256 // Store cached value for all intermediate directories.
257 while (Path != CurrentPath) {
Nicola Zaghen0efd5242018-05-15 16:37:45 +0000258 LLVM_DEBUG(llvm::dbgs()
259 << "Caching configuration for path " << Path << ".\n");
David Blaikie8e6cf9e2014-11-19 03:05:07 +0000260 CachedOptions[Path] = *Result;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000261 Path = llvm::sys::path::parent_path(Path);
262 }
Haojian Wu12e6b8f2016-04-27 09:15:01 +0000263 CachedOptions[Path] = *Result;
264
265 RawOptions.push_back(*Result);
266 break;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000267 }
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000268 }
Haojian Wu12e6b8f2016-04-27 09:15:01 +0000269 RawOptions.push_back(CommandLineOptions);
270 return RawOptions;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000271}
272
Haojian Wu12e6b8f2016-04-27 09:15:01 +0000273llvm::Optional<OptionsSource>
274FileOptionsProvider::tryReadConfigFile(StringRef Directory) {
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000275 assert(!Directory.empty());
276
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000277 if (!llvm::sys::fs::is_directory(Directory)) {
278 llvm::errs() << "Error reading configuration from " << Directory
279 << ": directory doesn't exist.\n";
280 return llvm::None;
281 }
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000282
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000283 for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
284 SmallString<128> ConfigFile(Directory);
285 llvm::sys::path::append(ConfigFile, ConfigHandler.first);
Nicola Zaghen0efd5242018-05-15 16:37:45 +0000286 LLVM_DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000287
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000288 bool IsFile = false;
289 // Ignore errors from is_regular_file: we only need to know if we can read
290 // the file or not.
291 llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
292 if (!IsFile)
293 continue;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000294
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000295 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
296 llvm::MemoryBuffer::getFile(ConfigFile.c_str());
297 if (std::error_code EC = Text.getError()) {
298 llvm::errs() << "Can't read " << ConfigFile << ": " << EC.message()
299 << "\n";
300 continue;
301 }
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000302
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000303 // Skip empty files, e.g. files opened for writing via shell output
304 // redirection.
305 if ((*Text)->getBuffer().empty())
306 continue;
307 llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
308 ConfigHandler.second((*Text)->getBuffer());
309 if (!ParsedOptions) {
Alexander Kornienkoddf97672014-11-04 15:25:22 +0000310 if (ParsedOptions.getError())
311 llvm::errs() << "Error parsing " << ConfigFile << ": "
312 << ParsedOptions.getError().message() << "\n";
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000313 continue;
314 }
Haojian Wu12e6b8f2016-04-27 09:15:01 +0000315 return OptionsSource(*ParsedOptions, ConfigFile.c_str());
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000316 }
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000317 return llvm::None;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000318}
319
Alexander Kornienkodad4acb2014-05-22 16:07:11 +0000320/// \brief Parses -line-filter option and stores it to the \c Options.
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000321std::error_code parseLineFilter(StringRef LineFilter,
Rafael Espindola15c57842014-06-12 13:32:11 +0000322 clang::tidy::ClangTidyGlobalOptions &Options) {
Alexander Kornienkodad4acb2014-05-22 16:07:11 +0000323 llvm::yaml::Input Input(LineFilter);
324 Input >> Options.LineFilter;
325 return Input.error();
326}
327
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000328llvm::ErrorOr<ClangTidyOptions> parseConfiguration(StringRef Config) {
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000329 llvm::yaml::Input Input(Config);
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000330 ClangTidyOptions Options;
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000331 Input >> Options;
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000332 if (Input.error())
333 return Input.error();
334 return Options;
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000335}
336
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000337std::string configurationAsText(const ClangTidyOptions &Options) {
338 std::string Text;
339 llvm::raw_string_ostream Stream(Text);
340 llvm::yaml::Output Output(Stream);
341 // We use the same mapping method for input and output, so we need a non-const
342 // reference here.
343 ClangTidyOptions NonConstValue = Options;
344 Output << NonConstValue;
345 return Stream.str();
346}
347
Alexander Kornienkodad4acb2014-05-22 16:07:11 +0000348} // namespace tidy
349} // namespace clang