/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/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.\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("Override filename used to determine the language.\n" |
81 | | "When reading from stdin, clang-format assumes this\n" |
82 | | "filename to determine the language."), |
83 | | cl::init("<stdin>"), cl::cat(ClangFormatCategory)); |
84 | | |
85 | | static cl::opt<bool> Inplace("i", |
86 | | cl::desc("Inplace edit <file>s, if specified."), |
87 | | cl::cat(ClangFormatCategory)); |
88 | | |
89 | | static cl::opt<bool> OutputXML("output-replacements-xml", |
90 | | cl::desc("Output replacements as XML."), |
91 | | cl::cat(ClangFormatCategory)); |
92 | | static cl::opt<bool> |
93 | | DumpConfig("dump-config", |
94 | | cl::desc("Dump configuration options to stdout and exit.\n" |
95 | | "Can be used with -style option."), |
96 | | cl::cat(ClangFormatCategory)); |
97 | | static cl::opt<unsigned> |
98 | | Cursor("cursor", |
99 | | cl::desc("The position of the cursor when invoking\n" |
100 | | "clang-format from an editor integration"), |
101 | | cl::init(0), cl::cat(ClangFormatCategory)); |
102 | | |
103 | | static cl::opt<bool> |
104 | | SortIncludes("sort-includes", |
105 | | cl::desc("If set, overrides the include sorting behavior\n" |
106 | | "determined by the SortIncludes style flag"), |
107 | | cl::cat(ClangFormatCategory)); |
108 | | |
109 | | static cl::opt<std::string> QualifierAlignment( |
110 | | "qualifier-alignment", |
111 | | cl::desc("If set, overrides the qualifier alignment style\n" |
112 | | "determined by the QualifierAlignment style flag"), |
113 | | cl::init(""), cl::cat(ClangFormatCategory)); |
114 | | |
115 | | static cl::opt<std::string> |
116 | | Files("files", cl::desc("Provide a list of files to run clang-format"), |
117 | | cl::init(""), cl::cat(ClangFormatCategory)); |
118 | | |
119 | | static cl::opt<bool> |
120 | | Verbose("verbose", cl::desc("If set, shows the list of processed files"), |
121 | | cl::cat(ClangFormatCategory)); |
122 | | |
123 | | // Use --dry-run to match other LLVM tools when you mean do it but don't |
124 | | // actually do it |
125 | | static cl::opt<bool> |
126 | | DryRun("dry-run", |
127 | | cl::desc("If set, do not actually make the formatting changes"), |
128 | | cl::cat(ClangFormatCategory)); |
129 | | |
130 | | // Use -n as a common command as an alias for --dry-run. (git and make use -n) |
131 | | static cl::alias DryRunShort("n", cl::desc("Alias for --dry-run"), |
132 | | cl::cat(ClangFormatCategory), cl::aliasopt(DryRun), |
133 | | cl::NotHidden); |
134 | | |
135 | | // Emulate being able to turn on/off the warning. |
136 | | static cl::opt<bool> |
137 | | WarnFormat("Wclang-format-violations", |
138 | | cl::desc("Warnings about individual formatting changes needed. " |
139 | | "Used only with --dry-run or -n"), |
140 | | cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden); |
141 | | |
142 | | static cl::opt<bool> |
143 | | NoWarnFormat("Wno-clang-format-violations", |
144 | | cl::desc("Do not warn about individual formatting changes " |
145 | | "needed. Used only with --dry-run or -n"), |
146 | | cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden); |
147 | | |
148 | | static cl::opt<unsigned> ErrorLimit( |
149 | | "ferror-limit", |
150 | | cl::desc("Set the maximum number of clang-format errors to emit\n" |
151 | | "before stopping (0 = no limit).\n" |
152 | | "Used only with --dry-run or -n"), |
153 | | cl::init(0), cl::cat(ClangFormatCategory)); |
154 | | |
155 | | static cl::opt<bool> |
156 | | WarningsAsErrors("Werror", |
157 | | cl::desc("If set, changes formatting warnings to errors"), |
158 | | cl::cat(ClangFormatCategory)); |
159 | | |
160 | | namespace { |
161 | | enum class WNoError { Unknown }; |
162 | | } |
163 | | |
164 | | static cl::bits<WNoError> WNoErrorList( |
165 | | "Wno-error", |
166 | | cl::desc("If set don't error out on the specified warning type."), |
167 | | cl::values( |
168 | | clEnumValN(WNoError::Unknown, "unknown", |
169 | | "If set, unknown format options are only warned about.\n" |
170 | | "This can be used to enable formatting, even if the\n" |
171 | | "configuration contains unknown (newer) options.\n" |
172 | | "Use with caution, as this might lead to dramatically\n" |
173 | | "differing format depending on an option being\n" |
174 | | "supported or not.")), |
175 | | cl::cat(ClangFormatCategory)); |
176 | | |
177 | | static cl::opt<bool> |
178 | | ShowColors("fcolor-diagnostics", |
179 | | cl::desc("If set, and on a color-capable terminal controls " |
180 | | "whether or not to print diagnostics in color"), |
181 | | cl::init(true), cl::cat(ClangFormatCategory), cl::Hidden); |
182 | | |
183 | | static cl::opt<bool> |
184 | | NoShowColors("fno-color-diagnostics", |
185 | | cl::desc("If set, and on a color-capable terminal controls " |
186 | | "whether or not to print diagnostics in color"), |
187 | | cl::init(false), cl::cat(ClangFormatCategory), cl::Hidden); |
188 | | |
189 | | static cl::list<std::string> FileNames(cl::Positional, cl::desc("[<file> ...]"), |
190 | | cl::cat(ClangFormatCategory)); |
191 | | |
192 | | namespace clang { |
193 | | namespace format { |
194 | | |
195 | | static FileID createInMemoryFile(StringRef FileName, MemoryBufferRef Source, |
196 | | SourceManager &Sources, FileManager &Files, |
197 | 81 | llvm::vfs::InMemoryFileSystem *MemFS) { |
198 | 81 | MemFS->addFileNoOwn(FileName, 0, Source); |
199 | 81 | auto File = Files.getOptionalFileRef(FileName); |
200 | 81 | assert(File && "File not added to MemFS?"); |
201 | 0 | return Sources.createFileID(*File, SourceLocation(), SrcMgr::C_User); |
202 | 81 | } |
203 | | |
204 | | // Parses <start line>:<end line> input to a pair of line numbers. |
205 | | // Returns true on error. |
206 | | static bool parseLineRange(StringRef Input, unsigned &FromLine, |
207 | 6 | unsigned &ToLine) { |
208 | 6 | std::pair<StringRef, StringRef> LineRange = Input.split(':'); |
209 | 6 | return LineRange.first.getAsInteger(0, FromLine) || |
210 | 6 | LineRange.second.getAsInteger(0, ToLine); |
211 | 6 | } |
212 | | |
213 | | static bool fillRanges(MemoryBuffer *Code, |
214 | 44 | std::vector<tooling::Range> &Ranges) { |
215 | 44 | IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
216 | 44 | new llvm::vfs::InMemoryFileSystem); |
217 | 44 | FileManager Files(FileSystemOptions(), InMemoryFileSystem); |
218 | 44 | DiagnosticsEngine Diagnostics( |
219 | 44 | IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), |
220 | 44 | new DiagnosticOptions); |
221 | 44 | SourceManager Sources(Diagnostics, Files); |
222 | 44 | FileID ID = createInMemoryFile("<irrelevant>", *Code, Sources, Files, |
223 | 44 | InMemoryFileSystem.get()); |
224 | 44 | if (!LineRanges.empty()) { |
225 | 5 | if (!Offsets.empty() || !Lengths.empty()) { |
226 | 0 | errs() << "error: cannot use -lines with -offset/-length\n"; |
227 | 0 | return true; |
228 | 0 | } |
229 | | |
230 | 11 | for (unsigned i = 0, e = LineRanges.size(); 5 i < e; ++i6 ) { |
231 | 6 | unsigned FromLine, ToLine; |
232 | 6 | if (parseLineRange(LineRanges[i], FromLine, ToLine)) { |
233 | 0 | errs() << "error: invalid <start line>:<end line> pair\n"; |
234 | 0 | return true; |
235 | 0 | } |
236 | 6 | if (FromLine > ToLine) { |
237 | 0 | errs() << "error: start line should be less than end line\n"; |
238 | 0 | return true; |
239 | 0 | } |
240 | 6 | SourceLocation Start = Sources.translateLineCol(ID, FromLine, 1); |
241 | 6 | SourceLocation End = Sources.translateLineCol(ID, ToLine, UINT_MAX); |
242 | 6 | if (Start.isInvalid() || End.isInvalid()) |
243 | 0 | return true; |
244 | 6 | unsigned Offset = Sources.getFileOffset(Start); |
245 | 6 | unsigned Length = Sources.getFileOffset(End) - Offset; |
246 | 6 | Ranges.push_back(tooling::Range(Offset, Length)); |
247 | 6 | } |
248 | 5 | return false; |
249 | 5 | } |
250 | | |
251 | 39 | if (Offsets.empty()) |
252 | 36 | Offsets.push_back(0); |
253 | 39 | if (Offsets.size() != Lengths.size() && |
254 | 39 | !(38 Offsets.size() == 138 && Lengths.empty()38 )) { |
255 | 0 | errs() << "error: number of -offset and -length arguments must match.\n"; |
256 | 0 | return true; |
257 | 0 | } |
258 | 79 | for (unsigned i = 0, e = Offsets.size(); 39 i != e; ++i40 ) { |
259 | 40 | if (Offsets[i] >= Code->getBufferSize()) { |
260 | 0 | errs() << "error: offset " << Offsets[i] << " is outside the file\n"; |
261 | 0 | return true; |
262 | 0 | } |
263 | 40 | SourceLocation Start = |
264 | 40 | Sources.getLocForStartOfFile(ID).getLocWithOffset(Offsets[i]); |
265 | 40 | SourceLocation End; |
266 | 40 | if (i < Lengths.size()) { |
267 | 2 | if (Offsets[i] + Lengths[i] > Code->getBufferSize()) { |
268 | 0 | errs() << "error: invalid length " << Lengths[i] |
269 | 0 | << ", offset + length (" << Offsets[i] + Lengths[i] |
270 | 0 | << ") is outside the file.\n"; |
271 | 0 | return true; |
272 | 0 | } |
273 | 2 | End = Start.getLocWithOffset(Lengths[i]); |
274 | 38 | } else { |
275 | 38 | End = Sources.getLocForEndOfFile(ID); |
276 | 38 | } |
277 | 40 | unsigned Offset = Sources.getFileOffset(Start); |
278 | 40 | unsigned Length = Sources.getFileOffset(End) - Offset; |
279 | 40 | Ranges.push_back(tooling::Range(Offset, Length)); |
280 | 40 | } |
281 | 39 | return false; |
282 | 39 | } |
283 | | |
284 | 3 | static void outputReplacementXML(StringRef Text) { |
285 | | // FIXME: When we sort includes, we need to make sure the stream is correct |
286 | | // utf-8. |
287 | 3 | size_t From = 0; |
288 | 3 | size_t Index; |
289 | 7 | while ((Index = Text.find_first_of("\n\r<&", From)) != StringRef::npos) { |
290 | 4 | outs() << Text.substr(From, Index - From); |
291 | 4 | switch (Text[Index]) { |
292 | 2 | case '\n': |
293 | 2 | outs() << " "; |
294 | 2 | break; |
295 | 0 | case '\r': |
296 | 0 | outs() << " "; |
297 | 0 | break; |
298 | 2 | case '<': |
299 | 2 | outs() << "<"; |
300 | 2 | break; |
301 | 0 | case '&': |
302 | 0 | outs() << "&"; |
303 | 0 | break; |
304 | 0 | default: |
305 | 0 | llvm_unreachable("Unexpected character encountered!"); |
306 | 4 | } |
307 | 4 | From = Index + 1; |
308 | 4 | } |
309 | 3 | outs() << Text.substr(From); |
310 | 3 | } |
311 | | |
312 | 1 | static void outputReplacementsXML(const Replacements &Replaces) { |
313 | 3 | for (const auto &R : Replaces) { |
314 | 3 | outs() << "<replacement " |
315 | 3 | << "offset='" << R.getOffset() << "' " |
316 | 3 | << "length='" << R.getLength() << "'>"; |
317 | 3 | outputReplacementXML(R.getReplacementText()); |
318 | 3 | outs() << "</replacement>\n"; |
319 | 3 | } |
320 | 1 | } |
321 | | |
322 | | static bool |
323 | | emitReplacementWarnings(const Replacements &Replaces, StringRef AssumedFileName, |
324 | 2 | const std::unique_ptr<llvm::MemoryBuffer> &Code) { |
325 | 2 | if (Replaces.empty()) |
326 | 0 | return false; |
327 | | |
328 | 2 | unsigned Errors = 0; |
329 | 2 | if (WarnFormat && !NoWarnFormat) { |
330 | 2 | llvm::SourceMgr Mgr; |
331 | 2 | const char *StartBuf = Code->getBufferStart(); |
332 | | |
333 | 2 | Mgr.AddNewSourceBuffer( |
334 | 2 | MemoryBuffer::getMemBuffer(StartBuf, AssumedFileName), SMLoc()); |
335 | 2 | for (const auto &R : Replaces) { |
336 | 2 | SMDiagnostic Diag = Mgr.GetMessage( |
337 | 2 | SMLoc::getFromPointer(StartBuf + R.getOffset()), |
338 | 2 | WarningsAsErrors ? SourceMgr::DiagKind::DK_Error0 |
339 | 2 | : SourceMgr::DiagKind::DK_Warning, |
340 | 2 | "code should be clang-formatted [-Wclang-format-violations]"); |
341 | | |
342 | 2 | Diag.print(nullptr, llvm::errs(), (ShowColors && !NoShowColors)); |
343 | 2 | if (ErrorLimit && ++Errors >= ErrorLimit0 ) |
344 | 0 | break; |
345 | 2 | } |
346 | 2 | } |
347 | 2 | return WarningsAsErrors; |
348 | 2 | } |
349 | | |
350 | | static void outputXML(const Replacements &Replaces, |
351 | | const Replacements &FormatChanges, |
352 | | const FormattingAttemptStatus &Status, |
353 | | const cl::opt<unsigned> &Cursor, |
354 | 1 | unsigned CursorPosition) { |
355 | 1 | outs() << "<?xml version='1.0'?>\n<replacements " |
356 | 1 | "xml:space='preserve' incomplete_format='" |
357 | 1 | << (Status.FormatComplete ? "false" : "true"0 ) << "'"; |
358 | 1 | if (!Status.FormatComplete) |
359 | 0 | outs() << " line='" << Status.Line << "'"; |
360 | 1 | outs() << ">\n"; |
361 | 1 | if (Cursor.getNumOccurrences() != 0) |
362 | 0 | outs() << "<cursor>" << FormatChanges.getShiftedCodePosition(CursorPosition) |
363 | 0 | << "</cursor>\n"; |
364 | | |
365 | 1 | outputReplacementsXML(Replaces); |
366 | 1 | outs() << "</replacements>\n"; |
367 | 1 | } |
368 | | |
369 | | class ClangFormatDiagConsumer : public DiagnosticConsumer { |
370 | 0 | virtual void anchor() {} |
371 | | |
372 | | void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, |
373 | 0 | const Diagnostic &Info) override { |
374 | |
|
375 | 0 | SmallVector<char, 16> vec; |
376 | 0 | Info.FormatDiagnostic(vec); |
377 | 0 | errs() << "clang-format error:" << vec << "\n"; |
378 | 0 | } |
379 | | }; |
380 | | |
381 | | // Returns true on error. |
382 | 44 | static bool format(StringRef FileName) { |
383 | 44 | if (!OutputXML && Inplace43 && FileName == "-"5 ) { |
384 | 0 | errs() << "error: cannot use -i when reading from stdin.\n"; |
385 | 0 | return false; |
386 | 0 | } |
387 | | // On Windows, overwriting a file with an open file mapping doesn't work, |
388 | | // so read the whole file into memory when formatting in-place. |
389 | 44 | ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = |
390 | 44 | !OutputXML && Inplace43 ? MemoryBuffer::getFileAsStream(FileName)5 |
391 | 44 | : MemoryBuffer::getFileOrSTDIN(FileName)39 ; |
392 | 44 | if (std::error_code EC = CodeOrErr.getError()) { |
393 | 0 | errs() << EC.message() << "\n"; |
394 | 0 | return true; |
395 | 0 | } |
396 | 44 | std::unique_ptr<llvm::MemoryBuffer> Code = std::move(CodeOrErr.get()); |
397 | 44 | if (Code->getBufferSize() == 0) |
398 | 0 | return false; // Empty files are formatted correctly. |
399 | | |
400 | 44 | StringRef BufStr = Code->getBuffer(); |
401 | | |
402 | 44 | const char *InvalidBOM = SrcMgr::ContentCache::getInvalidBOM(BufStr); |
403 | | |
404 | 44 | if (InvalidBOM) { |
405 | 0 | errs() << "error: encoding with unsupported byte order mark \"" |
406 | 0 | << InvalidBOM << "\" detected"; |
407 | 0 | if (FileName != "-") |
408 | 0 | errs() << " in file '" << FileName << "'"; |
409 | 0 | errs() << ".\n"; |
410 | 0 | return true; |
411 | 0 | } |
412 | | |
413 | 44 | std::vector<tooling::Range> Ranges; |
414 | 44 | if (fillRanges(Code.get(), Ranges)) |
415 | 0 | return true; |
416 | 44 | StringRef AssumedFileName = (FileName == "-") ? AssumeFileName18 : FileName26 ; |
417 | 44 | if (AssumedFileName.empty()) { |
418 | 0 | llvm::errs() << "error: empty filenames are not allowed\n"; |
419 | 0 | return true; |
420 | 0 | } |
421 | | |
422 | 44 | llvm::Expected<FormatStyle> FormatStyle = |
423 | 44 | getStyle(Style, AssumedFileName, FallbackStyle, Code->getBuffer(), |
424 | 44 | nullptr, WNoErrorList.isSet(WNoError::Unknown)); |
425 | 44 | if (!FormatStyle) { |
426 | 4 | llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n"; |
427 | 4 | return true; |
428 | 4 | } |
429 | | |
430 | 40 | StringRef QualifierAlignmentOrder = QualifierAlignment; |
431 | | |
432 | 40 | FormatStyle->QualifierAlignment = |
433 | 40 | StringSwitch<FormatStyle::QualifierAlignmentStyle>( |
434 | 40 | QualifierAlignmentOrder.lower()) |
435 | 40 | .Case("right", FormatStyle::QAS_Right) |
436 | 40 | .Case("left", FormatStyle::QAS_Left) |
437 | 40 | .Default(FormatStyle->QualifierAlignment); |
438 | | |
439 | 40 | if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Left) |
440 | 0 | FormatStyle->QualifierOrder = {"const", "volatile", "type"}; |
441 | 40 | else if (FormatStyle->QualifierAlignment == FormatStyle::QAS_Right) |
442 | 0 | FormatStyle->QualifierOrder = {"type", "const", "volatile"}; |
443 | 40 | else if (QualifierAlignmentOrder.contains("type")) { |
444 | 0 | FormatStyle->QualifierAlignment = FormatStyle::QAS_Custom; |
445 | 0 | SmallVector<StringRef> Qualifiers; |
446 | 0 | QualifierAlignmentOrder.split(Qualifiers, " ", /*MaxSplit=*/-1, |
447 | 0 | /*KeepEmpty=*/false); |
448 | 0 | FormatStyle->QualifierOrder = {Qualifiers.begin(), Qualifiers.end()}; |
449 | 0 | } |
450 | | |
451 | 40 | if (SortIncludes.getNumOccurrences() != 0) { |
452 | 3 | if (SortIncludes) |
453 | 2 | FormatStyle->SortIncludes = FormatStyle::SI_CaseSensitive; |
454 | 1 | else |
455 | 1 | FormatStyle->SortIncludes = FormatStyle::SI_Never; |
456 | 3 | } |
457 | 40 | unsigned CursorPosition = Cursor; |
458 | 40 | Replacements Replaces = sortIncludes(*FormatStyle, Code->getBuffer(), Ranges, |
459 | 40 | AssumedFileName, &CursorPosition); |
460 | | |
461 | | // To format JSON insert a variable to trick the code into thinking its |
462 | | // JavaScript. |
463 | 40 | if (FormatStyle->isJson() && !FormatStyle->DisableFormat0 ) { |
464 | 0 | auto Err = Replaces.add(tooling::Replacement( |
465 | 0 | tooling::Replacement(AssumedFileName, 0, 0, "x = "))); |
466 | 0 | if (Err) { |
467 | 0 | llvm::errs() << "Bad Json variable insertion\n"; |
468 | 0 | } |
469 | 0 | } |
470 | | |
471 | 40 | auto ChangedCode = tooling::applyAllReplacements(Code->getBuffer(), Replaces); |
472 | 40 | if (!ChangedCode) { |
473 | 0 | llvm::errs() << llvm::toString(ChangedCode.takeError()) << "\n"; |
474 | 0 | return true; |
475 | 0 | } |
476 | | // Get new affected ranges after sorting `#includes`. |
477 | 40 | Ranges = tooling::calculateRangesAfterReplacements(Replaces, Ranges); |
478 | 40 | FormattingAttemptStatus Status; |
479 | 40 | Replacements FormatChanges = |
480 | 40 | reformat(*FormatStyle, *ChangedCode, Ranges, AssumedFileName, &Status); |
481 | 40 | Replaces = Replaces.merge(FormatChanges); |
482 | 40 | if (OutputXML || DryRun39 ) { |
483 | 3 | if (DryRun) { |
484 | 2 | return emitReplacementWarnings(Replaces, AssumedFileName, Code); |
485 | 2 | } else { |
486 | 1 | outputXML(Replaces, FormatChanges, Status, Cursor, CursorPosition); |
487 | 1 | } |
488 | 37 | } else { |
489 | 37 | IntrusiveRefCntPtr<llvm::vfs::InMemoryFileSystem> InMemoryFileSystem( |
490 | 37 | new llvm::vfs::InMemoryFileSystem); |
491 | 37 | FileManager Files(FileSystemOptions(), InMemoryFileSystem); |
492 | | |
493 | 37 | IntrusiveRefCntPtr<DiagnosticOptions> DiagOpts(new DiagnosticOptions()); |
494 | 37 | ClangFormatDiagConsumer IgnoreDiagnostics; |
495 | 37 | DiagnosticsEngine Diagnostics( |
496 | 37 | IntrusiveRefCntPtr<DiagnosticIDs>(new DiagnosticIDs), &*DiagOpts, |
497 | 37 | &IgnoreDiagnostics, false); |
498 | 37 | SourceManager Sources(Diagnostics, Files); |
499 | 37 | FileID ID = createInMemoryFile(AssumedFileName, *Code, Sources, Files, |
500 | 37 | InMemoryFileSystem.get()); |
501 | 37 | Rewriter Rewrite(Sources, LangOptions()); |
502 | 37 | tooling::applyAllReplacements(Replaces, Rewrite); |
503 | 37 | if (Inplace) { |
504 | 3 | if (Rewrite.overwriteChangedFiles()) |
505 | 0 | return true; |
506 | 34 | } else { |
507 | 34 | if (Cursor.getNumOccurrences() != 0) { |
508 | 2 | outs() << "{ \"Cursor\": " |
509 | 2 | << FormatChanges.getShiftedCodePosition(CursorPosition) |
510 | 2 | << ", \"IncompleteFormat\": " |
511 | 2 | << (Status.FormatComplete ? "false"1 : "true"1 ); |
512 | 2 | if (!Status.FormatComplete) |
513 | 1 | outs() << ", \"Line\": " << Status.Line; |
514 | 2 | outs() << " }\n"; |
515 | 2 | } |
516 | 34 | Rewrite.getEditBuffer(ID).write(outs()); |
517 | 34 | } |
518 | 37 | } |
519 | 38 | return false; |
520 | 40 | } |
521 | | |
522 | | } // namespace format |
523 | | } // namespace clang |
524 | | |
525 | 0 | static void PrintVersion(raw_ostream &OS) { |
526 | 0 | OS << clang::getClangToolFullVersion("clang-format") << '\n'; |
527 | 0 | } |
528 | | |
529 | | // Dump the configuration. |
530 | 5 | static int dumpConfig() { |
531 | 5 | StringRef FileName; |
532 | 5 | std::unique_ptr<llvm::MemoryBuffer> Code; |
533 | 5 | if (FileNames.empty()) { |
534 | | // We can't read the code to detect the language if there's no |
535 | | // file name, so leave Code empty here. |
536 | 0 | FileName = AssumeFileName; |
537 | 5 | } else { |
538 | | // Read in the code in case the filename alone isn't enough to |
539 | | // detect the language. |
540 | 5 | ErrorOr<std::unique_ptr<MemoryBuffer>> CodeOrErr = |
541 | 5 | MemoryBuffer::getFileOrSTDIN(FileNames[0]); |
542 | 5 | if (std::error_code EC = CodeOrErr.getError()) { |
543 | 0 | llvm::errs() << EC.message() << "\n"; |
544 | 0 | return 1; |
545 | 0 | } |
546 | 5 | FileName = (FileNames[0] == "-") ? AssumeFileName0 : FileNames[0]; |
547 | 5 | Code = std::move(CodeOrErr.get()); |
548 | 5 | } |
549 | 5 | llvm::Expected<clang::format::FormatStyle> FormatStyle = |
550 | 5 | clang::format::getStyle(Style, FileName, FallbackStyle, |
551 | 5 | Code ? Code->getBuffer() : ""0 ); |
552 | 5 | if (!FormatStyle) { |
553 | 0 | llvm::errs() << llvm::toString(FormatStyle.takeError()) << "\n"; |
554 | 0 | return 1; |
555 | 0 | } |
556 | 5 | std::string Config = clang::format::configurationAsText(*FormatStyle); |
557 | 5 | outs() << Config << "\n"; |
558 | 5 | return 0; |
559 | 5 | } |
560 | | |
561 | 49 | int main(int argc, const char **argv) { |
562 | 49 | llvm::InitLLVM X(argc, argv); |
563 | | |
564 | 49 | cl::HideUnrelatedOptions(ClangFormatCategory); |
565 | | |
566 | 49 | cl::SetVersionPrinter(PrintVersion); |
567 | 49 | cl::ParseCommandLineOptions( |
568 | 49 | argc, argv, |
569 | 49 | "A tool to format C/C++/Java/JavaScript/JSON/Objective-C/Protobuf/C# " |
570 | 49 | "code.\n\n" |
571 | 49 | "If no arguments are specified, it formats the code from standard input\n" |
572 | 49 | "and writes the result to the standard output.\n" |
573 | 49 | "If <file>s are given, it reformats the files. If -i is specified\n" |
574 | 49 | "together with <file>s, the files are edited in-place. Otherwise, the\n" |
575 | 49 | "result is written to the standard output.\n"); |
576 | | |
577 | 49 | if (Help) { |
578 | 0 | cl::PrintHelpMessage(); |
579 | 0 | return 0; |
580 | 0 | } |
581 | | |
582 | 49 | if (DumpConfig) { |
583 | 5 | return dumpConfig(); |
584 | 5 | } |
585 | | |
586 | 44 | if (!Files.empty()) { |
587 | 0 | std::ifstream ExternalFileOfFiles{std::string(Files)}; |
588 | 0 | std::string Line; |
589 | 0 | unsigned LineNo = 1; |
590 | 0 | while (std::getline(ExternalFileOfFiles, Line)) { |
591 | 0 | FileNames.push_back(Line); |
592 | 0 | LineNo++; |
593 | 0 | } |
594 | 0 | errs() << "Clang-formating " << LineNo << " files\n"; |
595 | 0 | } |
596 | | |
597 | 44 | bool Error = false; |
598 | 44 | if (FileNames.empty()) { |
599 | 18 | Error = clang::format::format("-"); |
600 | 18 | return Error ? 11 : 017 ; |
601 | 18 | } |
602 | 26 | if (FileNames.size() != 1 && |
603 | 26 | (4 !Offsets.empty()4 || !Lengths.empty()3 || !LineRanges.empty()3 )) { |
604 | 2 | errs() << "error: -offset, -length and -lines can only be used for " |
605 | 2 | "single file.\n"; |
606 | 2 | return 1; |
607 | 2 | } |
608 | | |
609 | 24 | unsigned FileNo = 1; |
610 | 26 | for (const auto &FileName : FileNames) { |
611 | 26 | if (Verbose) |
612 | 2 | errs() << "Formatting [" << FileNo++ << "/" << FileNames.size() << "] " |
613 | 2 | << FileName << "\n"; |
614 | 26 | Error |= clang::format::format(FileName); |
615 | 26 | } |
616 | 24 | return Error ? 13 : 021 ; |
617 | 26 | } |