blob: 6a3aaebc13a79f3cfd581e654fa050578164b3f8 [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"
Hans Wennborgfece4c62014-09-04 22:41:03 +000014#include "llvm/Support/Errc.h"
Alexander Kornienkod53d2682014-09-04 14:23:36 +000015#include "llvm/Support/Debug.h"
16#include "llvm/Support/FileSystem.h"
17#include "llvm/Support/Path.h"
18#include "llvm/Support/raw_ostream.h"
Alexander Kornienkodad4acb2014-05-22 16:07:11 +000019#include "llvm/Support/YAMLTraits.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 Kornienkodad4acb2014-05-22 16:07:11 +000030
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);
Alexander Kornienkoa4695222014-06-05 13:31:45 +000086 IO.mapOptional("Checks", Options.Checks);
87 IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
88 IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
Alexander Kornienkoe9951542014-09-24 18:36:03 +000089 IO.mapOptional("User", Options.User);
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +000090 IO.mapOptional("CheckOptions", NOpts->Options);
Alexander Kornienkoa4695222014-06-05 13:31:45 +000091 }
92};
93
Alexander Kornienkodad4acb2014-05-22 16:07:11 +000094} // namespace yaml
95} // namespace llvm
96
97namespace clang {
98namespace tidy {
99
Alexander Kornienko1efc4252014-10-16 11:27:57 +0000100ClangTidyOptions ClangTidyOptions::getDefaults() {
101 ClangTidyOptions Options;
102 Options.Checks = "";
103 Options.HeaderFilterRegex = "";
104 Options.AnalyzeTemporaryDtors = false;
105 Options.User = llvm::None;
106 for (ClangTidyModuleRegistry::iterator I = ClangTidyModuleRegistry::begin(),
107 E = ClangTidyModuleRegistry::end();
108 I != E; ++I)
109 Options = Options.mergeWith(I->instantiate()->getModuleOptions());
110 return Options;
111}
112
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000113ClangTidyOptions
114ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
115 ClangTidyOptions Result = *this;
116
117 // Merge comma-separated glob lists by appending the new value after a comma.
118 if (Other.Checks)
119 Result.Checks =
120 (Result.Checks && !Result.Checks->empty() ? *Result.Checks + "," : "") +
121 *Other.Checks;
122
123 if (Other.HeaderFilterRegex)
124 Result.HeaderFilterRegex = Other.HeaderFilterRegex;
125 if (Other.AnalyzeTemporaryDtors)
126 Result.AnalyzeTemporaryDtors = Other.AnalyzeTemporaryDtors;
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000127 if (Other.User)
128 Result.User = Other.User;
Alexander Kornienko6e0cbc82014-09-12 08:53:36 +0000129
130 for (const auto &KeyValue : Other.CheckOptions)
131 Result.CheckOptions[KeyValue.first] = KeyValue.second;
132
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000133 return Result;
134}
135
136FileOptionsProvider::FileOptionsProvider(
137 const ClangTidyGlobalOptions &GlobalOptions,
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000138 const ClangTidyOptions &DefaultOptions,
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000139 const ClangTidyOptions &OverrideOptions)
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000140 : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000141 OverrideOptions(OverrideOptions) {
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000142 ConfigHandlers.emplace_back(".clang-tidy", parseConfiguration);
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000143 CachedOptions[""] = DefaultOptions.mergeWith(OverrideOptions);
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000144}
145
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000146FileOptionsProvider::FileOptionsProvider(
147 const ClangTidyGlobalOptions &GlobalOptions,
148 const ClangTidyOptions &DefaultOptions,
149 const ClangTidyOptions &OverrideOptions,
150 const FileOptionsProvider::ConfigFileHandlers &ConfigHandlers)
151 : DefaultOptionsProvider(GlobalOptions, DefaultOptions),
152 OverrideOptions(OverrideOptions), ConfigHandlers(ConfigHandlers) {
153 CachedOptions[""] = DefaultOptions.mergeWith(OverrideOptions);
154}
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000155
156// FIXME: This method has some common logic with clang::format::getStyle().
157// Consider pulling out common bits to a findParentFileWithName function or
158// similar.
159const ClangTidyOptions &FileOptionsProvider::getOptions(StringRef FileName) {
160 DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n");
161 SmallString<256> FilePath(FileName);
162
163 if (std::error_code EC = llvm::sys::fs::make_absolute(FilePath)) {
164 llvm::errs() << "Can't make absolute path from " << FileName << ": "
165 << EC.message() << "\n";
166 // FIXME: Figure out what to do.
167 } else {
168 FileName = FilePath;
169 }
170
171 // Look for a suitable configuration file in all parent directories of the
172 // file. Start with the immediate parent directory and move up.
173 StringRef Path = llvm::sys::path::parent_path(FileName);
174 for (StringRef CurrentPath = Path;;
175 CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000176 llvm::Optional<ClangTidyOptions> Result;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000177
178 auto Iter = CachedOptions.find(CurrentPath);
179 if (Iter != CachedOptions.end())
180 Result = Iter->second;
181
182 if (!Result)
183 Result = TryReadConfigFile(CurrentPath);
184
185 if (Result) {
186 // Store cached value for all intermediate directories.
187 while (Path != CurrentPath) {
188 DEBUG(llvm::dbgs() << "Caching configuration for path " << Path
189 << ".\n");
190 CachedOptions.GetOrCreateValue(Path, *Result);
191 Path = llvm::sys::path::parent_path(Path);
192 }
193 return CachedOptions.GetOrCreateValue(Path, *Result).getValue();
194 }
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000195 }
196}
197
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000198llvm::Optional<ClangTidyOptions>
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000199FileOptionsProvider::TryReadConfigFile(StringRef Directory) {
200 assert(!Directory.empty());
201
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000202 if (!llvm::sys::fs::is_directory(Directory)) {
203 llvm::errs() << "Error reading configuration from " << Directory
204 << ": directory doesn't exist.\n";
205 return llvm::None;
206 }
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000207
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000208 for (const ConfigFileHandler &ConfigHandler : ConfigHandlers) {
209 SmallString<128> ConfigFile(Directory);
210 llvm::sys::path::append(ConfigFile, ConfigHandler.first);
211 DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000212
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000213 bool IsFile = false;
214 // Ignore errors from is_regular_file: we only need to know if we can read
215 // the file or not.
216 llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
217 if (!IsFile)
218 continue;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000219
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000220 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
221 llvm::MemoryBuffer::getFile(ConfigFile.c_str());
222 if (std::error_code EC = Text.getError()) {
223 llvm::errs() << "Can't read " << ConfigFile << ": " << EC.message()
224 << "\n";
225 continue;
226 }
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000227
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000228 // Skip empty files, e.g. files opened for writing via shell output
229 // redirection.
230 if ((*Text)->getBuffer().empty())
231 continue;
232 llvm::ErrorOr<ClangTidyOptions> ParsedOptions =
233 ConfigHandler.second((*Text)->getBuffer());
234 if (!ParsedOptions) {
235 llvm::errs() << "Error parsing " << ConfigFile << ": "
236 << ParsedOptions.getError().message() << "\n";
237 continue;
238 }
239
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000240 ClangTidyOptions Defaults = DefaultOptionsProvider::getOptions(Directory);
241 // Only use checks from the config file.
242 Defaults.Checks = None;
243 return Defaults.mergeWith(*ParsedOptions).mergeWith(OverrideOptions);
244 }
Alexander Kornienko4f6b0662014-10-20 12:29:15 +0000245 return llvm::None;
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000246}
247
Alexander Kornienkodad4acb2014-05-22 16:07:11 +0000248/// \brief Parses -line-filter option and stores it to the \c Options.
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000249std::error_code parseLineFilter(StringRef LineFilter,
Rafael Espindola15c57842014-06-12 13:32:11 +0000250 clang::tidy::ClangTidyGlobalOptions &Options) {
Alexander Kornienkodad4acb2014-05-22 16:07:11 +0000251 llvm::yaml::Input Input(LineFilter);
252 Input >> Options.LineFilter;
253 return Input.error();
254}
255
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000256llvm::ErrorOr<ClangTidyOptions> parseConfiguration(StringRef Config) {
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000257 llvm::yaml::Input Input(Config);
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000258 ClangTidyOptions Options;
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000259 Input >> Options;
Alexander Kornienkoe9951542014-09-24 18:36:03 +0000260 if (Input.error())
261 return Input.error();
262 return Options;
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000263}
264
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000265std::string configurationAsText(const ClangTidyOptions &Options) {
266 std::string Text;
267 llvm::raw_string_ostream Stream(Text);
268 llvm::yaml::Output Output(Stream);
269 // We use the same mapping method for input and output, so we need a non-const
270 // reference here.
271 ClangTidyOptions NonConstValue = Options;
272 Output << NonConstValue;
273 return Stream.str();
274}
275
Alexander Kornienkodad4acb2014-05-22 16:07:11 +0000276} // namespace tidy
277} // namespace clang