/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/tools/clang-format/ClangFormat.cpp
Line | Count | Source (jump to first uncovered line) |
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/Support/CommandLine.h" |
23 | | #include "llvm/Support/FileSystem.h" |
24 | | #include "llvm/Support/InitLLVM.h" |
25 | | #include "llvm/Support/Process.h" |
26 | | |
27 | | using namespace llvm; |
28 | | using clang::tooling::Replacements; |
29 | | |
30 | | static cl::opt<bool> Help("h", cl::desc("Alias for -help"), cl::Hidden); |
31 | | |
32 | | // Mark all our options with this category, everything else (except for -version |
33 | | // and -help) will be hidden. |
34 | | static cl::OptionCategory ClangFormatCategory("Clang-format options"); |
35 | | |
36 | | static cl::list<unsigned> |
37 | | Offsets("offset", |
38 | | cl::desc("Format a range starting at this byte offset.\n" |
39 | | "Multiple ranges can be formatted by specifying\n" |
40 | | "several -offset and -length pairs.\n" |
41 | | "Can only be used with one input file."), |
42 | | cl::cat(ClangFormatCategory)); |
43 | | static cl::list<unsigned> |
44 | | Lengths("length", |
45 | | cl::desc("Format a range of this length (in bytes).\n" |
46 | | "Multiple ranges can be formatted by specifying\n" |
47 | | "several -offset and -length pairs.\n" |
48 | | "When only a single -offset is specified without\n" |
49 | | "-length, clang-format will format up to the end\n" |
50 | | "of the file.\n" |
51 | | "Can only be used with one input file."), |
52 | | cl::cat(ClangFormatCategory)); |
53 | | static cl::list<std::string> |
54 | | LineRanges("lines", |
55 | | cl::desc("<start line>:<end line> - format a range of\n" |
56 | | "lines (both 1-based).\n" |
57 | | "Multiple ranges can be formatted by specifying\n" |
58 | | "several -lines arguments.\n" |
59 | | "Can't be used with -offset and -length.\n" |
60 | | "Can only be used with one input file."), |
61 | | cl::cat(ClangFormatCategory)); |
62 | | static cl::opt<std::string> |
63 | | Style("style", cl::desc(clang::format::StyleOptionHelpDescription), |
64 | | cl::init(clang::format::DefaultFormatStyle), |
65 | | cl::cat(ClangFormatCategory)); |
66 | | static cl::opt<std::string> |
67 | | FallbackStyle("fallback-style", |
68 | | cl::desc("The name of the predefined style used as a\n" |
69 | | "fallback in case clang-format is invoked with\n" |
70 | | "-style=file, but can not find the .clang-format\n" |
71 | | "file to use.\n" |
72 | | "Use -fallback-style=none to skip formatting."), |
73 | | cl::init(clang::format::DefaultFallbackStyle), |
74 | | cl::cat(ClangFormatCategory)); |
75 | | |
76 | | static cl::opt<std::string> AssumeFileName( |
77 | | "assume-filename", |
78 | | cl::desc("Override filename used to determine the language.\n" |
79 | | "When reading from stdin, clang-format assumes this\n" |
80 | | "filename to determine the language."), |
81 | | cl::init("<stdin>"), cl::cat(ClangFormatCategory)); |
82 | | |
83 | | static cl::opt<bool> Inplace("i", |
84 | | cl::desc("Inplace edit <file>s, if specified."), |
85 | | cl::cat(ClangFormatCategory)); |
86 | | |
87 | | static cl::opt<bool> OutputXML("output-replacements-xml", |
88 | | cl::desc("Output replacements as XML."), |
89 | | cl::cat(ClangFormatCategory)); |
90 | | static cl::opt<bool> |
91 | | DumpConfig("dump-config", |
92 | | cl::desc("Dump configuration options to stdout and exit.\n" |
93 | | "Can be used with -style option."), |
94 | | cl::cat(ClangFormatCategory)); |
95 | | static cl::opt<unsigned> |
96 | | Cursor("cursor", |
97 | | cl::desc("The position of the cursor when invoking\n" |
98 | | "clang-format from an editor integration"), |
99 | | cl::init(0), cl::cat(ClangFormatCategory)); |
100 | | |
101 | | static cl::opt<bool> SortIncludes( |
102 | | "sort-includes", |
103 | | cl::desc("If set, overrides the include sorting behavior determined by the " |
104 | | "SortIncludes style flag"), |
105 | | cl::cat(ClangFormatCategory)); |
106 | | |
107 | | static cl::opt<bool> |
108 | | Verbose("verbose", cl::desc("If set, shows the list of processed files"), |
109 | | cl::cat(ClangFormatCategory)); |
110 | | |
111 | | // Use --dry-run to match other LLVM tools when you mean do it but don't |
112 | | // actually do it |
113 | | static cl::opt<bool> |
114 | | DryRun("dry-run", |
115 | | cl::desc("If set, do not actually make the formatting changes"), |
116 | | cl::cat(ClangFormatCategory)); |
117 | | |
118 | | // Use -n as a common command as an alias for --dry-run. (git and make use -n) |
119 | | static cl::alias DryRunShort("n", cl::desc("Alias for --dry-run"), |
120 | | cl::cat(ClangFormatCategory), cl::aliasopt(DryRun), |
121 | | cl::NotHidden); |
122 | | |
123 | | // Emulate being able to turn on/off the warning. |
124 | | static cl::opt<bool> |
125 | | WarnFormat("Wclang-format-violations", |
126 | | cl::desc("Warnings about individual formatting changes needed. " |
127 | | "Used only with --dry-run or -n"), |
128 | | cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden); |
129 | | |
130 | | static cl::opt<bool> |
131 | | NoWarnFormat("Wno-clang-format-violations", |
132 | | cl::desc("Do not warn about individual formatting changes " |
133 | | "needed. Used only with --dry-run or -n"), |
134 | | cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden); |
135 | | |
136 | | static cl::opt<unsigned> ErrorLimit( |
137 | | "ferror-limit", |
138 | | cl::desc("Set the maximum number of clang-format errors to emit before " |
139 | | "stopping (0 = no limit). Used only with --dry-run or -n"), |
140 | | cl::init(0), cl::cat(ClangFormatCategory)); |
141 | | |
142 | | static cl::opt<bool> |
143 | | WarningsAsErrors("Werror", |
144 | | cl::desc("If set, changes formatting warnings to errors"), |
145 | | cl::cat(ClangFormatCategory)); |
146 | | |
147 | | namespace { |
148 | | enum class WNoError { Unknown }; |
149 | | } |
150 | | |
151 | | static cl::bits<WNoError> WNoErrorList( |
152 | | "Wno-error", |
153 | | cl::desc("If set don't error out on the specified warning type."), |
154 | | cl::values( |
155 | | clEnumValN(WNoError::Unknown, "unknown", |
156 | | "If set, unknown format options are only warned about.\n" |
157 | | "This can be used to enable formatting, even if the\n" |
158 | | "configuration contains unknown (newer) options.\n" |
159 | | "Use with caution, as this might lead to dramatically\n" |
160 | | "differing format depending on an option being\n" |
161 | | "supported or not.")), |
162 | | cl::cat(ClangFormatCategory)); |
163 | | |
164 | | static cl::opt<bool> |
165 | | ShowColors("fcolor-diagnostics", |
166 | | cl::desc("If set, and on a color-capable terminal controls " |
167 | | "whether or not to print diagnostics in color"), |
168 | | cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden); |
169 | | |
170 | | static cl::opt<bool> |
171 | | NoShowColors("fno-color-diagnostics", |
172 | | cl::desc("If set, and on a color-capable terminal controls " |
173 | | "whether or not to print diagnostics in color"), |
174 | | cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden); |
175 | | |
176 | | static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"), |
177 | | cl::cat(ClangFormatCategory)); |
178 | | |
179 | | namespace clang { |
180 | | namespace format { |
181 | | |
182 | | static FileID createInMemoryFile(StringRef FileName, MemoryBufferRef Source, |
183 | | SourceManager &Sources, FileManager &Files, |
184 | 73 | llvm::vfs::InMemoryFileSystem *MemFS) { |
185 | 73 | MemFS->addFileNoOwn(FileName, 0, Source); |
186 | 73 | auto File = Files.getOptionalFileRef(FileName); |
187 | 73 | assert(File && "File not added to MemFS?"); |
188 | 73 | return Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User); |
189 | 73 | } |
190 | | |
191 | | // Parses <start line>:<end line> input to a pair of line numbers. |
192 | | // Returns true on error. |
193 | | static bool parseLineRange(StringRef Input, unsigned &FromLine, |
194 | 4 | unsigned &ToLine) { |
195 | 4 | std::pair<StringRef, StringRef> LineRange = Input.split(':'); |
196 | 4 | return LineRange.first.getAsInteger(0, FromLine) || |
197 | 4 | LineRange.second.getAsInteger(0, ToLine); |
198 | 4 | } |
199 | | |
200 | | static bool fillRanges(MemoryBuffer *Code, |
201 | 40 | std::vector<tooling::Range> &Ranges) { |
202 | 40 | IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
203 | 40 | new llvm::vfs::InMemoryFileSystem); |
204 | 40 | FileManager Files(FileSystemOptions(), InMemoryFileSystem); |
205 | 40 | DiagnosticsEngine Diagnostics( |
206 | 40 | IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), |
207 | 40 | new DiagnosticOptions); |
208 | 40 | SourceManager Sources(Diagnostics, Files); |
209 | 40 | FileID ID = createInMemoryFile("<irrelevant>", *Code, Sources, Files, |
210 | 40 | InMemoryFileSystem.get()); |
211 | 40 | if (!LineRanges.empty()) { |
212 | 3 | if (!Offsets.empty() || !Lengths.empty()) { |
213 | 0 | errs() << "error: cannot use -lines with -offset/-length\n"; |
214 | 0 | return true; |
215 | 0 | } |
216 | | |
217 | 7 | for (unsigned i = 0, e = LineRanges.size(); 3 i < e; ++i4 ) { |
218 | 4 | unsigned FromLine, ToLine; |
219 | 4 | if (parseLineRange(LineRanges[i], FromLine, ToLine)) { |
220 | 0 | errs() << "error: invalid <start line>:<end line> pair\n"; |
221 | 0 | return true; |
222 | 0 | } |
223 | 4 | if (FromLine > ToLine) { |
224 | 0 | errs() << "error: start line should be less than end line\n"; |
225 | 0 | return true; |
226 | 0 | } |
227 | 4 | SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1); |
228 | 4 | SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX); |
229 | 4 | if (Start.isInvalid() || End.isInvalid()) |
230 | 0 | return true; |
231 | 4 | unsigned Offset = Sources.getFileOffset(Start); |
232 | 4 | unsigned Length = Sources.getFileOffset(End) - Offset; |
233 | 4 | Ranges.push_back(tooling::Range(Offset, Length)); |
234 | 4 | } |
235 | 3 | return false; |
236 | 37 | } |
237 | | |
238 | 37 | if (Offsets.empty()) |
239 | 34 | Offsets.push_back(0); |
240 | 37 | if (Offsets.size() != Lengths.size() && |
241 | 36 | !(Offsets.size() == 1 && Lengths.empty())) { |
242 | 0 | errs() << "error: number of -offset and -length arguments must match.\n"; |
243 | 0 | return true; |
244 | 0 | } |
245 | 75 | for (unsigned i = 0, e = Offsets.size(); 37 i != e; ++i38 ) { |
246 | 38 | if (Offsets[i] >= Code->getBufferSize()) { |
247 | 0 | errs() << "error: offset " << Offsets[i] << " is outside the file\n"; |
248 | 0 | return true; |
249 | 0 | } |
250 | 38 | SourceLocation Start = |
251 | 38 | Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]); |
252 | 38 | SourceLocation End; |
253 | 38 | if (i < Lengths.size()) { |
254 | 2 | if (Offsets[i] + Lengths[i] > Code->getBufferSize()) { |
255 | 0 | errs() << "error: invalid length " << Lengths[i] |
256 | 0 | << ", offset + length (" << Offsets[i] + Lengths[i] |
257 | 0 | << ") is outside the file.\n"; |
258 | 0 | return true; |
259 | 0 | } |
260 | 2 | End = Start.getLocWithOffset(Lengths[i]); |
261 | 36 | } else { |
262 | 36 | End = Sources.getLocForEndOfFile(ID); |
263 | 36 | } |
264 | 38 | unsigned Offset = Sources.getFileOffset(Start); |
265 | 38 | unsigned Length = Sources.getFileOffset(End) - Offset; |
266 | 38 | Ranges.push_back(tooling::Range(Offset, Length)); |
267 | 38 | } |
268 | 37 | return false; |
269 | 37 | } |
270 | | |
271 | 3 | static void outputReplacementXML(StringRef Text) { |
272 | | // FIXME: When we sort includes, we need to make sure the stream is correct |
273 | | // utf-8. |
274 | 3 | size_t From = 0; |
275 | 3 | size_t Index; |
276 | 7 | while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) { |
277 | 4 | outs() << Text.substr(From, Index - From); |
278 | 4 | switch (Text[Index]) { |
279 | 2 | case '\n': |
280 | 2 | outs() << " "; |
281 | 2 | break; |
282 | 0 | case '\r': |
283 | 0 | outs() << " "; |
284 | 0 | break; |
285 | 2 | case '<': |
286 | 2 | outs() << "<"; |
287 | 2 | break; |
288 | 0 | case '&': |
289 | 0 | outs() << "&"; |
290 | 0 | break; |
291 | 0 | default: |
292 | 0 | llvm_unreachable("Unexpected character encountered!"); |
293 | 4 | } |
294 | 4 | From = Index + 1; |
295 | 4 | } |
296 | 3 | outs() << Text.substr(From); |
297 | 3 | } |
298 | | |
299 | 1 | static void outputReplacementsXML(const Replacements &Replaces) { |
300 | 3 | for (const auto &R : Replaces) { |
301 | 3 | outs() << "<replacement " |
302 | 3 | << "offset='" << R.getOffset() << "' " |
303 | 3 | << "length='" << R.getLength() << "'>"; |
304 | 3 | outputReplacementXML(R.getReplacementText()); |
305 | 3 | outs() << "</replacement>\n"; |
306 | 3 | } |
307 | 1 | } |
308 | | |
309 | | static bool |
310 | | emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName, |
311 | 2 | const std::unique_ptr<llvm::MemoryBuffer> &Code) { |
312 | 2 | if (Replaces.empty()) |
313 | 0 | return false; |
314 | | |
315 | 2 | unsigned Errors = 0; |
316 | 2 | if (WarnFormat && !NoWarnFormat) { |
317 | 2 | llvm::SourceMgr Mgr; |
318 | 2 | const char *StartBuf = Code->getBufferStart(); |
319 | | |
320 | 2 | Mgr.AddNewSourceBuffer( |
321 | 2 | MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc()); |
322 | 2 | for (const auto &R : Replaces) { |
323 | 2 | SMDiagnostic Diag = Mgr.GetMessage( |
324 | 2 | SMLoc::getFromPointer(StartBuf + R.getOffset()), |
325 | 0 | WarningsAsErrors ? SourceMgr::DiagKind::DK_Error |
326 | 2 | : SourceMgr::DiagKind::DK_Warning, |
327 | 2 | "code should be clang-formatted [-Wclang-format-violations]"); |
328 | | |
329 | 2 | Diag.print(nullptr, llvm::errs(), (ShowColors && !NoShowColors)); |
330 | 2 | if (ErrorLimit && ++Errors >= ErrorLimit0 ) |
331 | 0 | break; |
332 | 2 | } |
333 | 2 | } |
334 | 2 | return WarningsAsErrors; |
335 | 2 | } |
336 | | |
337 | | static void outputXML(const Replacements &Replaces, |
338 | | const Replacements &FormatChanges, |
339 | | const FormattingAttemptStatus &Status, |
340 | | const cl::opt<unsigned> &Cursor, |
341 | 1 | unsigned CursorPosition) { |
342 | 1 | outs() << "<?xml version='1.0'?>\n<replacements " |
343 | 1 | "xml:space='preserve' incomplete_format='" |
344 | 1 | << (Status.FormatComplete ? "false" : "true"0 ) << "'"; |
345 | 1 | if (!Status.FormatComplete) |
346 | 0 | outs() << " line='" << Status.Line << "'"; |
347 | 1 | outs() << ">\n"; |
348 | 1 | if (Cursor.getNumOccurrences() != 0) |
349 | 0 | outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition) |
350 | 0 | << "</cursor>\n"; |
351 | | |
352 | 1 | outputReplacementsXML(Replaces); |
353 | 1 | outs() << "</replacements>\n"; |
354 | 1 | } |
355 | | |
356 | | // Returns true on error. |
357 | 40 | static bool format(StringRef FileName) { |
358 | 40 | if (!OutputXML && Inplace39 && FileName == "-"5 ) { |
359 | 0 | errs() << "error: cannot use -i when reading from stdin.\n"; |
360 | 0 | return false; |
361 | 0 | } |
362 | | // On Windows, overwriting a file with an open file mapping doesn't work, |
363 | | // so read the whole file into memory when formatting in-place. |
364 | 40 | ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = |
365 | 40 | !OutputXML && Inplace39 ? MemoryBuffer::getFileAsStream(FileName)5 |
366 | 35 | : MemoryBuffer::getFileOrSTDIN(FileName); |
367 | 40 | if (std::error_code EC = CodeOrErr.getError()) { |
368 | 0 | errs() << EC.message() << "\n"; |
369 | 0 | return true; |
370 | 0 | } |
371 | 40 | std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get()); |
372 | 40 | if (Code->getBufferSize() == 0) |
373 | 0 | return false; // Empty files are formatted correctly. |
374 | | |
375 | 40 | StringRef BufStr = Code->getBuffer(); |
376 | | |
377 | 40 | const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr); |
378 | | |
379 | 40 | if (InvalidBOM) { |
380 | 0 | errs() << "error: encoding with unsupported byte order mark \"" |
381 | 0 | << InvalidBOM << "\" detected"; |
382 | 0 | if (FileName != "-") |
383 | 0 | errs() << " in file '" << FileName << "'"; |
384 | 0 | errs() << ".\n"; |
385 | 0 | return true; |
386 | 0 | } |
387 | | |
388 | 40 | std::vector<tooling::Range> Ranges; |
389 | 40 | if (fillRanges(Code.get(), Ranges)) |
390 | 0 | return true; |
391 | 40 | StringRef AssumedFileName = (FileName == "-") ? AssumeFileName14 : FileName26 ; |
392 | 40 | if (AssumedFileName.empty()) { |
393 | 0 | llvm::errs() << "error: empty filenames are not allowed\n"; |
394 | 0 | return true; |
395 | 0 | } |
396 | | |
397 | 40 | llvm::Expected<FormatStyle> FormatStyle = |
398 | 40 | getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(), |
399 | 40 | nullptr, WNoErrorList.isSet(WNoError::Unknown)); |
400 | 40 | if (!FormatStyle) { |
401 | 4 | llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n"; |
402 | 4 | return true; |
403 | 4 | } |
404 | | |
405 | 36 | if (SortIncludes.getNumOccurrences() != 0) |
406 | 3 | FormatStyle->SortIncludes = SortIncludes; |
407 | 36 | unsigned CursorPosition = Cursor; |
408 | 36 | Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges, |
409 | 36 | AssumedFileName, &CursorPosition); |
410 | 36 | auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces); |
411 | 36 | if (!ChangedCode) { |
412 | 0 | llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n"; |
413 | 0 | return true; |
414 | 0 | } |
415 | | // Get new affected ranges after sorting `#includes`. |
416 | 36 | Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges); |
417 | 36 | FormattingAttemptStatus Status; |
418 | 36 | Replacements FormatChanges = |
419 | 36 | reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status); |
420 | 36 | Replaces = Replaces.merge(FormatChanges); |
421 | 36 | if (OutputXML || DryRun35 ) { |
422 | 3 | if (DryRun) { |
423 | 2 | return emitReplacementWarnings(Replaces, AssumedFileName, Code); |
424 | 1 | } else { |
425 | 1 | outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition); |
426 | 1 | } |
427 | 33 | } else { |
428 | 33 | IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
429 | 33 | new llvm::vfs::InMemoryFileSystem); |
430 | 33 | FileManager Files(FileSystemOptions(), InMemoryFileSystem); |
431 | 33 | DiagnosticsEngine Diagnostics( |
432 | 33 | IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), |
433 | 33 | new DiagnosticOptions); |
434 | 33 | SourceManager Sources(Diagnostics, Files); |
435 | 33 | FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files, |
436 | 33 | InMemoryFileSystem.get()); |
437 | 33 | Rewriter Rewrite(Sources, LangOptions()); |
438 | 33 | tooling::applyAllReplacements(Replaces, Rewrite); |
439 | 33 | if (Inplace) { |
440 | 3 | if (Rewrite.overwriteChangedFiles()) |
441 | 0 | return true; |
442 | 30 | } else { |
443 | 30 | if (Cursor.getNumOccurrences() != 0) { |
444 | 2 | outs() << "{ \"Cursor\": " |
445 | 2 | << FormatChanges.getShiftedCodePosition(CursorPosition) |
446 | 2 | << ", \"IncompleteFormat\": " |
447 | 1 | << (Status.FormatComplete ? "false" : "true"); |
448 | 2 | if (!Status.FormatComplete) |
449 | 1 | outs() << ", \"Line\": " << Status.Line; |
450 | 2 | outs() << " }\n"; |
451 | 2 | } |
452 | 30 | Rewrite.getEditBuffer(ID).write(outs()); |
453 | 30 | } |
454 | 33 | } |
455 | 34 | return false; |
456 | 36 | } |
457 | | |
458 | | } // namespace format |
459 | | } // namespace clang |
460 | | |
461 | 0 | static void PrintVersion(raw_ostream &OS) { |
462 | 0 | OS << clang::getClangToolFullVersion("clang-format") << '\n'; |
463 | 0 | } |
464 | | |
465 | | // Dump the configuration. |
466 | 5 | static int dumpConfig() { |
467 | 5 | StringRef FileName; |
468 | 5 | std::unique_ptr<llvm::MemoryBuffer> Code; |
469 | 5 | if (FileNames.empty()) { |
470 | | // We can't read the code to detect the language if there's no |
471 | | // file name, so leave Code empty here. |
472 | 0 | FileName = AssumeFileName; |
473 | 5 | } else { |
474 | | // Read in the code in case the filename alone isn't enough to |
475 | | // detect the language. |
476 | 5 | ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = |
477 | 5 | MemoryBuffer::getFileOrSTDIN(FileNames[0]); |
478 | 5 | if (std::error_code EC = CodeOrErr.getError()) { |
479 | 0 | llvm::errs() << EC.message() << "\n"; |
480 | 0 | return 1; |
481 | 0 | } |
482 | 5 | FileName = (FileNames[0] == "-") ? AssumeFileName0 : FileNames[0]; |
483 | 5 | Code = std::move(CodeOrErr.get()); |
484 | 5 | } |
485 | 5 | llvm::Expected<clang::format::FormatStyle> FormatStyle = |
486 | 5 | clang::format::getStyle(Style, FileName, FallbackStyle, |
487 | 5 | Code ? Code->getBuffer() : ""0 ); |
488 | 5 | if (!FormatStyle) { |
489 | 0 | llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n"; |
490 | 0 | return 1; |
491 | 0 | } |
492 | 5 | std::string Config = clang::format::configurationAsText(*FormatStyle); |
493 | 5 | outs() << Config << "\n"; |
494 | 5 | return 0; |
495 | 5 | } |
496 | | |
497 | 45 | int main(int argc, const char **argv) { |
498 | 45 | llvm::InitLLVM X(argc, argv); |
499 | | |
500 | 45 | cl::HideUnrelatedOptions(ClangFormatCategory); |
501 | | |
502 | 45 | cl::SetVersionPrinter(PrintVersion); |
503 | 45 | cl::ParseCommandLineOptions( |
504 | 45 | argc, argv, |
505 | 45 | "A tool to format C/C++/Java/JavaScript/Objective-C/Protobuf/C# code.\n\n" |
506 | 45 | "If no arguments are specified, it formats the code from standard input\n" |
507 | 45 | "and writes the result to the standard output.\n" |
508 | 45 | "If <file>s are given, it reformats the files. If -i is specified\n" |
509 | 45 | "together with <file>s, the files are edited in-place. Otherwise, the\n" |
510 | 45 | "result is written to the standard output.\n"); |
511 | | |
512 | 45 | if (Help) { |
513 | 0 | cl::PrintHelpMessage(); |
514 | 0 | return 0; |
515 | 0 | } |
516 | | |
517 | 45 | if (DumpConfig) { |
518 | 5 | return dumpConfig(); |
519 | 5 | } |
520 | | |
521 | 40 | bool Error = false; |
522 | 40 | if (FileNames.empty()) { |
523 | 14 | Error = clang::format::format("-"); |
524 | 13 | return Error ? 11 : 0; |
525 | 14 | } |
526 | 26 | if (FileNames.size() != 1 && |
527 | 4 | (!Offsets.empty() || !Lengths.empty()3 || !LineRanges.empty()3 )) { |
528 | 2 | errs() << "error: -offset, -length and -lines can only be used for " |
529 | 2 | "single file.\n"; |
530 | 2 | return 1; |
531 | 2 | } |
532 | 26 | for (const auto &FileName : FileNames)24 { |
533 | 26 | if (Verbose) |
534 | 2 | errs() << "Formatting " << FileName << "\n"; |
535 | 26 | Error |= clang::format::format(FileName); |
536 | 26 | } |
537 | 21 | return Error ? 13 : 0; |
538 | 24 | } |