blob: e4ceb9c3be3686c07a08ffd76ed32ee1b26b0510 [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 Kornienkod53d2682014-09-04 14:23:36 +000011#include "clang/Basic/LLVM.h"
12#include "llvm/ADT/SmallString.h"
Hans Wennborgfece4c62014-09-04 22:41:03 +000013#include "llvm/Support/Errc.h"
Alexander Kornienkod53d2682014-09-04 14:23:36 +000014#include "llvm/Support/Debug.h"
15#include "llvm/Support/FileSystem.h"
16#include "llvm/Support/Path.h"
17#include "llvm/Support/raw_ostream.h"
Alexander Kornienkodad4acb2014-05-22 16:07:11 +000018#include "llvm/Support/YAMLTraits.h"
Alexander Kornienkod53d2682014-09-04 14:23:36 +000019#include <utility>
20
21#define DEBUG_TYPE "clang-tidy-options"
Alexander Kornienkodad4acb2014-05-22 16:07:11 +000022
Alexander Kornienkoa4695222014-06-05 13:31:45 +000023using clang::tidy::ClangTidyOptions;
Alexander Kornienkodad4acb2014-05-22 16:07:11 +000024using clang::tidy::FileFilter;
25
26LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter)
27LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(FileFilter::LineRange)
28
29namespace llvm {
30namespace yaml {
31
32// Map std::pair<int, int> to a JSON array of size 2.
33template <> struct SequenceTraits<FileFilter::LineRange> {
34 static size_t size(IO &IO, FileFilter::LineRange &Range) {
35 return Range.first == 0 ? 0 : Range.second == 0 ? 1 : 2;
36 }
37 static unsigned &element(IO &IO, FileFilter::LineRange &Range, size_t Index) {
38 if (Index > 1)
39 IO.setError("Too many elements in line range.");
40 return Index == 0 ? Range.first : Range.second;
41 }
42};
43
44template <> struct MappingTraits<FileFilter> {
45 static void mapping(IO &IO, FileFilter &File) {
46 IO.mapRequired("name", File.Name);
47 IO.mapOptional("lines", File.LineRanges);
48 }
49 static StringRef validate(IO &io, FileFilter &File) {
50 if (File.Name.empty())
51 return "No file name specified";
52 for (const FileFilter::LineRange &Range : File.LineRanges) {
53 if (Range.first <= 0 || Range.second <= 0)
54 return "Invalid line range";
55 }
56 return StringRef();
57 }
58};
59
Alexander Kornienkoa4695222014-06-05 13:31:45 +000060template <> struct MappingTraits<ClangTidyOptions> {
61 static void mapping(IO &IO, ClangTidyOptions &Options) {
62 IO.mapOptional("Checks", Options.Checks);
63 IO.mapOptional("HeaderFilterRegex", Options.HeaderFilterRegex);
64 IO.mapOptional("AnalyzeTemporaryDtors", Options.AnalyzeTemporaryDtors);
65 }
66};
67
Alexander Kornienkodad4acb2014-05-22 16:07:11 +000068} // namespace yaml
69} // namespace llvm
70
71namespace clang {
72namespace tidy {
73
Alexander Kornienkod53d2682014-09-04 14:23:36 +000074ClangTidyOptions
75ClangTidyOptions::mergeWith(const ClangTidyOptions &Other) const {
76 ClangTidyOptions Result = *this;
77
78 // Merge comma-separated glob lists by appending the new value after a comma.
79 if (Other.Checks)
80 Result.Checks =
81 (Result.Checks && !Result.Checks->empty() ? *Result.Checks + "," : "") +
82 *Other.Checks;
83
84 if (Other.HeaderFilterRegex)
85 Result.HeaderFilterRegex = Other.HeaderFilterRegex;
86 if (Other.AnalyzeTemporaryDtors)
87 Result.AnalyzeTemporaryDtors = Other.AnalyzeTemporaryDtors;
88 return Result;
89}
90
91FileOptionsProvider::FileOptionsProvider(
92 const ClangTidyGlobalOptions &GlobalOptions,
93 const ClangTidyOptions &FallbackOptions,
94 const ClangTidyOptions &OverrideOptions)
95 : DefaultOptionsProvider(GlobalOptions, FallbackOptions),
96 OverrideOptions(OverrideOptions) {
97 CachedOptions[""] = FallbackOptions.mergeWith(OverrideOptions);
98}
99
100static const char ConfigFileName[] = ".clang-tidy";
101
102// FIXME: This method has some common logic with clang::format::getStyle().
103// Consider pulling out common bits to a findParentFileWithName function or
104// similar.
105const ClangTidyOptions &FileOptionsProvider::getOptions(StringRef FileName) {
106 DEBUG(llvm::dbgs() << "Getting options for file " << FileName << "...\n");
107 SmallString<256> FilePath(FileName);
108
109 if (std::error_code EC = llvm::sys::fs::make_absolute(FilePath)) {
110 llvm::errs() << "Can't make absolute path from " << FileName << ": "
111 << EC.message() << "\n";
112 // FIXME: Figure out what to do.
113 } else {
114 FileName = FilePath;
115 }
116
117 // Look for a suitable configuration file in all parent directories of the
118 // file. Start with the immediate parent directory and move up.
119 StringRef Path = llvm::sys::path::parent_path(FileName);
120 for (StringRef CurrentPath = Path;;
121 CurrentPath = llvm::sys::path::parent_path(CurrentPath)) {
122 llvm::ErrorOr<ClangTidyOptions> Result = std::error_code();
123
124 auto Iter = CachedOptions.find(CurrentPath);
125 if (Iter != CachedOptions.end())
126 Result = Iter->second;
127
128 if (!Result)
129 Result = TryReadConfigFile(CurrentPath);
130
131 if (Result) {
132 // Store cached value for all intermediate directories.
133 while (Path != CurrentPath) {
134 DEBUG(llvm::dbgs() << "Caching configuration for path " << Path
135 << ".\n");
136 CachedOptions.GetOrCreateValue(Path, *Result);
137 Path = llvm::sys::path::parent_path(Path);
138 }
139 return CachedOptions.GetOrCreateValue(Path, *Result).getValue();
140 }
Hans Wennborgfece4c62014-09-04 22:41:03 +0000141 if (Result.getError() != llvm::errc::no_such_file_or_directory) {
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000142 llvm::errs() << "Error reading " << ConfigFileName << " from " << Path
143 << ": " << Result.getError().message() << "\n";
144 }
145 }
146}
147
148llvm::ErrorOr<ClangTidyOptions>
149FileOptionsProvider::TryReadConfigFile(StringRef Directory) {
150 assert(!Directory.empty());
151
152 ClangTidyOptions Options = DefaultOptionsProvider::getOptions(Directory);
153 if (!llvm::sys::fs::is_directory(Directory))
Hans Wennborgfece4c62014-09-04 22:41:03 +0000154 return make_error_code(llvm::errc::not_a_directory);
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000155
156 SmallString<128> ConfigFile(Directory);
157 llvm::sys::path::append(ConfigFile, ".clang-tidy");
158 DEBUG(llvm::dbgs() << "Trying " << ConfigFile << "...\n");
159
160 bool IsFile = false;
161 // Ignore errors from is_regular_file: we only need to know if we can read
162 // the file or not.
163 llvm::sys::fs::is_regular_file(Twine(ConfigFile), IsFile);
164
165 if (!IsFile)
Hans Wennborgfece4c62014-09-04 22:41:03 +0000166 return make_error_code(llvm::errc::no_such_file_or_directory);
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000167
168 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> Text =
169 llvm::MemoryBuffer::getFile(ConfigFile.c_str());
170 if (std::error_code EC = Text.getError())
171 return EC;
172 if (std::error_code EC = parseConfiguration((*Text)->getBuffer(), Options))
173 return EC;
174 return Options.mergeWith(OverrideOptions);
175}
176
Alexander Kornienkodad4acb2014-05-22 16:07:11 +0000177/// \brief Parses -line-filter option and stores it to the \c Options.
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000178std::error_code parseLineFilter(StringRef LineFilter,
Rafael Espindola15c57842014-06-12 13:32:11 +0000179 clang::tidy::ClangTidyGlobalOptions &Options) {
Alexander Kornienkodad4acb2014-05-22 16:07:11 +0000180 llvm::yaml::Input Input(LineFilter);
181 Input >> Options.LineFilter;
182 return Input.error();
183}
184
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000185std::error_code parseConfiguration(StringRef Config,
Rafael Espindola15c57842014-06-12 13:32:11 +0000186 clang::tidy::ClangTidyOptions &Options) {
Alexander Kornienkoa4695222014-06-05 13:31:45 +0000187 llvm::yaml::Input Input(Config);
188 Input >> Options;
189 return Input.error();
190}
191
Alexander Kornienkod53d2682014-09-04 14:23:36 +0000192std::string configurationAsText(const ClangTidyOptions &Options) {
193 std::string Text;
194 llvm::raw_string_ostream Stream(Text);
195 llvm::yaml::Output Output(Stream);
196 // We use the same mapping method for input and output, so we need a non-const
197 // reference here.
198 ClangTidyOptions NonConstValue = Options;
199 Output << NonConstValue;
200 return Stream.str();
201}
202
Alexander Kornienkodad4acb2014-05-22 16:07:11 +0000203} // namespace tidy
204} // namespace clang