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 //===----------------------------------------------------------------------===//
14
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>
28
29 using namespace llvm;
30 using clang::tooling::Replacements;
31
32 static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden);
33
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");
37
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));
77
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));
97
98 static cl::opt<bool> Inplace("i",
99 cl::desc("Inplace edit <file>s, if specified."),
100 cl::cat(ClangFormatCategory));
101
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));
115
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));
121
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));
127
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));
132
133 static cl::opt<bool>
134 Verbose("verbose", cl::desc("If set, shows the list of processed files"),
135 cl::cat(ClangFormatCategory));
136
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));
143
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);
148
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);
155
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);
161
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));
168
169 static cl::opt<bool>
170 WarningsAsErrors("Werror",
171 cl::desc("If set, changes formatting warnings to errors"),
172 cl::cat(ClangFormatCategory));
173
174 namespace {
175 enum class WNoError { Unknown };
176 }
177
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));
190
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);
196
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);
202
203 static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"),
204 cl::cat(ClangFormatCategory));
205
206 namespace clang {
207 namespace format {
208
createInMemoryFile(StringRef FileName,MemoryBufferRef Source,SourceManager & Sources,FileManager & Files,llvm::vfs::InMemoryFileSystem * MemFS)209 static FileID createInMemoryFile(StringRef FileName, MemoryBufferRef Source,
210 SourceManager &Sources, FileManager &Files,
211 llvm::vfs::InMemoryFileSystem *MemFS) {
212 MemFS->addFileNoOwn(FileName, 0, Source);
213 auto File = Files.getOptionalFileRef(FileName);
214 assert(File && "File not added to MemFS?");
215 return Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User);
216 }
217
218 // Parses <start line>:<end line> input to a pair of line numbers.
219 // Returns true on error.
parseLineRange(StringRef Input,unsigned & FromLine,unsigned & ToLine)220 static bool parseLineRange(StringRef Input, unsigned &FromLine,
221 unsigned &ToLine) {
222 std::pair<StringRef, StringRef> LineRange = Input.split(':');
223 return LineRange.first.getAsInteger(0, FromLine) ||
224 LineRange.second.getAsInteger(0, ToLine);
225 }
226
fillRanges(MemoryBuffer * Code,std::vector<tooling::Range> & Ranges)227 static bool fillRanges(MemoryBuffer *Code,
228 std::vector<tooling::Range> &Ranges) {
229 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
230 new llvm::vfs::InMemoryFileSystem);
231 FileManager Files(FileSystemOptions(), InMemoryFileSystem);
232 DiagnosticsEngine Diagnostics(
233 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs),
234 new DiagnosticOptions);
235 SourceManager Sources(Diagnostics, Files);
236 FileID ID = createInMemoryFile("<irrelevant>", *Code, Sources, Files,
237 InMemoryFileSystem.get());
238 if (!LineRanges.empty()) {
239 if (!Offsets.empty() || !Lengths.empty()) {
240 errs() << "error: cannot use -lines with -offset/-length\n";
241 return true;
242 }
243
244 for (unsigned i = 0, e = LineRanges.size(); i < e; ++i) {
245 unsigned FromLine, ToLine;
246 if (parseLineRange(LineRanges[i], FromLine, ToLine)) {
247 errs() << "error: invalid <start line>:<end line> pair\n";
248 return true;
249 }
250 if (FromLine < 1) {
251 errs() << "error: start line should be at least 1\n";
252 return true;
253 }
254 if (FromLine > ToLine) {
255 errs() << "error: start line should not exceed end line\n";
256 return true;
257 }
258 SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1);
259 SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX);
260 if (Start.isInvalid() || End.isInvalid())
261 return true;
262 unsigned Offset = Sources.getFileOffset(Start);
263 unsigned Length = Sources.getFileOffset(End) - Offset;
264 Ranges.push_back(tooling::Range(Offset, Length));
265 }
266 return false;
267 }
268
269 if (Offsets.empty())
270 Offsets.push_back(0);
271 if (Offsets.size() != Lengths.size() &&
272 !(Offsets.size() == 1 && Lengths.empty())) {
273 errs() << "error: number of -offset and -length arguments must match.\n";
274 return true;
275 }
276 for (unsigned i = 0, e = Offsets.size(); i != e; ++i) {
277 if (Offsets[i] >= Code->getBufferSize()) {
278 errs() << "error: offset " << Offsets[i] << " is outside the file\n";
279 return true;
280 }
281 SourceLocation Start =
282 Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]);
283 SourceLocation End;
284 if (i < Lengths.size()) {
285 if (Offsets[i] + Lengths[i] > Code->getBufferSize()) {
286 errs() << "error: invalid length " << Lengths[i]
287 << ", offset + length (" << Offsets[i] + Lengths[i]
288 << ") is outside the file.\n";
289 return true;
290 }
291 End = Start.getLocWithOffset(Lengths[i]);
292 } else {
293 End = Sources.getLocForEndOfFile(ID);
294 }
295 unsigned Offset = Sources.getFileOffset(Start);
296 unsigned Length = Sources.getFileOffset(End) - Offset;
297 Ranges.push_back(tooling::Range(Offset, Length));
298 }
299 return false;
300 }
301
outputReplacementXML(StringRef Text)302 static void outputReplacementXML(StringRef Text) {
303 // FIXME: When we sort includes, we need to make sure the stream is correct
304 // utf-8.
305 size_t From = 0;
306 size_t Index;
307 while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) {
308 outs() << Text.substr(From, Index - From);
309 switch (Text[Index]) {
310 case '\n':
311 outs() << " ";
312 break;
313 case '\r':
314 outs() << " ";
315 break;
316 case '<':
317 outs() << "<";
318 break;
319 case '&':
320 outs() << "&";
321 break;
322 default:
323 llvm_unreachable("Unexpected character encountered!");
324 }
325 From = Index + 1;
326 }
327 outs() << Text.substr(From);
328 }
329
outputReplacementsXML(const Replacements & Replaces)330 static void outputReplacementsXML(const Replacements &Replaces) {
331 for (const auto &R : Replaces) {
332 outs() << "<replacement "
333 << "offset='" << R.getOffset() << "' "
334 << "length='" << R.getLength() << "'>";
335 outputReplacementXML(R.getReplacementText());
336 outs() << "</replacement>\n";
337 }
338 }
339
340 static bool
emitReplacementWarnings(const Replacements & Replaces,StringRef AssumedFileName,const std::unique_ptr<llvm::MemoryBuffer> & Code)341 emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName,
342 const std::unique_ptr<llvm::MemoryBuffer> &Code) {
343 if (Replaces.empty())
344 return false;
345
346 unsigned Errors = 0;
347 if (WarnFormat && !NoWarnFormat) {
348 llvm::SourceMgr Mgr;
349 const char *StartBuf = Code->getBufferStart();
350
351 Mgr.AddNewSourceBuffer(
352 MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc());
353 for (const auto &R : Replaces) {
354 SMDiagnostic Diag = Mgr.GetMessage(
355 SMLoc::getFromPointer(StartBuf + R.getOffset()),
356 WarningsAsErrors ? SourceMgr::DiagKind::DK_Error
357 : SourceMgr::DiagKind::DK_Warning,
358 "code should be clang-formatted [-Wclang-format-violations]");
359
360 Diag.print(nullptr, llvm::errs(), (ShowColors && !NoShowColors));
361 if (ErrorLimit && ++Errors >= ErrorLimit)
362 break;
363 }
364 }
365 return WarningsAsErrors;
366 }
367
outputXML(const Replacements & Replaces,const Replacements & FormatChanges,const FormattingAttemptStatus & Status,const cl::opt<unsigned> & Cursor,unsigned CursorPosition)368 static void outputXML(const Replacements &Replaces,
369 const Replacements &FormatChanges,
370 const FormattingAttemptStatus &Status,
371 const cl::opt<unsigned> &Cursor,
372 unsigned CursorPosition) {
373 outs() << "<?xml version='1.0'?>\n<replacements "
374 "xml:space='preserve' incomplete_format='"
375 << (Status.FormatComplete ? "false" : "true") << "'";
376 if (!Status.FormatComplete)
377 outs() << " line='" << Status.Line << "'";
378 outs() << ">\n";
379 if (Cursor.getNumOccurrences() != 0) {
380 outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition)
381 << "</cursor>\n";
382 }
383
384 outputReplacementsXML(Replaces);
385 outs() << "</replacements>\n";
386 }
387
388 class ClangFormatDiagConsumer : public DiagnosticConsumer {
anchor()389 virtual void anchor() {}
390
HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,const Diagnostic & Info)391 void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
392 const Diagnostic &Info) override {
393
394 SmallVector<char, 16> vec;
395 Info.FormatDiagnostic(vec);
396 errs() << "clang-format error:" << vec << "\n";
397 }
398 };
399
400 // Returns true on error.
format(StringRef FileName)401 static bool format(StringRef FileName) {
402 if (!OutputXML && Inplace && FileName == "-") {
403 errs() << "error: cannot use -i when reading from stdin.\n";
404 return false;
405 }
406 // On Windows, overwriting a file with an open file mapping doesn't work,
407 // so read the whole file into memory when formatting in-place.
408 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
409 !OutputXML && Inplace ? MemoryBuffer::getFileAsStream(FileName)
410 : MemoryBuffer::getFileOrSTDIN(FileName);
411 if (std::error_code EC = CodeOrErr.getError()) {
412 errs() << EC.message() << "\n";
413 return true;
414 }
415 std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get());
416 if (Code->getBufferSize() == 0)
417 return false; // Empty files are formatted correctly.
418
419 StringRef BufStr = Code->getBuffer();
420
421 const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr);
422
423 if (InvalidBOM) {
424 errs() << "error: encoding with unsupported byte order mark \""
425 << InvalidBOM << "\" detected";
426 if (FileName != "-")
427 errs() << " in file '" << FileName << "'";
428 errs() << ".\n";
429 return true;
430 }
431
432 std::vector<tooling::Range> Ranges;
433 if (fillRanges(Code.get(), Ranges))
434 return true;
435 StringRef AssumedFileName = (FileName == "-") ? AssumeFileName : FileName;
436 if (AssumedFileName.empty()) {
437 llvm::errs() << "error: empty filenames are not allowed\n";
438 return true;
439 }
440
441 llvm::Expected<FormatStyle> FormatStyle =
442 getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(),
443 nullptr, WNoErrorList.isSet(WNoError::Unknown));
444 if (!FormatStyle) {
445 llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
446 return true;
447 }
448
449 StringRef QualifierAlignmentOrder = QualifierAlignment;
450
451 FormatStyle->QualifierAlignment =
452 StringSwitch<FormatStyle::QualifierAlignmentStyle>(
453 QualifierAlignmentOrder.lower())
454 .Case("right", FormatStyle::QAS_Right)
455 .Case("left", FormatStyle::QAS_Left)
456 .Default(FormatStyle->QualifierAlignment);
457
458 if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) {
459 FormatStyle->QualifierOrder = {"const", "volatile", "type"};
460 } else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) {
461 FormatStyle->QualifierOrder = {"type", "const", "volatile"};
462 } else if (QualifierAlignmentOrder.contains("type")) {
463 FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom;
464 SmallVector<StringRef> Qualifiers;
465 QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1,
466 /*KeepEmpty=*/false);
467 FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()};
468 }
469
470 if (SortIncludes.getNumOccurrences() != 0) {
471 if (SortIncludes)
472 FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive;
473 else
474 FormatStyle->SortIncludes = FormatStyle::SI_Never;
475 }
476 unsigned CursorPosition = Cursor;
477 Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges,
478 AssumedFileName, &CursorPosition);
479
480 // To format JSON insert a variable to trick the code into thinking its
481 // JavaScript.
482 if (FormatStyle->isJson() && !FormatStyle->DisableFormat) {
483 auto Err = Replaces.add(tooling::Replacement(
484 tooling::Replacement(AssumedFileName, 0, 0, "x = ")));
485 if (Err)
486 llvm::errs() << "Bad Json variable insertion\n";
487 }
488
489 auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces);
490 if (!ChangedCode) {
491 llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n";
492 return true;
493 }
494 // Get new affected ranges after sorting `#includes`.
495 Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges);
496 FormattingAttemptStatus Status;
497 Replacements FormatChanges =
498 reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status);
499 Replaces = Replaces.merge(FormatChanges);
500 if (OutputXML || DryRun) {
501 if (DryRun)
502 return emitReplacementWarnings(Replaces, AssumedFileName, Code);
503 else
504 outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition);
505 } else {
506 IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem(
507 new llvm::vfs::InMemoryFileSystem);
508 FileManager Files(FileSystemOptions(), InMemoryFileSystem);
509
510 IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions());
511 ClangFormatDiagConsumer IgnoreDiagnostics;
512 DiagnosticsEngine Diagnostics(
513 IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts,
514 &IgnoreDiagnostics, false);
515 SourceManager Sources(Diagnostics, Files);
516 FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files,
517 InMemoryFileSystem.get());
518 Rewriter Rewrite(Sources, LangOptions());
519 tooling::applyAllReplacements(Replaces, Rewrite);
520 if (Inplace) {
521 if (Rewrite.overwriteChangedFiles())
522 return true;
523 } else {
524 if (Cursor.getNumOccurrences() != 0) {
525 outs() << "{ \"Cursor\": "
526 << FormatChanges.getShiftedCodePosition(CursorPosition)
527 << ", \"IncompleteFormat\": "
528 << (Status.FormatComplete ? "false" : "true");
529 if (!Status.FormatComplete)
530 outs() << ", \"Line\": " << Status.Line;
531 outs() << " }\n";
532 }
533 Rewrite.getEditBuffer(ID).write(outs());
534 }
535 }
536 return false;
537 }
538
539 } // namespace format
540 } // namespace clang
541
PrintVersion(raw_ostream & OS)542 static void PrintVersion(raw_ostream &OS) {
543 OS << clang::getClangToolFullVersion("clang-format") << '\n';
544 }
545
546 // Dump the configuration.
dumpConfig()547 static int dumpConfig() {
548 StringRef FileName;
549 std::unique_ptr<llvm::MemoryBuffer> Code;
550 if (FileNames.empty()) {
551 // We can't read the code to detect the language if there's no
552 // file name, so leave Code empty here.
553 FileName = AssumeFileName;
554 } else {
555 // Read in the code in case the filename alone isn't enough to
556 // detect the language.
557 ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr =
558 MemoryBuffer::getFileOrSTDIN(FileNames[0]);
559 if (std::error_code EC = CodeOrErr.getError()) {
560 llvm::errs() << EC.message() << "\n";
561 return 1;
562 }
563 FileName = (FileNames[0] == "-") ? AssumeFileName : FileNames[0];
564 Code = std::move(CodeOrErr.get());
565 }
566 llvm::Expected<clang::format::FormatStyle> FormatStyle =
567 clang::format::getStyle(Style, FileName, FallbackStyle,
568 Code ? Code->getBuffer() : "");
569 if (!FormatStyle) {
570 llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n";
571 return 1;
572 }
573 std::string Config = clang::format::configurationAsText(*FormatStyle);
574 outs() << Config << "\n";
575 return 0;
576 }
577
main(int argc,const char ** argv)578 int main(int argc, const char **argv) {
579 llvm::InitLLVM X(argc, argv);
580
581 cl::HideUnrelatedOptions(ClangFormatCategory);
582
583 cl::SetVersionPrinter(PrintVersion);
584 cl::ParseCommandLineOptions(
585 argc, argv,
586 "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# "
587 "code.\n\n"
588 "If no arguments are specified, it formats the code from standard input\n"
589 "and writes the result to the standard output.\n"
590 "If <file>s are given, it reformats the files. If -i is specified\n"
591 "together with <file>s, the files are edited in-place. Otherwise, the\n"
592 "result is written to the standard output.\n");
593
594 if (Help) {
595 cl::PrintHelpMessage();
596 return 0;
597 }
598
599 if (DumpConfig)
600 return dumpConfig();
601
602 if (!Files.empty()) {
603 std::ifstream ExternalFileOfFiles{std::string(Files)};
604 std::string Line;
605 unsigned LineNo = 1;
606 while (std::getline(ExternalFileOfFiles, Line)) {
607 FileNames.push_back(Line);
608 LineNo++;
609 }
610 errs() << "Clang-formating " << LineNo << " files\n";
611 }
612
613 bool Error = false;
614 if (FileNames.empty()) {
615 Error = clang::format::format("-");
616 return Error ? 1 : 0;
617 }
618 if (FileNames.size() != 1 &&
619 (!Offsets.empty() || !Lengths.empty() || !LineRanges.empty())) {
620 errs() << "error: -offset, -length and -lines can only be used for "
621 "single file.\n";
622 return 1;
623 }
624
625 unsigned FileNo = 1;
626 for (const auto &FileName : FileNames) {
627 if (Verbose) {
628 errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] "
629 << FileName << "\n";
630 }
631 Error |= clang::format::format(FileName);
632 }
633 return Error ? 1 : 0;
634 }
635