]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/llvm/tools/clang/lib/Format/WhitespaceManager.cpp
MFV r319951: 8311 ZFS_READONLY is a little too strict
[FreeBSD/FreeBSD.git] / contrib / llvm / tools / clang / lib / Format / WhitespaceManager.cpp
1 //===--- WhitespaceManager.cpp - Format C++ code --------------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 ///
10 /// \file
11 /// \brief This file implements WhitespaceManager class.
12 ///
13 //===----------------------------------------------------------------------===//
14
15 #include "WhitespaceManager.h"
16 #include "llvm/ADT/STLExtras.h"
17
18 namespace clang {
19 namespace format {
20
21 bool WhitespaceManager::Change::IsBeforeInFile::
22 operator()(const Change &C1, const Change &C2) const {
23   return SourceMgr.isBeforeInTranslationUnit(
24       C1.OriginalWhitespaceRange.getBegin(),
25       C2.OriginalWhitespaceRange.getBegin());
26 }
27
28 WhitespaceManager::Change::Change(
29     bool CreateReplacement, SourceRange OriginalWhitespaceRange,
30     unsigned IndentLevel, int Spaces, unsigned StartOfTokenColumn,
31     unsigned NewlinesBefore, StringRef PreviousLinePostfix,
32     StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective,
33     bool IsStartOfDeclName, bool IsInsideToken)
34     : CreateReplacement(CreateReplacement),
35       OriginalWhitespaceRange(OriginalWhitespaceRange),
36       StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),
37       PreviousLinePostfix(PreviousLinePostfix),
38       CurrentLinePrefix(CurrentLinePrefix), Kind(Kind),
39       ContinuesPPDirective(ContinuesPPDirective),
40       IsStartOfDeclName(IsStartOfDeclName), IndentLevel(IndentLevel),
41       Spaces(Spaces), IsInsideToken(IsInsideToken), IsTrailingComment(false),
42       TokenLength(0), PreviousEndOfTokenColumn(0), EscapedNewlineColumn(0),
43       StartOfBlockComment(nullptr), IndentationOffset(0) {}
44
45 void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,
46                                           unsigned IndentLevel, unsigned Spaces,
47                                           unsigned StartOfTokenColumn,
48                                           bool InPPDirective) {
49   if (Tok.Finalized)
50     return;
51   Tok.Decision = (Newlines > 0) ? FD_Break : FD_Continue;
52   Changes.push_back(
53       Change(/*CreateReplacement=*/true, Tok.WhitespaceRange, IndentLevel,
54              Spaces, StartOfTokenColumn, Newlines, "", "", Tok.Tok.getKind(),
55              InPPDirective && !Tok.IsFirst,
56              Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName),
57              /*IsInsideToken=*/false));
58 }
59
60 void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
61                                             bool InPPDirective) {
62   if (Tok.Finalized)
63     return;
64   Changes.push_back(Change(
65       /*CreateReplacement=*/false, Tok.WhitespaceRange, /*IndentLevel=*/0,
66       /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore, "", "",
67       Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst,
68       Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName),
69       /*IsInsideToken=*/false));
70 }
71
72 void WhitespaceManager::replaceWhitespaceInToken(
73     const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
74     StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,
75     unsigned Newlines, unsigned IndentLevel, int Spaces) {
76   if (Tok.Finalized)
77     return;
78   SourceLocation Start = Tok.getStartOfNonWhitespace().getLocWithOffset(Offset);
79   Changes.push_back(Change(
80       true, SourceRange(Start, Start.getLocWithOffset(ReplaceChars)),
81       IndentLevel, Spaces, std::max(0, Spaces), Newlines, PreviousPostfix,
82       CurrentPrefix, Tok.is(TT_LineComment) ? tok::comment : tok::unknown,
83       InPPDirective && !Tok.IsFirst,
84       Tok.is(TT_StartOfName) || Tok.is(TT_FunctionDeclarationName),
85       /*IsInsideToken=*/Newlines == 0));
86 }
87
88 const tooling::Replacements &WhitespaceManager::generateReplacements() {
89   if (Changes.empty())
90     return Replaces;
91
92   std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr));
93   calculateLineBreakInformation();
94   alignConsecutiveDeclarations();
95   alignConsecutiveAssignments();
96   alignTrailingComments();
97   alignEscapedNewlines();
98   generateChanges();
99
100   return Replaces;
101 }
102
103 void WhitespaceManager::calculateLineBreakInformation() {
104   Changes[0].PreviousEndOfTokenColumn = 0;
105   Change *LastOutsideTokenChange = &Changes[0];
106   for (unsigned i = 1, e = Changes.size(); i != e; ++i) {
107     unsigned OriginalWhitespaceStart =
108         SourceMgr.getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin());
109     unsigned PreviousOriginalWhitespaceEnd = SourceMgr.getFileOffset(
110         Changes[i - 1].OriginalWhitespaceRange.getEnd());
111     Changes[i - 1].TokenLength = OriginalWhitespaceStart -
112                                  PreviousOriginalWhitespaceEnd +
113                                  Changes[i].PreviousLinePostfix.size() +
114                                  Changes[i - 1].CurrentLinePrefix.size();
115
116     // If there are multiple changes in this token, sum up all the changes until
117     // the end of the line.
118     if (Changes[i - 1].IsInsideToken)
119       LastOutsideTokenChange->TokenLength +=
120           Changes[i - 1].TokenLength + Changes[i - 1].Spaces;
121     else
122       LastOutsideTokenChange = &Changes[i - 1];
123
124     Changes[i].PreviousEndOfTokenColumn =
125         Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
126
127     Changes[i - 1].IsTrailingComment =
128         (Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof ||
129          (Changes[i].IsInsideToken && Changes[i].Kind == tok::comment)) &&
130         Changes[i - 1].Kind == tok::comment;
131   }
132   // FIXME: The last token is currently not always an eof token; in those
133   // cases, setting TokenLength of the last token to 0 is wrong.
134   Changes.back().TokenLength = 0;
135   Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment;
136
137   const WhitespaceManager::Change *LastBlockComment = nullptr;
138   for (auto &Change : Changes) {
139     // Reset the IsTrailingComment flag for changes inside of trailing comments
140     // so they don't get realigned later.
141     if (Change.IsInsideToken)
142       Change.IsTrailingComment = false;
143     Change.StartOfBlockComment = nullptr;
144     Change.IndentationOffset = 0;
145     if (Change.Kind == tok::comment) {
146       LastBlockComment = &Change;
147     } else if (Change.Kind == tok::unknown) {
148       if ((Change.StartOfBlockComment = LastBlockComment))
149         Change.IndentationOffset =
150             Change.StartOfTokenColumn -
151             Change.StartOfBlockComment->StartOfTokenColumn;
152     } else {
153       LastBlockComment = nullptr;
154     }
155   }
156 }
157
158 // Align a single sequence of tokens, see AlignTokens below.
159 template <typename F>
160 static void
161 AlignTokenSequence(unsigned Start, unsigned End, unsigned Column, F &&Matches,
162                    SmallVector<WhitespaceManager::Change, 16> &Changes) {
163   bool FoundMatchOnLine = false;
164   int Shift = 0;
165   for (unsigned i = Start; i != End; ++i) {
166     if (Changes[i].NewlinesBefore > 0) {
167       FoundMatchOnLine = false;
168       Shift = 0;
169     }
170
171     // If this is the first matching token to be aligned, remember by how many
172     // spaces it has to be shifted, so the rest of the changes on the line are
173     // shifted by the same amount
174     if (!FoundMatchOnLine && Matches(Changes[i])) {
175       FoundMatchOnLine = true;
176       Shift = Column - Changes[i].StartOfTokenColumn;
177       Changes[i].Spaces += Shift;
178     }
179
180     assert(Shift >= 0);
181     Changes[i].StartOfTokenColumn += Shift;
182     if (i + 1 != Changes.size())
183       Changes[i + 1].PreviousEndOfTokenColumn += Shift;
184   }
185 }
186
187 // Walk through all of the changes and find sequences of matching tokens to
188 // align. To do so, keep track of the lines and whether or not a matching token
189 // was found on a line. If a matching token is found, extend the current
190 // sequence. If the current line cannot be part of a sequence, e.g. because
191 // there is an empty line before it or it contains only non-matching tokens,
192 // finalize the previous sequence.
193 template <typename F>
194 static void AlignTokens(const FormatStyle &Style, F &&Matches,
195                         SmallVector<WhitespaceManager::Change, 16> &Changes) {
196   unsigned MinColumn = 0;
197   unsigned MaxColumn = UINT_MAX;
198
199   // Line number of the start and the end of the current token sequence.
200   unsigned StartOfSequence = 0;
201   unsigned EndOfSequence = 0;
202
203   // Keep track of the nesting level of matching tokens, i.e. the number of
204   // surrounding (), [], or {}. We will only align a sequence of matching
205   // token that share the same scope depth.
206   //
207   // FIXME: This could use FormatToken::NestingLevel information, but there is
208   // an outstanding issue wrt the brace scopes.
209   unsigned NestingLevelOfLastMatch = 0;
210   unsigned NestingLevel = 0;
211
212   // Keep track of the number of commas before the matching tokens, we will only
213   // align a sequence of matching tokens if they are preceded by the same number
214   // of commas.
215   unsigned CommasBeforeLastMatch = 0;
216   unsigned CommasBeforeMatch = 0;
217
218   // Whether a matching token has been found on the current line.
219   bool FoundMatchOnLine = false;
220
221   // Aligns a sequence of matching tokens, on the MinColumn column.
222   //
223   // Sequences start from the first matching token to align, and end at the
224   // first token of the first line that doesn't need to be aligned.
225   //
226   // We need to adjust the StartOfTokenColumn of each Change that is on a line
227   // containing any matching token to be aligned and located after such token.
228   auto AlignCurrentSequence = [&] {
229     if (StartOfSequence > 0 && StartOfSequence < EndOfSequence)
230       AlignTokenSequence(StartOfSequence, EndOfSequence, MinColumn, Matches,
231                          Changes);
232     MinColumn = 0;
233     MaxColumn = UINT_MAX;
234     StartOfSequence = 0;
235     EndOfSequence = 0;
236   };
237
238   for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
239     if (Changes[i].NewlinesBefore != 0) {
240       CommasBeforeMatch = 0;
241       EndOfSequence = i;
242       // If there is a blank line, or if the last line didn't contain any
243       // matching token, the sequence ends here.
244       if (Changes[i].NewlinesBefore > 1 || !FoundMatchOnLine)
245         AlignCurrentSequence();
246
247       FoundMatchOnLine = false;
248     }
249
250     if (Changes[i].Kind == tok::comma) {
251       ++CommasBeforeMatch;
252     } else if (Changes[i].Kind == tok::r_brace ||
253                Changes[i].Kind == tok::r_paren ||
254                Changes[i].Kind == tok::r_square) {
255       --NestingLevel;
256     } else if (Changes[i].Kind == tok::l_brace ||
257                Changes[i].Kind == tok::l_paren ||
258                Changes[i].Kind == tok::l_square) {
259       // We want sequences to skip over child scopes if possible, but not the
260       // other way around.
261       NestingLevelOfLastMatch = std::min(NestingLevelOfLastMatch, NestingLevel);
262       ++NestingLevel;
263     }
264
265     if (!Matches(Changes[i]))
266       continue;
267
268     // If there is more than one matching token per line, or if the number of
269     // preceding commas, or the scope depth, do not match anymore, end the
270     // sequence.
271     if (FoundMatchOnLine || CommasBeforeMatch != CommasBeforeLastMatch ||
272         NestingLevel != NestingLevelOfLastMatch)
273       AlignCurrentSequence();
274
275     CommasBeforeLastMatch = CommasBeforeMatch;
276     NestingLevelOfLastMatch = NestingLevel;
277     FoundMatchOnLine = true;
278
279     if (StartOfSequence == 0)
280       StartOfSequence = i;
281
282     unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
283     int LineLengthAfter = -Changes[i].Spaces;
284     for (unsigned j = i; j != e && Changes[j].NewlinesBefore == 0; ++j)
285       LineLengthAfter += Changes[j].Spaces + Changes[j].TokenLength;
286     unsigned ChangeMaxColumn = Style.ColumnLimit - LineLengthAfter;
287
288     // If we are restricted by the maximum column width, end the sequence.
289     if (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn ||
290         CommasBeforeLastMatch != CommasBeforeMatch) {
291       AlignCurrentSequence();
292       StartOfSequence = i;
293     }
294
295     MinColumn = std::max(MinColumn, ChangeMinColumn);
296     MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
297   }
298
299   EndOfSequence = Changes.size();
300   AlignCurrentSequence();
301 }
302
303 void WhitespaceManager::alignConsecutiveAssignments() {
304   if (!Style.AlignConsecutiveAssignments)
305     return;
306
307   AlignTokens(Style,
308               [&](const Change &C) {
309                 // Do not align on equal signs that are first on a line.
310                 if (C.NewlinesBefore > 0)
311                   return false;
312
313                 // Do not align on equal signs that are last on a line.
314                 if (&C != &Changes.back() && (&C + 1)->NewlinesBefore > 0)
315                   return false;
316
317                 return C.Kind == tok::equal;
318               },
319               Changes);
320 }
321
322 void WhitespaceManager::alignConsecutiveDeclarations() {
323   if (!Style.AlignConsecutiveDeclarations)
324     return;
325
326   // FIXME: Currently we don't handle properly the PointerAlignment: Right
327   // The * and & are not aligned and are left dangling. Something has to be done
328   // about it, but it raises the question of alignment of code like:
329   //   const char* const* v1;
330   //   float const* v2;
331   //   SomeVeryLongType const& v3;
332
333   AlignTokens(Style, [](Change const &C) { return C.IsStartOfDeclName; },
334               Changes);
335 }
336
337 void WhitespaceManager::alignTrailingComments() {
338   unsigned MinColumn = 0;
339   unsigned MaxColumn = UINT_MAX;
340   unsigned StartOfSequence = 0;
341   bool BreakBeforeNext = false;
342   unsigned Newlines = 0;
343   for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
344     if (Changes[i].StartOfBlockComment)
345       continue;
346     Newlines += Changes[i].NewlinesBefore;
347     if (!Changes[i].IsTrailingComment)
348       continue;
349
350     unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
351     unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
352
353     // If we don't create a replacement for this change, we have to consider
354     // it to be immovable.
355     if (!Changes[i].CreateReplacement)
356       ChangeMaxColumn = ChangeMinColumn;
357
358     if (i + 1 != e && Changes[i + 1].ContinuesPPDirective)
359       ChangeMaxColumn -= 2;
360     // If this comment follows an } in column 0, it probably documents the
361     // closing of a namespace and we don't want to align it.
362     bool FollowsRBraceInColumn0 = i > 0 && Changes[i].NewlinesBefore == 0 &&
363                                   Changes[i - 1].Kind == tok::r_brace &&
364                                   Changes[i - 1].StartOfTokenColumn == 0;
365     bool WasAlignedWithStartOfNextLine = false;
366     if (Changes[i].NewlinesBefore == 1) { // A comment on its own line.
367       unsigned CommentColumn = SourceMgr.getSpellingColumnNumber(
368           Changes[i].OriginalWhitespaceRange.getEnd());
369       for (unsigned j = i + 1; j != e; ++j) {
370         if (Changes[j].Kind == tok::comment ||
371             Changes[j].Kind == tok::unknown)
372           // Skip over comments and unknown tokens. "unknown tokens are used for
373           // the continuation of multiline comments.
374           continue;
375
376         unsigned NextColumn = SourceMgr.getSpellingColumnNumber(
377             Changes[j].OriginalWhitespaceRange.getEnd());
378         // The start of the next token was previously aligned with the
379         // start of this comment.
380         WasAlignedWithStartOfNextLine =
381             CommentColumn == NextColumn ||
382             CommentColumn == NextColumn + Style.IndentWidth;
383         break;
384       }
385     }
386     if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) {
387       alignTrailingComments(StartOfSequence, i, MinColumn);
388       MinColumn = ChangeMinColumn;
389       MaxColumn = ChangeMinColumn;
390       StartOfSequence = i;
391     } else if (BreakBeforeNext || Newlines > 1 ||
392                (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
393                // Break the comment sequence if the previous line did not end
394                // in a trailing comment.
395                (Changes[i].NewlinesBefore == 1 && i > 0 &&
396                 !Changes[i - 1].IsTrailingComment) ||
397                WasAlignedWithStartOfNextLine) {
398       alignTrailingComments(StartOfSequence, i, MinColumn);
399       MinColumn = ChangeMinColumn;
400       MaxColumn = ChangeMaxColumn;
401       StartOfSequence = i;
402     } else {
403       MinColumn = std::max(MinColumn, ChangeMinColumn);
404       MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
405     }
406     BreakBeforeNext =
407         (i == 0) || (Changes[i].NewlinesBefore > 1) ||
408         // Never start a sequence with a comment at the beginning of
409         // the line.
410         (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
411     Newlines = 0;
412   }
413   alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
414 }
415
416 void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
417                                               unsigned Column) {
418   for (unsigned i = Start; i != End; ++i) {
419     int Shift = 0;
420     if (Changes[i].IsTrailingComment) {
421       Shift = Column - Changes[i].StartOfTokenColumn;
422     }
423     if (Changes[i].StartOfBlockComment) {
424       Shift = Changes[i].IndentationOffset +
425               Changes[i].StartOfBlockComment->StartOfTokenColumn -
426               Changes[i].StartOfTokenColumn;
427     }
428     assert(Shift >= 0);
429     Changes[i].Spaces += Shift;
430     if (i + 1 != Changes.size())
431       Changes[i + 1].PreviousEndOfTokenColumn += Shift;
432     Changes[i].StartOfTokenColumn += Shift;
433   }
434 }
435
436 void WhitespaceManager::alignEscapedNewlines() {
437   unsigned MaxEndOfLine =
438       Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
439   unsigned StartOfMacro = 0;
440   for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
441     Change &C = Changes[i];
442     if (C.NewlinesBefore > 0) {
443       if (C.ContinuesPPDirective) {
444         MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
445       } else {
446         alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
447         MaxEndOfLine = Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
448         StartOfMacro = i;
449       }
450     }
451   }
452   alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
453 }
454
455 void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
456                                              unsigned Column) {
457   for (unsigned i = Start; i < End; ++i) {
458     Change &C = Changes[i];
459     if (C.NewlinesBefore > 0) {
460       assert(C.ContinuesPPDirective);
461       if (C.PreviousEndOfTokenColumn + 1 > Column)
462         C.EscapedNewlineColumn = 0;
463       else
464         C.EscapedNewlineColumn = Column;
465     }
466   }
467 }
468
469 void WhitespaceManager::generateChanges() {
470   for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
471     const Change &C = Changes[i];
472     if (i > 0) {
473       assert(Changes[i - 1].OriginalWhitespaceRange.getBegin() !=
474                  C.OriginalWhitespaceRange.getBegin() &&
475              "Generating two replacements for the same location");
476     }
477     if (C.CreateReplacement) {
478       std::string ReplacementText = C.PreviousLinePostfix;
479       if (C.ContinuesPPDirective)
480         appendNewlineText(ReplacementText, C.NewlinesBefore,
481                           C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn);
482       else
483         appendNewlineText(ReplacementText, C.NewlinesBefore);
484       appendIndentText(ReplacementText, C.IndentLevel, std::max(0, C.Spaces),
485                        C.StartOfTokenColumn - std::max(0, C.Spaces));
486       ReplacementText.append(C.CurrentLinePrefix);
487       storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
488     }
489   }
490 }
491
492 void WhitespaceManager::storeReplacement(SourceRange Range,
493                                          StringRef Text) {
494   unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
495                               SourceMgr.getFileOffset(Range.getBegin());
496   // Don't create a replacement, if it does not change anything.
497   if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
498                 WhitespaceLength) == Text)
499     return;
500   auto Err = Replaces.add(tooling::Replacement(
501       SourceMgr, CharSourceRange::getCharRange(Range), Text));
502   // FIXME: better error handling. For now, just print an error message in the
503   // release version.
504   if (Err) {
505     llvm::errs() << llvm::toString(std::move(Err)) << "\n";
506     assert(false);
507   }
508 }
509
510 void WhitespaceManager::appendNewlineText(std::string &Text,
511                                           unsigned Newlines) {
512   for (unsigned i = 0; i < Newlines; ++i)
513     Text.append(UseCRLF ? "\r\n" : "\n");
514 }
515
516 void WhitespaceManager::appendNewlineText(std::string &Text, unsigned Newlines,
517                                           unsigned PreviousEndOfTokenColumn,
518                                           unsigned EscapedNewlineColumn) {
519   if (Newlines > 0) {
520     unsigned Offset =
521         std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
522     for (unsigned i = 0; i < Newlines; ++i) {
523       Text.append(EscapedNewlineColumn - Offset - 1, ' ');
524       Text.append(UseCRLF ? "\\\r\n" : "\\\n");
525       Offset = 0;
526     }
527   }
528 }
529
530 void WhitespaceManager::appendIndentText(std::string &Text,
531                                          unsigned IndentLevel, unsigned Spaces,
532                                          unsigned WhitespaceStartColumn) {
533   switch (Style.UseTab) {
534   case FormatStyle::UT_Never:
535     Text.append(Spaces, ' ');
536     break;
537   case FormatStyle::UT_Always: {
538     unsigned FirstTabWidth =
539         Style.TabWidth - WhitespaceStartColumn % Style.TabWidth;
540     // Indent with tabs only when there's at least one full tab.
541     if (FirstTabWidth + Style.TabWidth <= Spaces) {
542       Spaces -= FirstTabWidth;
543       Text.append("\t");
544     }
545     Text.append(Spaces / Style.TabWidth, '\t');
546     Text.append(Spaces % Style.TabWidth, ' ');
547     break;
548   }
549   case FormatStyle::UT_ForIndentation:
550     if (WhitespaceStartColumn == 0) {
551       unsigned Indentation = IndentLevel * Style.IndentWidth;
552       // This happens, e.g. when a line in a block comment is indented less than
553       // the first one.
554       if (Indentation > Spaces)
555         Indentation = Spaces;
556       unsigned Tabs = Indentation / Style.TabWidth;
557       Text.append(Tabs, '\t');
558       Spaces -= Tabs * Style.TabWidth;
559     }
560     Text.append(Spaces, ' ');
561     break;
562   case FormatStyle::UT_ForContinuationAndIndentation:
563     if (WhitespaceStartColumn == 0) {
564       unsigned Tabs = Spaces / Style.TabWidth;
565       Text.append(Tabs, '\t');
566       Spaces -= Tabs * Style.TabWidth;
567     }
568     Text.append(Spaces, ' ');
569     break;
570   }
571 }
572
573 } // namespace format
574 } // namespace clang