]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/llvm/tools/clang/lib/Format/WhitespaceManager.cpp
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.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
22 WhitespaceManager::Change::IsBeforeInFile::operator()(const Change &C1,
23                                                       const Change &C2) const {
24   return SourceMgr.isBeforeInTranslationUnit(
25       C1.OriginalWhitespaceRange.getBegin(),
26       C2.OriginalWhitespaceRange.getBegin());
27 }
28
29 WhitespaceManager::Change::Change(
30     bool CreateReplacement, const SourceRange &OriginalWhitespaceRange,
31     unsigned IndentLevel, unsigned Spaces, unsigned StartOfTokenColumn,
32     unsigned NewlinesBefore, StringRef PreviousLinePostfix,
33     StringRef CurrentLinePrefix, tok::TokenKind Kind, bool ContinuesPPDirective)
34     : CreateReplacement(CreateReplacement),
35       OriginalWhitespaceRange(OriginalWhitespaceRange),
36       StartOfTokenColumn(StartOfTokenColumn), NewlinesBefore(NewlinesBefore),
37       PreviousLinePostfix(PreviousLinePostfix),
38       CurrentLinePrefix(CurrentLinePrefix), Kind(Kind),
39       ContinuesPPDirective(ContinuesPPDirective), IndentLevel(IndentLevel),
40       Spaces(Spaces) {}
41
42 void WhitespaceManager::reset() {
43   Changes.clear();
44   Replaces.clear();
45 }
46
47 void WhitespaceManager::replaceWhitespace(FormatToken &Tok, unsigned Newlines,
48                                           unsigned IndentLevel, unsigned Spaces,
49                                           unsigned StartOfTokenColumn,
50                                           bool InPPDirective) {
51   if (Tok.Finalized)
52     return;
53   Tok.Decision = (Newlines > 0) ? FD_Break : FD_Continue;
54   Changes.push_back(Change(true, Tok.WhitespaceRange, IndentLevel, Spaces,
55                            StartOfTokenColumn, Newlines, "", "",
56                            Tok.Tok.getKind(), InPPDirective && !Tok.IsFirst));
57 }
58
59 void WhitespaceManager::addUntouchableToken(const FormatToken &Tok,
60                                             bool InPPDirective) {
61   if (Tok.Finalized)
62     return;
63   Changes.push_back(Change(false, Tok.WhitespaceRange, /*IndentLevel=*/0,
64                            /*Spaces=*/0, Tok.OriginalColumn, Tok.NewlinesBefore,
65                            "", "", Tok.Tok.getKind(),
66                            InPPDirective && !Tok.IsFirst));
67 }
68
69 void WhitespaceManager::replaceWhitespaceInToken(
70     const FormatToken &Tok, unsigned Offset, unsigned ReplaceChars,
71     StringRef PreviousPostfix, StringRef CurrentPrefix, bool InPPDirective,
72     unsigned Newlines, unsigned IndentLevel, unsigned Spaces) {
73   if (Tok.Finalized)
74     return;
75   Changes.push_back(Change(
76       true, SourceRange(Tok.getStartOfNonWhitespace().getLocWithOffset(Offset),
77                         Tok.getStartOfNonWhitespace().getLocWithOffset(
78                             Offset + ReplaceChars)),
79       IndentLevel, Spaces, Spaces, Newlines, PreviousPostfix, CurrentPrefix,
80       // If we don't add a newline this change doesn't start a comment. Thus,
81       // when we align line comments, we don't need to treat this change as one.
82       // FIXME: We still need to take this change in account to properly
83       // calculate the new length of the comment and to calculate the changes
84       // for which to do the alignment when aligning comments.
85       Tok.Type == TT_LineComment && Newlines > 0 ? tok::comment : tok::unknown,
86       InPPDirective && !Tok.IsFirst));
87 }
88
89 const tooling::Replacements &WhitespaceManager::generateReplacements() {
90   if (Changes.empty())
91     return Replaces;
92
93   std::sort(Changes.begin(), Changes.end(), Change::IsBeforeInFile(SourceMgr));
94   calculateLineBreakInformation();
95   alignTrailingComments();
96   alignEscapedNewlines();
97   generateChanges();
98
99   return Replaces;
100 }
101
102 void WhitespaceManager::calculateLineBreakInformation() {
103   Changes[0].PreviousEndOfTokenColumn = 0;
104   for (unsigned i = 1, e = Changes.size(); i != e; ++i) {
105     unsigned OriginalWhitespaceStart =
106         SourceMgr.getFileOffset(Changes[i].OriginalWhitespaceRange.getBegin());
107     unsigned PreviousOriginalWhitespaceEnd = SourceMgr.getFileOffset(
108         Changes[i - 1].OriginalWhitespaceRange.getEnd());
109     Changes[i - 1].TokenLength = OriginalWhitespaceStart -
110                                  PreviousOriginalWhitespaceEnd +
111                                  Changes[i].PreviousLinePostfix.size() +
112                                  Changes[i - 1].CurrentLinePrefix.size();
113
114     Changes[i].PreviousEndOfTokenColumn =
115         Changes[i - 1].StartOfTokenColumn + Changes[i - 1].TokenLength;
116
117     Changes[i - 1].IsTrailingComment =
118         (Changes[i].NewlinesBefore > 0 || Changes[i].Kind == tok::eof) &&
119         Changes[i - 1].Kind == tok::comment;
120   }
121   // FIXME: The last token is currently not always an eof token; in those
122   // cases, setting TokenLength of the last token to 0 is wrong.
123   Changes.back().TokenLength = 0;
124   Changes.back().IsTrailingComment = Changes.back().Kind == tok::comment;
125 }
126
127 void WhitespaceManager::alignTrailingComments() {
128   unsigned MinColumn = 0;
129   unsigned MaxColumn = UINT_MAX;
130   unsigned StartOfSequence = 0;
131   bool BreakBeforeNext = false;
132   unsigned Newlines = 0;
133   for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
134     unsigned ChangeMinColumn = Changes[i].StartOfTokenColumn;
135     // FIXME: Correctly handle ChangeMaxColumn in PP directives.
136     unsigned ChangeMaxColumn = Style.ColumnLimit - Changes[i].TokenLength;
137     Newlines += Changes[i].NewlinesBefore;
138     if (Changes[i].IsTrailingComment) {
139       // If this comment follows an } in column 0, it probably documents the
140       // closing of a namespace and we don't want to align it.
141       bool FollowsRBraceInColumn0 = i > 0 && Changes[i].NewlinesBefore == 0 &&
142                                     Changes[i - 1].Kind == tok::r_brace &&
143                                     Changes[i - 1].StartOfTokenColumn == 0;
144       bool WasAlignedWithStartOfNextLine = false;
145       if (Changes[i].NewlinesBefore == 1) { // A comment on its own line.
146         for (unsigned j = i + 1; j != e; ++j) {
147           if (Changes[j].Kind != tok::comment) { // Skip over comments.
148             // The start of the next token was previously aligned with the
149             // start of this comment.
150             WasAlignedWithStartOfNextLine =
151                 (SourceMgr.getSpellingColumnNumber(
152                      Changes[i].OriginalWhitespaceRange.getEnd()) ==
153                  SourceMgr.getSpellingColumnNumber(
154                      Changes[j].OriginalWhitespaceRange.getEnd()));
155             break;
156           }
157         }
158       }
159       if (!Style.AlignTrailingComments || FollowsRBraceInColumn0) {
160         alignTrailingComments(StartOfSequence, i, MinColumn);
161         MinColumn = ChangeMinColumn;
162         MaxColumn = ChangeMinColumn;
163         StartOfSequence = i;
164       } else if (BreakBeforeNext || Newlines > 1 ||
165                  (ChangeMinColumn > MaxColumn || ChangeMaxColumn < MinColumn) ||
166                  // Break the comment sequence if the previous line did not end
167                  // in a trailing comment.
168                  (Changes[i].NewlinesBefore == 1 && i > 0 &&
169                   !Changes[i - 1].IsTrailingComment) ||
170                  WasAlignedWithStartOfNextLine) {
171         alignTrailingComments(StartOfSequence, i, MinColumn);
172         MinColumn = ChangeMinColumn;
173         MaxColumn = ChangeMaxColumn;
174         StartOfSequence = i;
175       } else {
176         MinColumn = std::max(MinColumn, ChangeMinColumn);
177         MaxColumn = std::min(MaxColumn, ChangeMaxColumn);
178       }
179       BreakBeforeNext =
180           (i == 0) || (Changes[i].NewlinesBefore > 1) ||
181           // Never start a sequence with a comment at the beginning of
182           // the line.
183           (Changes[i].NewlinesBefore == 1 && StartOfSequence == i);
184       Newlines = 0;
185     }
186   }
187   alignTrailingComments(StartOfSequence, Changes.size(), MinColumn);
188 }
189
190 void WhitespaceManager::alignTrailingComments(unsigned Start, unsigned End,
191                                               unsigned Column) {
192   for (unsigned i = Start; i != End; ++i) {
193     if (Changes[i].IsTrailingComment) {
194       assert(Column >= Changes[i].StartOfTokenColumn);
195       Changes[i].Spaces += Column - Changes[i].StartOfTokenColumn;
196       Changes[i].StartOfTokenColumn = Column;
197     }
198   }
199 }
200
201 void WhitespaceManager::alignEscapedNewlines() {
202   unsigned MaxEndOfLine =
203       Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
204   unsigned StartOfMacro = 0;
205   for (unsigned i = 1, e = Changes.size(); i < e; ++i) {
206     Change &C = Changes[i];
207     if (C.NewlinesBefore > 0) {
208       if (C.ContinuesPPDirective) {
209         MaxEndOfLine = std::max(C.PreviousEndOfTokenColumn + 2, MaxEndOfLine);
210       } else {
211         alignEscapedNewlines(StartOfMacro + 1, i, MaxEndOfLine);
212         MaxEndOfLine = Style.AlignEscapedNewlinesLeft ? 0 : Style.ColumnLimit;
213         StartOfMacro = i;
214       }
215     }
216   }
217   alignEscapedNewlines(StartOfMacro + 1, Changes.size(), MaxEndOfLine);
218 }
219
220 void WhitespaceManager::alignEscapedNewlines(unsigned Start, unsigned End,
221                                              unsigned Column) {
222   for (unsigned i = Start; i < End; ++i) {
223     Change &C = Changes[i];
224     if (C.NewlinesBefore > 0) {
225       assert(C.ContinuesPPDirective);
226       if (C.PreviousEndOfTokenColumn + 1 > Column)
227         C.EscapedNewlineColumn = 0;
228       else
229         C.EscapedNewlineColumn = Column;
230     }
231   }
232 }
233
234 void WhitespaceManager::generateChanges() {
235   for (unsigned i = 0, e = Changes.size(); i != e; ++i) {
236     const Change &C = Changes[i];
237     if (C.CreateReplacement) {
238       std::string ReplacementText = C.PreviousLinePostfix;
239       if (C.ContinuesPPDirective)
240         appendNewlineText(ReplacementText, C.NewlinesBefore,
241                           C.PreviousEndOfTokenColumn, C.EscapedNewlineColumn);
242       else
243         appendNewlineText(ReplacementText, C.NewlinesBefore);
244       appendIndentText(ReplacementText, C.IndentLevel, C.Spaces,
245                        C.StartOfTokenColumn - C.Spaces);
246       ReplacementText.append(C.CurrentLinePrefix);
247       storeReplacement(C.OriginalWhitespaceRange, ReplacementText);
248     }
249   }
250 }
251
252 void WhitespaceManager::storeReplacement(const SourceRange &Range,
253                                          StringRef Text) {
254   unsigned WhitespaceLength = SourceMgr.getFileOffset(Range.getEnd()) -
255                               SourceMgr.getFileOffset(Range.getBegin());
256   // Don't create a replacement, if it does not change anything.
257   if (StringRef(SourceMgr.getCharacterData(Range.getBegin()),
258                 WhitespaceLength) == Text)
259     return;
260   Replaces.insert(tooling::Replacement(
261       SourceMgr, CharSourceRange::getCharRange(Range), Text));
262 }
263
264 void WhitespaceManager::appendNewlineText(std::string &Text,
265                                           unsigned Newlines) {
266   for (unsigned i = 0; i < Newlines; ++i)
267     Text.append(UseCRLF ? "\r\n" : "\n");
268 }
269
270 void WhitespaceManager::appendNewlineText(std::string &Text, unsigned Newlines,
271                                           unsigned PreviousEndOfTokenColumn,
272                                           unsigned EscapedNewlineColumn) {
273   if (Newlines > 0) {
274     unsigned Offset =
275         std::min<int>(EscapedNewlineColumn - 1, PreviousEndOfTokenColumn);
276     for (unsigned i = 0; i < Newlines; ++i) {
277       Text.append(std::string(EscapedNewlineColumn - Offset - 1, ' '));
278       Text.append(UseCRLF ? "\\\r\n" : "\\\n");
279       Offset = 0;
280     }
281   }
282 }
283
284 void WhitespaceManager::appendIndentText(std::string &Text,
285                                          unsigned IndentLevel, unsigned Spaces,
286                                          unsigned WhitespaceStartColumn) {
287   switch (Style.UseTab) {
288   case FormatStyle::UT_Never:
289     Text.append(std::string(Spaces, ' '));
290     break;
291   case FormatStyle::UT_Always: {
292     unsigned FirstTabWidth =
293         Style.TabWidth - WhitespaceStartColumn % Style.TabWidth;
294     // Indent with tabs only when there's at least one full tab.
295     if (FirstTabWidth + Style.TabWidth <= Spaces) {
296       Spaces -= FirstTabWidth;
297       Text.append("\t");
298     }
299     Text.append(std::string(Spaces / Style.TabWidth, '\t'));
300     Text.append(std::string(Spaces % Style.TabWidth, ' '));
301     break;
302   }
303   case FormatStyle::UT_ForIndentation:
304     if (WhitespaceStartColumn == 0) {
305       unsigned Indentation = IndentLevel * Style.IndentWidth;
306       // This happens, e.g. when a line in a block comment is indented less than
307       // the first one.
308       if (Indentation > Spaces)
309         Indentation = Spaces;
310       unsigned Tabs = Indentation / Style.TabWidth;
311       Text.append(std::string(Tabs, '\t'));
312       Spaces -= Tabs * Style.TabWidth;
313     }
314     Text.append(std::string(Spaces, ' '));
315     break;
316   }
317 }
318
319 } // namespace format
320 } // namespace clang