blob: 9db129cd7e03a80fe92596a00123159aaf6224e4 [file] [log] [blame]
Benjamin Kramer6b236262016-04-20 12:43:43 +00001//===-- ClangIncludeFixer.cpp - Standalone include fixer ------------------===//
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
Sam McCall9c5ebf72017-03-13 15:55:59 +000010#include "FuzzySymbolIndex.h"
Benjamin Kramera3d82332016-05-13 09:27:54 +000011#include "InMemorySymbolIndex.h"
Benjamin Kramer6b236262016-04-20 12:43:43 +000012#include "IncludeFixer.h"
Haojian Wu11e9bd22016-05-31 09:31:51 +000013#include "IncludeFixerContext.h"
Benjamin Kramera3d82332016-05-13 09:27:54 +000014#include "SymbolIndexManager.h"
15#include "YamlSymbolIndex.h"
Haojian Wu11e9bd22016-05-31 09:31:51 +000016#include "clang/Format/Format.h"
Benjamin Kramer6b236262016-04-20 12:43:43 +000017#include "clang/Frontend/TextDiagnosticPrinter.h"
18#include "clang/Rewrite/Core/Rewriter.h"
19#include "clang/Tooling/CommonOptionsParser.h"
Haojian Wu627ca962016-07-08 09:10:29 +000020#include "clang/Tooling/Core/Replacement.h"
Benjamin Kramer6b236262016-04-20 12:43:43 +000021#include "clang/Tooling/Tooling.h"
22#include "llvm/Support/CommandLine.h"
Benjamin Kramer8ff8fdf2016-07-18 19:21:22 +000023#include "llvm/Support/Path.h"
Haojian Wu17a54e32016-06-01 11:43:10 +000024#include "llvm/Support/YAMLTraits.h"
Haojian Wud8c12ba2016-04-29 09:23:38 +000025
Benjamin Kramer6b236262016-04-20 12:43:43 +000026using namespace clang;
Benjamin Kramerf412e902016-04-27 14:24:32 +000027using namespace llvm;
Haojian Wu17a54e32016-06-01 11:43:10 +000028using clang::include_fixer::IncludeFixerContext;
29
30LLVM_YAML_IS_DOCUMENT_LIST_VECTOR(IncludeFixerContext)
31LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(std::string)
Haojian Wu68c34a02016-07-13 16:43:54 +000032LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(IncludeFixerContext::HeaderInfo)
Haojian Wu62aee522016-07-21 13:47:09 +000033LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(IncludeFixerContext::QuerySymbolInfo)
Haojian Wu17a54e32016-06-01 11:43:10 +000034
35namespace llvm {
36namespace yaml {
Haojian Wu68c34a02016-07-13 16:43:54 +000037
38template <> struct MappingTraits<tooling::Range> {
39 struct NormalizedRange {
40 NormalizedRange(const IO &) : Offset(0), Length(0) {}
41
42 NormalizedRange(const IO &, const tooling::Range &R)
43 : Offset(R.getOffset()), Length(R.getLength()) {}
44
45 tooling::Range denormalize(const IO &) {
46 return tooling::Range(Offset, Length);
47 }
48
49 unsigned Offset;
50 unsigned Length;
51 };
52 static void mapping(IO &IO, tooling::Range &Info) {
53 MappingNormalization<NormalizedRange, tooling::Range> Keys(IO, Info);
54 IO.mapRequired("Offset", Keys->Offset);
55 IO.mapRequired("Length", Keys->Length);
56 }
57};
58
59template <> struct MappingTraits<IncludeFixerContext::HeaderInfo> {
60 static void mapping(IO &io, IncludeFixerContext::HeaderInfo &Info) {
61 io.mapRequired("Header", Info.Header);
62 io.mapRequired("QualifiedName", Info.QualifiedName);
63 }
64};
65
Haojian Wu9e4bd0c2016-07-19 14:49:04 +000066template <> struct MappingTraits<IncludeFixerContext::QuerySymbolInfo> {
67 static void mapping(IO &io, IncludeFixerContext::QuerySymbolInfo &Info) {
68 io.mapRequired("RawIdentifier", Info.RawIdentifier);
69 io.mapRequired("Range", Info.Range);
70 }
71};
72
Haojian Wu17a54e32016-06-01 11:43:10 +000073template <> struct MappingTraits<IncludeFixerContext> {
Haojian Wu68c34a02016-07-13 16:43:54 +000074 static void mapping(IO &IO, IncludeFixerContext &Context) {
Haojian Wu62aee522016-07-21 13:47:09 +000075 IO.mapRequired("QuerySymbolInfos", Context.QuerySymbolInfos);
Haojian Wu68c34a02016-07-13 16:43:54 +000076 IO.mapRequired("HeaderInfos", Context.HeaderInfos);
Haojian Wuc99f7282016-08-09 08:26:19 +000077 IO.mapRequired("FilePath", Context.FilePath);
Haojian Wu17a54e32016-06-01 11:43:10 +000078 }
79};
80} // namespace yaml
81} // namespace llvm
Benjamin Kramer6b236262016-04-20 12:43:43 +000082
Benjamin Kramerf412e902016-04-27 14:24:32 +000083namespace {
84cl::OptionCategory IncludeFixerCategory("Tool options");
85
86enum DatabaseFormatTy {
Sam McCall9c5ebf72017-03-13 15:55:59 +000087 fixed, ///< Hard-coded mapping.
88 yaml, ///< Yaml database created by find-all-symbols.
89 fuzzyYaml, ///< Yaml database with fuzzy-matched identifiers.
Benjamin Kramerf412e902016-04-27 14:24:32 +000090};
91
92cl::opt<DatabaseFormatTy> DatabaseFormat(
93 "db", cl::desc("Specify input format"),
Haojian Wud8c12ba2016-04-29 09:23:38 +000094 cl::values(clEnumVal(fixed, "Hard-coded mapping"),
Sam McCall9c5ebf72017-03-13 15:55:59 +000095 clEnumVal(yaml, "Yaml database created by find-all-symbols"),
96 clEnumVal(fuzzyYaml, "Yaml database, with fuzzy-matched names")),
Benjamin Kramer8fd85a52016-05-10 11:35:47 +000097 cl::init(yaml), cl::cat(IncludeFixerCategory));
Benjamin Kramerf412e902016-04-27 14:24:32 +000098
99cl::opt<std::string> Input("input",
100 cl::desc("String to initialize the database"),
101 cl::cat(IncludeFixerCategory));
Benjamin Kramer3a45fab2016-04-28 11:21:29 +0000102
Haojian Wucd637012016-09-07 16:34:35 +0000103cl::opt<std::string>
104 QuerySymbol("query-symbol",
105 cl::desc("Query a given symbol (e.g. \"a::b::foo\") in\n"
106 "database directly without parsing the file."),
107 cl::cat(IncludeFixerCategory));
108
Benjamin Kramer3a45fab2016-04-28 11:21:29 +0000109cl::opt<bool>
110 MinimizeIncludePaths("minimize-paths",
111 cl::desc("Whether to minimize added include paths"),
112 cl::init(true), cl::cat(IncludeFixerCategory));
Benjamin Kramer6b236262016-04-20 12:43:43 +0000113
Benjamin Kramer54718292016-05-10 08:36:56 +0000114cl::opt<bool> Quiet("q", cl::desc("Reduce terminal output"), cl::init(false),
115 cl::cat(IncludeFixerCategory));
116
Eric Liuc7f3b102016-05-18 14:10:16 +0000117cl::opt<bool>
118 STDINMode("stdin",
119 cl::desc("Override source file's content (in the overlaying\n"
120 "virtual file system) with input from <stdin> and run\n"
121 "the tool on the new content with the compilation\n"
122 "options of the source file. This mode is currently\n"
123 "used for editor integration."),
124 cl::init(false), cl::cat(IncludeFixerCategory));
125
Haojian Wu11e9bd22016-05-31 09:31:51 +0000126cl::opt<bool> OutputHeaders(
127 "output-headers",
Haojian Wu17a54e32016-06-01 11:43:10 +0000128 cl::desc("Print the symbol being queried and all its relevant headers in\n"
129 "JSON format to stdout:\n"
130 " {\n"
Haojian Wuab372642016-08-17 11:31:19 +0000131 " \"FilePath\": \"/path/to/foo.cc\",\n"
Haojian Wu62aee522016-07-21 13:47:09 +0000132 " \"QuerySymbolInfos\": [\n"
133 " {\"RawIdentifier\": \"foo\",\n"
134 " \"Range\": {\"Offset\": 0, \"Length\": 3}}\n"
135 " ],\n"
Haojian Wu68c34a02016-07-13 16:43:54 +0000136 " \"HeaderInfos\": [ {\"Header\": \"\\\"foo_a.h\\\"\",\n"
137 " \"QualifiedName\": \"a::foo\"} ]\n"
Haojian Wu17a54e32016-06-01 11:43:10 +0000138 " }"),
Haojian Wu11e9bd22016-05-31 09:31:51 +0000139 cl::init(false), cl::cat(IncludeFixerCategory));
140
141cl::opt<std::string> InsertHeader(
142 "insert-header",
143 cl::desc("Insert a specific header. This should run with STDIN mode.\n"
144 "The result is written to stdout. It is currently used for\n"
Haojian Wu17a54e32016-06-01 11:43:10 +0000145 "editor integration. Support YAML/JSON format:\n"
Haojian Wu68c34a02016-07-13 16:43:54 +0000146 " -insert-header=\"{\n"
Haojian Wuab372642016-08-17 11:31:19 +0000147 " FilePath: \"/path/to/foo.cc\",\n"
Haojian Wu62aee522016-07-21 13:47:09 +0000148 " QuerySymbolInfos: [\n"
149 " {RawIdentifier: foo,\n"
150 " Range: {Offset: 0, Length: 3}}\n"
151 " ],\n"
Haojian Wu68c34a02016-07-13 16:43:54 +0000152 " HeaderInfos: [ {Headers: \"\\\"foo_a.h\\\"\",\n"
153 " QualifiedName: \"a::foo\"} ]}\""),
Haojian Wu11e9bd22016-05-31 09:31:51 +0000154 cl::init(""), cl::cat(IncludeFixerCategory));
155
Eric Liu702cfd12016-05-19 08:21:09 +0000156cl::opt<std::string>
157 Style("style",
Haojian Wuab372642016-08-17 11:31:19 +0000158 cl::desc("Fallback style for reformatting after inserting new\n"
Eric Liu702cfd12016-05-19 08:21:09 +0000159 "headers if there is no clang-format config file found."),
160 cl::init("llvm"), cl::cat(IncludeFixerCategory));
161
Haojian Wueb6ce062016-05-31 13:23:00 +0000162std::unique_ptr<include_fixer::SymbolIndexManager>
163createSymbolIndexManager(StringRef FilePath) {
Sam McCallb27dc222017-02-28 08:13:15 +0000164 using find_all_symbols::SymbolInfo;
165
Haojian Wueb6ce062016-05-31 13:23:00 +0000166 auto SymbolIndexMgr = llvm::make_unique<include_fixer::SymbolIndexManager>();
167 switch (DatabaseFormat) {
168 case fixed: {
169 // Parse input and fill the database with it.
170 // <symbol>=<header><, header...>
171 // Multiple symbols can be given, separated by semicolons.
172 std::map<std::string, std::vector<std::string>> SymbolsMap;
173 SmallVector<StringRef, 4> SemicolonSplits;
174 StringRef(Input).split(SemicolonSplits, ";");
Sam McCallb27dc222017-02-28 08:13:15 +0000175 std::vector<find_all_symbols::SymbolAndSignals> Symbols;
Haojian Wueb6ce062016-05-31 13:23:00 +0000176 for (StringRef Pair : SemicolonSplits) {
177 auto Split = Pair.split('=');
178 std::vector<std::string> Headers;
179 SmallVector<StringRef, 4> CommaSplits;
180 Split.second.split(CommaSplits, ",");
Benjamin Kramer658d2802016-05-31 14:33:28 +0000181 for (size_t I = 0, E = CommaSplits.size(); I != E; ++I)
Sam McCallb27dc222017-02-28 08:13:15 +0000182 Symbols.push_back(
183 {SymbolInfo(Split.first.trim(), SymbolInfo::SymbolKind::Unknown,
Sam McCall573050e2017-03-09 10:47:44 +0000184 CommaSplits[I].trim(), {}),
Sam McCallb27dc222017-02-28 08:13:15 +0000185 // Use fake "seen" signal for tests, so first header wins.
186 SymbolInfo::Signals(/*Seen=*/static_cast<unsigned>(E - I),
187 /*Used=*/0)});
Haojian Wueb6ce062016-05-31 13:23:00 +0000188 }
Benjamin Kramerbdb21712017-01-09 15:18:28 +0000189 SymbolIndexMgr->addSymbolIndex([=]() {
190 return llvm::make_unique<include_fixer::InMemorySymbolIndex>(Symbols);
191 });
Haojian Wueb6ce062016-05-31 13:23:00 +0000192 break;
193 }
194 case yaml: {
Benjamin Kramerbdb21712017-01-09 15:18:28 +0000195 auto CreateYamlIdx = [=]() -> std::unique_ptr<include_fixer::SymbolIndex> {
196 llvm::ErrorOr<std::unique_ptr<include_fixer::YamlSymbolIndex>> DB(
197 nullptr);
198 if (!Input.empty()) {
199 DB = include_fixer::YamlSymbolIndex::createFromFile(Input);
200 } else {
201 // If we don't have any input file, look in the directory of the
202 // first
203 // file and its parents.
204 SmallString<128> AbsolutePath(tooling::getAbsolutePath(FilePath));
205 StringRef Directory = llvm::sys::path::parent_path(AbsolutePath);
206 DB = include_fixer::YamlSymbolIndex::createFromDirectory(
207 Directory, "find_all_symbols_db.yaml");
208 }
Haojian Wueb6ce062016-05-31 13:23:00 +0000209
Benjamin Kramerbdb21712017-01-09 15:18:28 +0000210 if (!DB) {
211 llvm::errs() << "Couldn't find YAML db: " << DB.getError().message()
212 << '\n';
213 return nullptr;
214 }
215 return std::move(*DB);
216 };
Haojian Wueb6ce062016-05-31 13:23:00 +0000217
Benjamin Kramerbdb21712017-01-09 15:18:28 +0000218 SymbolIndexMgr->addSymbolIndex(std::move(CreateYamlIdx));
Haojian Wueb6ce062016-05-31 13:23:00 +0000219 break;
220 }
Sam McCall9c5ebf72017-03-13 15:55:59 +0000221 case fuzzyYaml: {
222 // This mode is not very useful, because we don't correct the identifier.
223 // It's main purpose is to expose FuzzySymbolIndex to tests.
224 SymbolIndexMgr->addSymbolIndex(
225 []() -> std::unique_ptr<include_fixer::SymbolIndex> {
226 auto DB = include_fixer::FuzzySymbolIndex::createFromYAML(Input);
227 if (!DB) {
228 llvm::errs() << "Couldn't load fuzzy YAML db: "
229 << llvm::toString(DB.takeError()) << '\n';
230 return nullptr;
231 }
232 return std::move(*DB);
233 });
234 break;
235 }
Haojian Wueb6ce062016-05-31 13:23:00 +0000236 }
237 return SymbolIndexMgr;
238}
239
Haojian Wu17a54e32016-06-01 11:43:10 +0000240void writeToJson(llvm::raw_ostream &OS, const IncludeFixerContext& Context) {
241 OS << "{\n"
Haojian Wuc99f7282016-08-09 08:26:19 +0000242 << " \"FilePath\": \""
243 << llvm::yaml::escape(Context.getFilePath()) << "\",\n"
244 << " \"QuerySymbolInfos\": [\n";
Haojian Wu62aee522016-07-21 13:47:09 +0000245 for (const auto &Info : Context.getQuerySymbolInfos()) {
246 OS << " {\"RawIdentifier\": \"" << Info.RawIdentifier << "\",\n";
247 OS << " \"Range\":{";
248 OS << "\"Offset\":" << Info.Range.getOffset() << ",";
249 OS << "\"Length\":" << Info.Range.getLength() << "}}";
250 if (&Info != &Context.getQuerySymbolInfos().back())
251 OS << ",\n";
252 }
253 OS << "\n ],\n";
Haojian Wu68c34a02016-07-13 16:43:54 +0000254 OS << " \"HeaderInfos\": [\n";
255 const auto &HeaderInfos = Context.getHeaderInfos();
256 for (const auto &Info : HeaderInfos) {
257 OS << " {\"Header\": \"" << llvm::yaml::escape(Info.Header) << "\",\n"
258 << " \"QualifiedName\": \"" << Info.QualifiedName << "\"}";
259 if (&Info != &HeaderInfos.back())
260 OS << ",\n";
Haojian Wu17a54e32016-06-01 11:43:10 +0000261 }
Haojian Wu68c34a02016-07-13 16:43:54 +0000262 OS << "\n";
263 OS << " ]\n";
264 OS << "}\n";
Haojian Wu17a54e32016-06-01 11:43:10 +0000265}
266
Haojian Wua315dcb2016-05-03 08:38:35 +0000267int includeFixerMain(int argc, const char **argv) {
Benjamin Kramerf412e902016-04-27 14:24:32 +0000268 tooling::CommonOptionsParser options(argc, argv, IncludeFixerCategory);
269 tooling::ClangTool tool(options.getCompilations(),
270 options.getSourcePathList());
271
Haojian Wucd637012016-09-07 16:34:35 +0000272 llvm::StringRef SourceFilePath = options.getSourcePathList().front();
Eric Liuc7f3b102016-05-18 14:10:16 +0000273 // In STDINMode, we override the file content with the <stdin> input.
274 // Since `tool.mapVirtualFile` takes `StringRef`, we define `Code` outside of
275 // the if-block so that `Code` is not released after the if-block.
276 std::unique_ptr<llvm::MemoryBuffer> Code;
277 if (STDINMode) {
278 assert(options.getSourcePathList().size() == 1 &&
279 "Expect exactly one file path in STDINMode.");
280 llvm::ErrorOr<std::unique_ptr<llvm::MemoryBuffer>> CodeOrErr =
281 MemoryBuffer::getSTDIN();
282 if (std::error_code EC = CodeOrErr.getError()) {
283 errs() << EC.message() << "\n";
284 return 1;
285 }
286 Code = std::move(CodeOrErr.get());
287 if (Code->getBufferSize() == 0)
288 return 0; // Skip empty files.
289
Haojian Wucd637012016-09-07 16:34:35 +0000290 tool.mapVirtualFile(SourceFilePath, Code->getBuffer());
Eric Liuc7f3b102016-05-18 14:10:16 +0000291 }
292
Haojian Wu11e9bd22016-05-31 09:31:51 +0000293 if (!InsertHeader.empty()) {
294 if (!STDINMode) {
295 errs() << "Should be running in STDIN mode\n";
296 return 1;
297 }
298
Haojian Wu17a54e32016-06-01 11:43:10 +0000299 llvm::yaml::Input yin(InsertHeader);
300 IncludeFixerContext Context;
301 yin >> Context;
302
Haojian Wu68c34a02016-07-13 16:43:54 +0000303 const auto &HeaderInfos = Context.getHeaderInfos();
304 assert(!HeaderInfos.empty());
305 // We only accept one unique header.
306 // Check all elements in HeaderInfos have the same header.
307 bool IsUniqueHeader = std::equal(
308 HeaderInfos.begin()+1, HeaderInfos.end(), HeaderInfos.begin(),
309 [](const IncludeFixerContext::HeaderInfo &LHS,
310 const IncludeFixerContext::HeaderInfo &RHS) {
311 return LHS.Header == RHS.Header;
312 });
313 if (!IsUniqueHeader) {
314 errs() << "Expect exactly one unique header.\n";
Haojian Wu17a54e32016-06-01 11:43:10 +0000315 return 1;
316 }
317
Haojian Wu62aee522016-07-21 13:47:09 +0000318 // If a header has multiple symbols, we won't add the missing namespace
Haojian Wu68c34a02016-07-13 16:43:54 +0000319 // qualifiers because we don't know which one is exactly used.
320 //
321 // Check whether all elements in HeaderInfos have the same qualified name.
322 bool IsUniqueQualifiedName = std::equal(
323 HeaderInfos.begin() + 1, HeaderInfos.end(), HeaderInfos.begin(),
324 [](const IncludeFixerContext::HeaderInfo &LHS,
325 const IncludeFixerContext::HeaderInfo &RHS) {
326 return LHS.QualifiedName == RHS.QualifiedName;
327 });
Antonio Maiorano0d7d9c22017-01-17 00:13:32 +0000328 auto InsertStyle = format::getStyle("file", Context.getFilePath(), Style);
329 if (!InsertStyle) {
330 llvm::errs() << llvm::toString(InsertStyle.takeError()) << "\n";
331 return 1;
332 }
Haojian Wu62aee522016-07-21 13:47:09 +0000333 auto Replacements = clang::include_fixer::createIncludeFixerReplacements(
Antonio Maiorano0d7d9c22017-01-17 00:13:32 +0000334 Code->getBuffer(), Context, *InsertStyle,
Haojian Wu62aee522016-07-21 13:47:09 +0000335 /*AddQualifiers=*/IsUniqueQualifiedName);
336 if (!Replacements) {
337 errs() << "Failed to create replacements: "
338 << llvm::toString(Replacements.takeError()) << "\n";
339 return 1;
340 }
341
Eric Liua452db42016-07-11 13:53:21 +0000342 auto ChangedCode =
343 tooling::applyAllReplacements(Code->getBuffer(), *Replacements);
344 if (!ChangedCode) {
345 llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
346 return 1;
347 }
348 llvm::outs() << *ChangedCode;
Haojian Wu11e9bd22016-05-31 09:31:51 +0000349 return 0;
350 }
351
Eric Liu692aca62016-05-04 08:22:35 +0000352 // Set up data source.
Haojian Wueb6ce062016-05-31 13:23:00 +0000353 std::unique_ptr<include_fixer::SymbolIndexManager> SymbolIndexMgr =
Haojian Wucd637012016-09-07 16:34:35 +0000354 createSymbolIndexManager(SourceFilePath);
Haojian Wueb6ce062016-05-31 13:23:00 +0000355 if (!SymbolIndexMgr)
356 return 1;
Benjamin Kramer6b236262016-04-20 12:43:43 +0000357
Haojian Wucd637012016-09-07 16:34:35 +0000358 // Query symbol mode.
359 if (!QuerySymbol.empty()) {
Manuel Klimeka47515e2017-01-11 10:32:47 +0000360 auto MatchedSymbols = SymbolIndexMgr->search(
361 QuerySymbol, /*IsNestedSearch=*/true, SourceFilePath);
Haojian Wucd637012016-09-07 16:34:35 +0000362 for (auto &Symbol : MatchedSymbols) {
363 std::string HeaderPath = Symbol.getFilePath().str();
364 Symbol.SetFilePath(((HeaderPath[0] == '"' || HeaderPath[0] == '<')
365 ? HeaderPath
366 : "\"" + HeaderPath + "\""));
367 }
368
369 // We leave an empty symbol range as we don't know the range of the symbol
370 // being queried in this mode. include-fixer won't add namespace qualifiers
371 // if the symbol range is empty, which also fits this case.
372 IncludeFixerContext::QuerySymbolInfo Symbol;
373 Symbol.RawIdentifier = QuerySymbol;
374 auto Context =
375 IncludeFixerContext(SourceFilePath, {Symbol}, MatchedSymbols);
376 writeToJson(llvm::outs(), Context);
377 return 0;
378 }
379
Benjamin Kramer6b236262016-04-20 12:43:43 +0000380 // Now run our tool.
Haojian Wuc99f7282016-08-09 08:26:19 +0000381 std::vector<include_fixer::IncludeFixerContext> Contexts;
382 include_fixer::IncludeFixerActionFactory Factory(*SymbolIndexMgr, Contexts,
Haojian Wu11e9bd22016-05-31 09:31:51 +0000383 Style, MinimizeIncludePaths);
Benjamin Kramer6b236262016-04-20 12:43:43 +0000384
Benjamin Kramer6b5160a2016-05-18 13:32:38 +0000385 if (tool.run(&Factory) != 0) {
Benjamin Kramer2df9a3f2016-10-28 13:00:49 +0000386 // We suppress all Clang diagnostics (because they would be wrong,
387 // include-fixer does custom recovery) but still want to give some feedback
388 // in case there was a compiler error we couldn't recover from. The most
389 // common case for this is a #include in the file that couldn't be found.
390 llvm::errs() << "Fatal compiler error occurred while parsing file!"
391 " (incorrect include paths?)\n";
Benjamin Kramer6b5160a2016-05-18 13:32:38 +0000392 return 1;
393 }
Benjamin Kramer6b236262016-04-20 12:43:43 +0000394
Haojian Wuc99f7282016-08-09 08:26:19 +0000395 assert(!Contexts.empty());
396
Haojian Wu11e9bd22016-05-31 09:31:51 +0000397 if (OutputHeaders) {
Haojian Wuc99f7282016-08-09 08:26:19 +0000398 // FIXME: Print contexts of all processing files instead of the first one.
399 writeToJson(llvm::outs(), Contexts.front());
Haojian Wu11e9bd22016-05-31 09:31:51 +0000400 return 0;
401 }
402
Haojian Wuc99f7282016-08-09 08:26:19 +0000403 std::vector<tooling::Replacements> FixerReplacements;
404 for (const auto &Context : Contexts) {
405 StringRef FilePath = Context.getFilePath();
Antonio Maiorano0d7d9c22017-01-17 00:13:32 +0000406 auto InsertStyle = format::getStyle("file", FilePath, Style);
407 if (!InsertStyle) {
408 llvm::errs() << llvm::toString(InsertStyle.takeError()) << "\n";
409 return 1;
410 }
Haojian Wuc99f7282016-08-09 08:26:19 +0000411 auto Buffer = llvm::MemoryBuffer::getFile(FilePath);
412 if (!Buffer) {
413 errs() << "Couldn't open file: " + FilePath.str() + ": "
414 << Buffer.getError().message() + "\n";
415 return 1;
416 }
Haojian Wu11e9bd22016-05-31 09:31:51 +0000417
Haojian Wuc99f7282016-08-09 08:26:19 +0000418 auto Replacements = clang::include_fixer::createIncludeFixerReplacements(
Antonio Maiorano0d7d9c22017-01-17 00:13:32 +0000419 Buffer.get()->getBuffer(), Context, *InsertStyle);
Haojian Wuc99f7282016-08-09 08:26:19 +0000420 if (!Replacements) {
421 errs() << "Failed to create replacement: "
422 << llvm::toString(Replacements.takeError()) << "\n";
423 return 1;
424 }
425 FixerReplacements.push_back(*Replacements);
Haojian Wu11e9bd22016-05-31 09:31:51 +0000426 }
427
Haojian Wuc99f7282016-08-09 08:26:19 +0000428 if (!Quiet) {
429 for (const auto &Context : Contexts) {
430 if (!Context.getHeaderInfos().empty()) {
431 llvm::errs() << "Added #include "
432 << Context.getHeaderInfos().front().Header << " for "
433 << Context.getFilePath() << "\n";
434 }
435 }
Eric Liua452db42016-07-11 13:53:21 +0000436 }
Haojian Wu11e9bd22016-05-31 09:31:51 +0000437
Eric Liuc7f3b102016-05-18 14:10:16 +0000438 if (STDINMode) {
Haojian Wuc99f7282016-08-09 08:26:19 +0000439 assert(FixerReplacements.size() == 1);
440 auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(),
441 FixerReplacements.front());
Eric Liua452db42016-07-11 13:53:21 +0000442 if (!ChangedCode) {
443 llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
444 return 1;
445 }
446 llvm::outs() << *ChangedCode;
Eric Liuc7f3b102016-05-18 14:10:16 +0000447 return 0;
448 }
449
Haojian Wuc99f7282016-08-09 08:26:19 +0000450 // Set up a new source manager for applying the resulting replacements.
451 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions);
452 DiagnosticsEngine Diagnostics(new DiagnosticIDs, &*DiagOpts);
453 TextDiagnosticPrinter DiagnosticPrinter(outs(), &*DiagOpts);
454 SourceManager SM(Diagnostics, tool.getFiles());
455 Diagnostics.setClient(&DiagnosticPrinter, false);
456
Benjamin Kramer6b236262016-04-20 12:43:43 +0000457 // Write replacements to disk.
Benjamin Kramerf412e902016-04-27 14:24:32 +0000458 Rewriter Rewrites(SM, LangOptions());
Benjamin Kramer089a39e2016-10-19 13:50:17 +0000459 for (const auto &Replacement : FixerReplacements) {
Haojian Wuc99f7282016-08-09 08:26:19 +0000460 if (!tooling::applyAllReplacements(Replacement, Rewrites)) {
461 llvm::errs() << "Failed to apply replacements.\n";
462 return 1;
463 }
464 }
Benjamin Kramer6b236262016-04-20 12:43:43 +0000465 return Rewrites.overwriteChangedFiles();
466}
Haojian Wua315dcb2016-05-03 08:38:35 +0000467
468} // namespace
469
470int main(int argc, const char **argv) {
471 return includeFixerMain(argc, argv);
472}