blob: 9f6be18d314197650f85537786ec65b452030c76 [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;
26
27LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
28LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
Benjamin Kramer39e40cc2014-09-23 14:46:55 +000029LLVM_YAML_IS_SEQUENCE_VECTOR(ClangTidyOptions::StringPair)
Alexander Kornienko64956b52015-11-09 16:28:11 +000030LLVM_YAML_IS_SEQUENCE_VECTOR(std::string)
Alexander Kornienkodad4acb2014-05-22 16:07:11 +000031
32namespace llvm {
33namespace yaml {
34
35// Map std::pair<int, int> to a JSON array of size 2.
36template <> 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
47template <> 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
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +000063template <> 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
70struct 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
Alexander Kornienkoa4695222014-06-05 13:31:45 +000083template <> struct MappingTraits<ClangTidyOptions> {
84 static void mapping(IO &IO, ClangTidyOptions &Options) {
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +000085 MappingNormalization<NOptionMap, ClangTidyOptions::OptionMap> NOpts(
86 IO, Options.CheckOptions);
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);
90 IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
Alexander Kornienkoe9951542014-09-24 18:36:03 +000091 IO.mapOptional("User", Options.User);
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +000092 IO.mapOptional("CheckOptions", NOpts->Options);
Alexander Kornienko64956b52015-11-09 16:28:11 +000093 IO.mapOptional("ExtraArgs", Options.ExtraArgs);
94 IO.mapOptional("ExtraArgsBefore", Options.ExtraArgsBefore);
Alexander Kornienkoa4695222014-06-05 13:31:45 +000095 }
96};
97
Alexander Kornienkodad4acb2014-05-22 16:07:11 +000098} // namespace yaml
99} // namespace llvm
100
101namespace clang {
102namespace tidy {
103
Alexander Kornienko1efc4252014-10-16 11:27:57 +0000104ClangTidyOptions ClangTidyOptions::getDefaults() {
105 ClangTidyOptions Options;
106 Options.Checks = "";
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000107 Options.WarningsAsErrors = "";
Alexander Kornienko1efc4252014-10-16 11:27:57 +0000108 Options.HeaderFilterRegex = "";
Alexander Kornienko37f7abe2014-10-28 22:16:13 +0000109 Options.SystemHeaders = false;
Alexander Kornienko1efc4252014-10-16 11:27:57 +0000110 Options.AnalyzeTemporaryDtors = false;
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
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000119ClangTidyOptions
120ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
121 ClangTidyOptions Result = *this;
122
123 // Merge comma-separated glob lists by appending the new value after a comma.
124 if (Other.Checks)
125 Result.Checks =
126 (Result.Checks && !Result.Checks->empty() ? *Result.Checks + "," : "") +
127 *Other.Checks;
Jonathan Roelofsd60388a2016-01-13 17:36:41 +0000128 if (Other.WarningsAsErrors)
129 Result.WarningsAsErrors =
130 (Result.WarningsAsErrors && !Result.WarningsAsErrors->empty()
131 ? *Result.WarningsAsErrors + ","
132 : "") +
133 *Other.WarningsAsErrors;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000134
135 if (Other.HeaderFilterRegex)
136 Result.HeaderFilterRegex = Other.HeaderFilterRegex;
Alexander Kornienko37f7abe2014-10-28 22:16:13 +0000137 if (Other.SystemHeaders)
138 Result.SystemHeaders = Other.SystemHeaders;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000139 if (Other.AnalyzeTemporaryDtors)
140 Result.AnalyzeTemporaryDtors = Other.AnalyzeTemporaryDtors;
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000141 if (Other.User)
142 Result.User = Other.User;
Alexander Kornienko64956b52015-11-09 16:28:11 +0000143 if (Other.ExtraArgs)
144 Result.ExtraArgs = Other.ExtraArgs;
145 if (Other.ExtraArgsBefore)
146 Result.ExtraArgsBefore = Other.ExtraArgsBefore;
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000147
148 for (const auto &KeyValue : Other.CheckOptions)
149 Result.CheckOptions[KeyValue.first] = KeyValue.second;
150
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000151 return Result;
152}
153
154FileOptionsProvider::FileOptionsProvider(
155 const ClangTidyGlobalOptions &GlobalOptions,
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000156 const ClangTidyOptions &DefaultOptions,
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000157 const ClangTidyOptions &OverrideOptions)
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000158 : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000159 OverrideOptions(OverrideOptions) {
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000160 ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000161 CachedOptions[""] = DefaultOptions.mergeWith(OverrideOptions);
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000162}
163
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000164FileOptionsProvider::FileOptionsProvider(
165 const ClangTidyGlobalOptions &GlobalOptions,
166 const ClangTidyOptions &DefaultOptions,
167 const ClangTidyOptions &OverrideOptions,
168 const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
169 : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
170 OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {
171 CachedOptions[""] = DefaultOptions.mergeWith(OverrideOptions);
172}
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000173
174// FIXME: This method has some common logic with clang::format::getStyle().
175// Consider pulling out common bits to a findParentFileWithName function or
176// similar.
Alexander Kornienko9115a3a2015-01-20 09:48:51 +0000177ClangTidyOptions FileOptionsProvider::getOptions(StringRef FileName) {
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000178 DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n");
179 SmallString<256> FilePath(FileName);
180
181 if (std::error_code EC = llvm::sys::fs::make_absolute(FilePath)) {
182 llvm::errs() << "Can't make absolute path from " << FileName << ": "
183 << EC.message() << "\n";
184 // FIXME: Figure out what to do.
185 } else {
186 FileName = FilePath;
187 }
188
189 // Look for a suitable configuration file in all parent directories of the
190 // file. Start with the immediate parent directory and move up.
191 StringRef Path = llvm::sys::path::parent_path(FileName);
192 for (StringRef CurrentPath = Path;;
193 CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000194 llvm::Optional<ClangTidyOptions> Result;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000195
196 auto Iter = CachedOptions.find(CurrentPath);
197 if (Iter != CachedOptions.end())
198 Result = Iter->second;
199
200 if (!Result)
201 Result = TryReadConfigFile(CurrentPath);
202
203 if (Result) {
204 // Store cached value for all intermediate directories.
205 while (Path != CurrentPath) {
206 DEBUG(llvm::dbgs() << "Caching configuration for path " << Path
207 << ".\n");
David Blaikie8e6cf9e2014-11-19 03:05:07 +0000208 CachedOptions[Path] = *Result;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000209 Path = llvm::sys::path::parent_path(Path);
210 }
David Blaikie8e6cf9e2014-11-19 03:05:07 +0000211 return CachedOptions[Path] = *Result;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000212 }
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000213 }
214}
215
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000216llvm::Optional<ClangTidyOptions>
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000217FileOptionsProvider::TryReadConfigFile(StringRef Directory) {
218 assert(!Directory.empty());
219
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000220 if (!llvm::sys::fs::is_directory(Directory)) {
221 llvm::errs() << "Error reading configuration from " << Directory
222 << ": directory doesn't exist.\n";
223 return llvm::None;
224 }
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000225
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000226 for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
227 SmallString<128> ConfigFile(Directory);
228 llvm::sys::path::append(ConfigFile, ConfigHandler.first);
229 DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000230
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000231 bool IsFile = false;
232 // Ignore errors from is_regular_file: we only need to know if we can read
233 // the file or not.
234 llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
235 if (!IsFile)
236 continue;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000237
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000238 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
239 llvm::MemoryBuffer::getFile(ConfigFile.c_str());
240 if (std::error_code EC = Text.getError()) {
241 llvm::errs() << "Can't read " << ConfigFile << ": " << EC.message()
242 << "\n";
243 continue;
244 }
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000245
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000246 // Skip empty files, e.g. files opened for writing via shell output
247 // redirection.
248 if ((*Text)->getBuffer().empty())
249 continue;
250 llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
251 ConfigHandler.second((*Text)->getBuffer());
252 if (!ParsedOptions) {
Alexander Kornienkoddf97672014-11-04 15:25:22 +0000253 if (ParsedOptions.getError())
254 llvm::errs() << "Error parsing " << ConfigFile << ": "
255 << ParsedOptions.getError().message() << "\n";
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000256 continue;
257 }
258
Alexander Kornienkob6e1fde2015-02-05 14:50:17 +0000259 return DefaultOptionsProvider::getOptions(Directory)
260 .mergeWith(*ParsedOptions)
261 .mergeWith(OverrideOptions);
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000262 }
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000263 return llvm::None;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000264}
265
Alexander Kornienkodad4acb2014-05-22 16:07:11 +0000266/// \brief Parses -line-filter option and stores it to the \c Options.
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000267std::error_code parseLineFilter(StringRef LineFilter,
Rafael Espindola15c57842014-06-12 13:32:11 +0000268 clang::tidy::ClangTidyGlobalOptions &Options) {
Alexander Kornienkodad4acb2014-05-22 16:07:11 +0000269 llvm::yaml::Input Input(LineFilter);
270 Input >> Options.LineFilter;
271 return Input.error();
272}
273
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000274llvm::ErrorOr<ClangTidyOptions> parseConfiguration(StringRef Config) {
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000275 llvm::yaml::Input Input(Config);
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000276 ClangTidyOptions Options;
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000277 Input >> Options;
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000278 if (Input.error())
279 return Input.error();
280 return Options;
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000281}
282
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000283std::string configurationAsText(const ClangTidyOptions &Options) {
284 std::string Text;
285 llvm::raw_string_ostream Stream(Text);
286 llvm::yaml::Output Output(Stream);
287 // We use the same mapping method for input and output, so we need a non-const
288 // reference here.
289 ClangTidyOptions NonConstValue = Options;
290 Output << NonConstValue;
291 return Stream.str();
292}
293
Alexander Kornienkodad4acb2014-05-22 16:07:11 +0000294} // namespace tidy
295} // namespace clang