Run DCE after a LoopFlatten test to reduce spurious output [nfc]
[llvm-project.git] / clang / tools / clang-format / ClangFormat.cpp
blobcb6e7460a66ecb759caeb8045b0cea25476a08d9
1 //===-- clang-format/ClangFormat.cpp - Clang format tool ------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 ///
9 /// \file
10 /// This file implements a clang-format tool that automatically formats
11 /// (fragments of) C++ code.
12 ///
13 //===----------------------------------------------------------------------===//
15 #include "clang/Basic/Diagnostic.h"
16 #include "clang/Basic/DiagnosticOptions.h"
17 #include "clang/Basic/FileManager.h"
18 #include "clang/Basic/SourceManager.h"
19 #include "clang/Basic/Version.h"
20 #include "clang/Format/Format.h"
21 #include "clang/Rewrite/Core/Rewriter.h"
22 #include "llvm/ADT/StringSwitch.h"
23 #include "llvm/Support/CommandLine.h"
24 #include "llvm/Support/FileSystem.h"
25 #include "llvm/Support/InitLLVM.h"
26 #include "llvm/Support/Process.h"
27 #include <fstream>
29 using namespace llvm;
30 using clang::tooling::Replacements;
32 static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
34 // Mark all our options with this category, everything else (except for -version
35 // and -help) will be hidden.
36 static cl::OptionCategory ClangFormatCategory("Clang-format options");
38 static cl::list<unsigned>
39 Offsets("offset",
40 cl::desc("Format a range starting at this byte offset.\n"
41 "Multiple ranges can be formatted by specifying\n"
42 "several -offset and -length pairs.\n"
43 "Can only be used with one input file."),
44 cl::cat(ClangFormatCategory));
45 static cl::list<unsigned>
46 Lengths("length",
47 cl::desc("Format a range of this length (in bytes).\n"
48 "Multiple ranges can be formatted by specifying\n"
49 "several -offset and -length pairs.\n"
50 "When only a single -offset is specified without\n"
51 "-length, clang-format will format up to the end\n"
52 "of the file.\n"
53 "Can only be used with one input file."),
54 cl::cat(ClangFormatCategory));
55 static cl::list<std::string>
56 LineRanges("lines",
57 cl::desc("<start line>:<end line> - format a range of\n"
58 "lines (both 1-based).\n"
59 "Multiple ranges can be formatted by specifying\n"
60 "several -lines arguments.\n"
61 "Can't be used with -offset and -length.\n"
62 "Can only be used with one input file."),
63 cl::cat(ClangFormatCategory));
64 static cl::opt<std::string>
65 Style("style", cl::desc(clang::format::StyleOptionHelpDescription),
66 cl::init(clang::format::DefaultFormatStyle),
67 cl::cat(ClangFormatCategory));
68 static cl::opt<std::string>
69 FallbackStyle("fallback-style",
70 cl::desc("The name of the predefined style used as a\n"
71 "fallback in case clang-format is invoked with\n"
72 "-style=file, but can not find the .clang-format\n"
73 "file to use. Defaults to 'LLVM'.\n"
74 "Use -fallback-style=none to skip formatting."),
75 cl::init(clang::format::DefaultFallbackStyle),
76 cl::cat(ClangFormatCategory));
78 static cl::opt<std::string> AssumeFileName(
79 "assume-filename",
80 cl::desc("Set filename used to determine the language and to find\n"
81 ".clang-format file.\n"
82 "Only used when reading from stdin.\n"
83 "If this is not passed, the .clang-format file is searched\n"
84 "relative to the current working directory when reading stdin.\n"
85 "Unrecognized filenames are treated as C++.\n"
86 "supported:\n"
87 " CSharp: .cs\n"
88 " Java: .java\n"
89 " JavaScript: .mjs .js .ts\n"
90 " Json: .json\n"
91 " Objective-C: .m .mm\n"
92 " Proto: .proto .protodevel\n"
93 " TableGen: .td\n"
94 " TextProto: .textpb .pb.txt .textproto .asciipb\n"
95 " Verilog: .sv .svh .v .vh"),
96 cl::init("<stdin>"), cl::cat(ClangFormatCategory));
98 static cl::opt<bool> Inplace("i",
99 cl::desc("Inplace edit <file>s, if specified."),
100 cl::cat(ClangFormatCategory));
102 static cl::opt<bool> OutputXML("output-replacements-xml",
103 cl::desc("Output replacements as XML."),
104 cl::cat(ClangFormatCategory));
105 static cl::opt<bool>
106 DumpConfig("dump-config",
107 cl::desc("Dump configuration options to stdout and exit.\n"
108 "Can be used with -style option."),
109 cl::cat(ClangFormatCategory));
110 static cl::opt<unsigned>
111 Cursor("cursor",
112 cl::desc("The position of the cursor when invoking\n"
113 "clang-format from an editor integration"),
114 cl::init(0), cl::cat(ClangFormatCategory));
116 static cl::opt<bool>
117 SortIncludes("sort-includes",
118 cl::desc("If set, overrides the include sorting behavior\n"
119 "determined by the SortIncludes style flag"),
120 cl::cat(ClangFormatCategory));
122 static cl::opt<std::string> QualifierAlignment(
123 "qualifier-alignment",
124 cl::desc("If set, overrides the qualifier alignment style\n"
125 "determined by the QualifierAlignment style flag"),
126 cl::init(""), cl::cat(ClangFormatCategory));
128 static cl::opt<std::string> Files(
129 "files",
130 cl::desc("A file containing a list of files to process, one per line."),
131 cl::value_desc("filename"), cl::init(""), cl::cat(ClangFormatCategory));
133 static cl::opt<bool>
134 Verbose("verbose", cl::desc("If set, shows the list of processed files"),
135 cl::cat(ClangFormatCategory));
137 // Use --dry-run to match other LLVM tools when you mean do it but don't
138 // actually do it
139 static cl::opt<bool>
140 DryRun("dry-run",
141 cl::desc("If set, do not actually make the formatting changes"),
142 cl::cat(ClangFormatCategory));
144 // Use -n as a common command as an alias for --dry-run. (git and make use -n)
145 static cl::alias DryRunShort("n", cl::desc("Alias for --dry-run"),
146 cl::cat(ClangFormatCategory), cl::aliasopt(DryRun),
147 cl::NotHidden);
149 // Emulate being able to turn on/off the warning.
150 static cl::opt<bool>
151 WarnFormat("Wclang-format-violations",
152 cl::desc("Warnings about individual formatting changes needed. "
153 "Used only with --dry-run or -n"),
154 cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);
156 static cl::opt<bool>
157 NoWarnFormat("Wno-clang-format-violations",
158 cl::desc("Do not warn about individual formatting changes "
159 "needed. Used only with --dry-run or -n"),
160 cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
162 static cl::opt<unsigned> ErrorLimit(
163 "ferror-limit",
164 cl::desc("Set the maximum number of clang-format errors to emit\n"
165 "before stopping (0 = no limit).\n"
166 "Used only with --dry-run or -n"),
167 cl::init(0), cl::cat(ClangFormatCategory));
169 static cl::opt<bool>
170 WarningsAsErrors("Werror",
171 cl::desc("If set, changes formatting warnings to errors"),
172 cl::cat(ClangFormatCategory));
174 namespace {
175 enum class WNoError { Unknown };
178 static cl::bits<WNoError> WNoErrorList(
179 "Wno-error",
180 cl::desc("If set don't error out on the specified warning type."),
181 cl::values(
182 clEnumValN(WNoError::Unknown, "unknown",
183 "If set, unknown format options are only warned about.\n"
184 "This can be used to enable formatting, even if the\n"
185 "configuration contains unknown (newer) options.\n"
186 "Use with caution, as this might lead to dramatically\n"
187 "differing format depending on an option being\n"
188 "supported or not.")),
189 cl::cat(ClangFormatCategory));
191 static cl::opt<bool>
192 ShowColors("fcolor-diagnostics",
193 cl::desc("If set, and on a color-capable terminal controls "
194 "whether or not to print diagnostics in color"),
195 cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden);
197 static cl::opt<bool>
198 NoShowColors("fno-color-diagnostics",
199 cl::desc("If set, and on a color-capable terminal controls "
200 "whether or not to print diagnostics in color"),
201 cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden);
203 static cl::list<std::string> FileNames(cl::Positional,
204 cl::desc("[@<file>] [<file> ...]"),
205 cl::cat(ClangFormatCategory));
207 namespace clang {
208 namespace format {
210 static FileID createInMemoryFile(StringRef FileName, MemoryBufferRef Source,
211 SourceManager &Sources, FileManager &Files,
212 llvm::vfs::InMemoryFileSystem *MemFS) {
213 MemFS->addFileNoOwn(FileName, 0, Source);
214 auto File = Files.getOptionalFileRef(FileName);
215 assert(File && "File not added to MemFS?");
216 return Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User);
219 // Parses <start line>:<end line> input to a pair of line numbers.
220 // Returns true on error.
221 static bool parseLineRange(StringRef Input, unsigned &FromLine,
222 unsigned &ToLine) {
223 std::pair<StringRef, StringRef> LineRange = Input.split(':');
224 return LineRange.first.getAsInteger(0, FromLine) ||
225 LineRange.second.getAsInteger(0, ToLine);
228 static bool fillRanges(MemoryBuffer *Code,
229 std::vector<tooling::Range> &Ranges) {
230 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
231 new llvm::vfs::InMemoryFileSystem);
232 FileManager Files(FileSystemOptions(), InMemoryFileSystem);
233 DiagnosticsEngine Diagnostics(
234 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
235 new DiagnosticOptions);
236 SourceManager Sources(Diagnostics, Files);
237 FileID ID = createInMemoryFile("<irrelevant>", *Code, Sources, Files,
238 InMemoryFileSystem.get());
239 if (!LineRanges.empty()) {
240 if (!Offsets.empty() || !Lengths.empty()) {
241 errs() << "error: cannot use -lines with -offset/-length\n";
242 return true;
245 for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) {
246 unsigned FromLine, ToLine;
247 if (parseLineRange(LineRanges[i], FromLine, ToLine)) {
248 errs() << "error: invalid <start line>:<end line> pair\n";
249 return true;
251 if (FromLine < 1) {
252 errs() << "error: start line should be at least 1\n";
253 return true;
255 if (FromLine > ToLine) {
256 errs() << "error: start line should not exceed end line\n";
257 return true;
259 SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1);
260 SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX);
261 if (Start.isInvalid() || End.isInvalid())
262 return true;
263 unsigned Offset = Sources.getFileOffset(Start);
264 unsigned Length = Sources.getFileOffset(End) - Offset;
265 Ranges.push_back(tooling::Range(Offset, Length));
267 return false;
270 if (Offsets.empty())
271 Offsets.push_back(0);
272 if (Offsets.size() != Lengths.size() &&
273 !(Offsets.size() == 1 && Lengths.empty())) {
274 errs() << "error: number of -offset and -length arguments must match.\n";
275 return true;
277 for (unsigned i = 0, e = Offsets.size(); i != e; ++i) {
278 if (Offsets[i] >= Code->getBufferSize()) {
279 errs() << "error: offset " << Offsets[i] << " is outside the file\n";
280 return true;
282 SourceLocation Start =
283 Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
284 SourceLocation End;
285 if (i < Lengths.size()) {
286 if (Offsets[i] + Lengths[i] > Code->getBufferSize()) {
287 errs() << "error: invalid length " << Lengths[i]
288 << ", offset + length (" << Offsets[i] + Lengths[i]
289 << ") is outside the file.\n";
290 return true;
292 End = Start.getLocWithOffset(Lengths[i]);
293 } else {
294 End = Sources.getLocForEndOfFile(ID);
296 unsigned Offset = Sources.getFileOffset(Start);
297 unsigned Length = Sources.getFileOffset(End) - Offset;
298 Ranges.push_back(tooling::Range(Offset, Length));
300 return false;
303 static void outputReplacementXML(StringRef Text) {
304 // FIXME: When we sort includes, we need to make sure the stream is correct
305 // utf-8.
306 size_t From = 0;
307 size_t Index;
308 while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) {
309 outs() << Text.substr(From, Index - From);
310 switch (Text[Index]) {
311 case '\n':
312 outs() << "&#10;";
313 break;
314 case '\r':
315 outs() << "&#13;";
316 break;
317 case '<':
318 outs() << "&lt;";
319 break;
320 case '&':
321 outs() << "&amp;";
322 break;
323 default:
324 llvm_unreachable("Unexpected character encountered!");
326 From = Index + 1;
328 outs() << Text.substr(From);
331 static void outputReplacementsXML(const Replacements &Replaces) {
332 for (const auto &R : Replaces) {
333 outs() << "<replacement "
334 << "offset='" << R.getOffset() << "' "
335 << "length='" << R.getLength() << "'>";
336 outputReplacementXML(R.getReplacementText());
337 outs() << "</replacement>\n";
341 static bool
342 emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName,
343 const std::unique_ptr<llvm::MemoryBuffer> &Code) {
344 if (Replaces.empty())
345 return false;
347 unsigned Errors = 0;
348 if (WarnFormat && !NoWarnFormat) {
349 llvm::SourceMgr Mgr;
350 const char *StartBuf = Code->getBufferStart();
352 Mgr.AddNewSourceBuffer(
353 MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc());
354 for (const auto &R : Replaces) {
355 SMDiagnostic Diag = Mgr.GetMessage(
356 SMLoc::getFromPointer(StartBuf + R.getOffset()),
357 WarningsAsErrors ? SourceMgr::DiagKind::DK_Error
358 : SourceMgr::DiagKind::DK_Warning,
359 "code should be clang-formatted [-Wclang-format-violations]");
361 Diag.print(nullptr, llvm::errs(), (ShowColors && !NoShowColors));
362 if (ErrorLimit && ++Errors >= ErrorLimit)
363 break;
366 return WarningsAsErrors;
369 static void outputXML(const Replacements &Replaces,
370 const Replacements &FormatChanges,
371 const FormattingAttemptStatus &Status,
372 const cl::opt<unsigned> &Cursor,
373 unsigned CursorPosition) {
374 outs() << "<?xml version='1.0'?>\n<replacements "
375 "xml:space='preserve' incomplete_format='"
376 << (Status.FormatComplete ? "false" : "true") << "'";
377 if (!Status.FormatComplete)
378 outs() << " line='" << Status.Line << "'";
379 outs() << ">\n";
380 if (Cursor.getNumOccurrences() != 0) {
381 outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition)
382 << "</cursor>\n";
385 outputReplacementsXML(Replaces);
386 outs() << "</replacements>\n";
389 class ClangFormatDiagConsumer : public DiagnosticConsumer {
390 virtual void anchor() {}
392 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
393 const Diagnostic &Info) override {
395 SmallVector<char, 16> vec;
396 Info.FormatDiagnostic(vec);
397 errs() << "clang-format error:" << vec << "\n";
401 // Returns true on error.
402 static bool format(StringRef FileName) {
403 if (!OutputXML && Inplace && FileName == "-") {
404 errs() << "error: cannot use -i when reading from stdin.\n";
405 return false;
407 // On Windows, overwriting a file with an open file mapping doesn't work,
408 // so read the whole file into memory when formatting in-place.
409 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
410 !OutputXML && Inplace ? MemoryBuffer::getFileAsStream(FileName)
411 : MemoryBuffer::getFileOrSTDIN(FileName);
412 if (std::error_code EC = CodeOrErr.getError()) {
413 errs() << EC.message() << "\n";
414 return true;
416 std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
417 if (Code->getBufferSize() == 0)
418 return false; // Empty files are formatted correctly.
420 StringRef BufStr = Code->getBuffer();
422 const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
424 if (InvalidBOM) {
425 errs() << "error: encoding with unsupported byte order mark \""
426 << InvalidBOM << "\" detected";
427 if (FileName != "-")
428 errs() << " in file '" << FileName << "'";
429 errs() << ".\n";
430 return true;
433 std::vector<tooling::Range> Ranges;
434 if (fillRanges(Code.get(), Ranges))
435 return true;
436 StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName;
437 if (AssumedFileName.empty()) {
438 llvm::errs() << "error: empty filenames are not allowed\n";
439 return true;
442 llvm::Expected<FormatStyle> FormatStyle =
443 getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
444 nullptr, WNoErrorList.isSet(WNoError::Unknown));
445 if (!FormatStyle) {
446 llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
447 return true;
450 StringRef QualifierAlignmentOrder = QualifierAlignment;
452 FormatStyle->QualifierAlignment =
453 StringSwitch<FormatStyle::QualifierAlignmentStyle>(
454 QualifierAlignmentOrder.lower())
455 .Case("right", FormatStyle::QAS_Right)
456 .Case("left", FormatStyle::QAS_Left)
457 .Default(FormatStyle->QualifierAlignment);
459 if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {
460 FormatStyle->QualifierOrder = {"const", "volatile", "type"};
461 } else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {
462 FormatStyle->QualifierOrder = {"type", "const", "volatile"};
463 } else if (QualifierAlignmentOrder.contains("type")) {
464 FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;
465 SmallVector<StringRef> Qualifiers;
466 QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,
467 /*KeepEmpty=*/false);
468 FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
471 if (SortIncludes.getNumOccurrences() != 0) {
472 if (SortIncludes)
473 FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive;
474 else
475 FormatStyle->SortIncludes = FormatStyle::SI_Never;
477 unsigned CursorPosition = Cursor;
478 Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges,
479 AssumedFileName, &CursorPosition);
481 // To format JSON insert a variable to trick the code into thinking its
482 // JavaScript.
483 if (FormatStyle->isJson() && !FormatStyle->DisableFormat) {
484 auto Err = Replaces.add(tooling::Replacement(
485 tooling::Replacement(AssumedFileName, 0, 0, "x = ")));
486 if (Err)
487 llvm::errs() << "Bad Json variable insertion\n";
490 auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
491 if (!ChangedCode) {
492 llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
493 return true;
495 // Get new affected ranges after sorting `#includes`.
496 Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges);
497 FormattingAttemptStatus Status;
498 Replacements FormatChanges =
499 reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status);
500 Replaces = Replaces.merge(FormatChanges);
501 if (OutputXML || DryRun) {
502 if (DryRun)
503 return emitReplacementWarnings(Replaces, AssumedFileName, Code);
504 else
505 outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition);
506 } else {
507 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
508 new llvm::vfs::InMemoryFileSystem);
509 FileManager Files(FileSystemOptions(), InMemoryFileSystem);
511 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
512 ClangFormatDiagConsumer IgnoreDiagnostics;
513 DiagnosticsEngine Diagnostics(
514 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
515 &IgnoreDiagnostics, false);
516 SourceManager Sources(Diagnostics, Files);
517 FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files,
518 InMemoryFileSystem.get());
519 Rewriter Rewrite(Sources, LangOptions());
520 tooling::applyAllReplacements(Replaces, Rewrite);
521 if (Inplace) {
522 if (Rewrite.overwriteChangedFiles())
523 return true;
524 } else {
525 if (Cursor.getNumOccurrences() != 0) {
526 outs() << "{ \"Cursor\": "
527 << FormatChanges.getShiftedCodePosition(CursorPosition)
528 << ", \"IncompleteFormat\": "
529 << (Status.FormatComplete ? "false" : "true");
530 if (!Status.FormatComplete)
531 outs() << ", \"Line\": " << Status.Line;
532 outs() << " }\n";
534 Rewrite.getEditBuffer(ID).write(outs());
537 return false;
540 } // namespace format
541 } // namespace clang
543 static void PrintVersion(raw_ostream &OS) {
544 OS << clang::getClangToolFullVersion("clang-format") << '\n';
547 // Dump the configuration.
548 static int dumpConfig() {
549 StringRef FileName;
550 std::unique_ptr<llvm::MemoryBuffer> Code;
551 if (FileNames.empty()) {
552 // We can't read the code to detect the language if there's no
553 // file name, so leave Code empty here.
554 FileName = AssumeFileName;
555 } else {
556 // Read in the code in case the filename alone isn't enough to
557 // detect the language.
558 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
559 MemoryBuffer::getFileOrSTDIN(FileNames[0]);
560 if (std::error_code EC = CodeOrErr.getError()) {
561 llvm::errs() << EC.message() << "\n";
562 return 1;
564 FileName = (FileNames[0] == "-") ? AssumeFileName : FileNames[0];
565 Code = std::move(CodeOrErr.get());
567 llvm::Expected<clang::format::FormatStyle> FormatStyle =
568 clang::format::getStyle(Style, FileName, FallbackStyle,
569 Code ? Code->getBuffer() : "");
570 if (!FormatStyle) {
571 llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
572 return 1;
574 std::string Config = clang::format::configurationAsText(*FormatStyle);
575 outs() << Config << "\n";
576 return 0;
579 int main(int argc, const char **argv) {
580 llvm::InitLLVM X(argc, argv);
582 cl::HideUnrelatedOptions(ClangFormatCategory);
584 cl::SetVersionPrinter(PrintVersion);
585 cl::ParseCommandLineOptions(
586 argc, argv,
587 "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# "
588 "code.\n\n"
589 "If no arguments are specified, it formats the code from standard input\n"
590 "and writes the result to the standard output.\n"
591 "If <file>s are given, it reformats the files. If -i is specified\n"
592 "together with <file>s, the files are edited in-place. Otherwise, the\n"
593 "result is written to the standard output.\n");
595 if (Help) {
596 cl::PrintHelpMessage();
597 return 0;
600 if (DumpConfig)
601 return dumpConfig();
603 if (!Files.empty()) {
604 std::ifstream ExternalFileOfFiles{std::string(Files)};
605 std::string Line;
606 unsigned LineNo = 1;
607 while (std::getline(ExternalFileOfFiles, Line)) {
608 FileNames.push_back(Line);
609 LineNo++;
611 errs() << "Clang-formating " << LineNo << " files\n";
614 bool Error = false;
615 if (FileNames.empty()) {
616 Error = clang::format::format("-");
617 return Error ? 1 : 0;
619 if (FileNames.size() != 1 &&
620 (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
621 errs() << "error: -offset, -length and -lines can only be used for "
622 "single file.\n";
623 return 1;
626 unsigned FileNo = 1;
627 for (const auto &FileName : FileNames) {
628 if (Verbose) {
629 errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
630 << FileName << "\n";
632 Error |= clang::format::format(FileName);
634 return Error ? 1 : 0;