/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Format/FormatToken.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===--- FormatToken.cpp - Format C++ code --------------------------------===// |
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 specific functions of \c FormatTokens and their |
11 | | /// roles. |
12 | | /// |
13 | | //===----------------------------------------------------------------------===// |
14 | | |
15 | | #include "FormatToken.h" |
16 | | #include "ContinuationIndenter.h" |
17 | | #include "llvm/ADT/SmallVector.h" |
18 | | #include "llvm/Support/Debug.h" |
19 | | #include <climits> |
20 | | |
21 | | namespace clang { |
22 | | namespace format { |
23 | | |
24 | 0 | const char *getTokenTypeName(TokenType Type) { |
25 | 0 | static const char *const TokNames[] = { |
26 | 0 | #define TYPE(X) #X, |
27 | 0 | LIST_TOKEN_TYPES |
28 | 0 | #undef TYPE |
29 | 0 | nullptr}; |
30 | |
|
31 | 0 | if (Type < NUM_TOKEN_TYPES) |
32 | 0 | return TokNames[Type]; |
33 | 0 | llvm_unreachable("unknown TokenType"); |
34 | 0 | return nullptr; |
35 | 0 | } |
36 | | |
37 | | // FIXME: This is copy&pasted from Sema. Put it in a common place and remove |
38 | | // duplication. |
39 | 222k | bool FormatToken::isSimpleTypeSpecifier() const { |
40 | 222k | switch (Tok.getKind()) { |
41 | 3 | case tok::kw_short: |
42 | 44 | case tok::kw_long: |
43 | 44 | case tok::kw___int64: |
44 | 44 | case tok::kw___int128: |
45 | 44 | case tok::kw_signed: |
46 | 305 | case tok::kw_unsigned: |
47 | 9.00k | case tok::kw_void: |
48 | 9.21k | case tok::kw_char: |
49 | 23.9k | case tok::kw_int: |
50 | 23.9k | case tok::kw_half: |
51 | 24.1k | case tok::kw_float: |
52 | 24.3k | case tok::kw_double: |
53 | 24.3k | case tok::kw___bf16: |
54 | 24.3k | case tok::kw__Float16: |
55 | 24.3k | case tok::kw___float128: |
56 | 24.3k | case tok::kw_wchar_t: |
57 | 25.1k | case tok::kw_bool: |
58 | 25.1k | case tok::kw___underlying_type: |
59 | 25.1k | case tok::annot_typename: |
60 | 25.1k | case tok::kw_char8_t: |
61 | 25.1k | case tok::kw_char16_t: |
62 | 25.1k | case tok::kw_char32_t: |
63 | 25.2k | case tok::kw_typeof: |
64 | 25.2k | case tok::kw_decltype: |
65 | 25.3k | case tok::kw__Atomic: |
66 | 25.3k | return true; |
67 | 197k | default: |
68 | 197k | return false; |
69 | 222k | } |
70 | 222k | } |
71 | | |
72 | 9.04k | TokenRole::~TokenRole() {} |
73 | | |
74 | 0 | void TokenRole::precomputeFormattingInfos(const FormatToken *Token) {} |
75 | | |
76 | | unsigned CommaSeparatedList::formatAfterToken(LineState &State, |
77 | | ContinuationIndenter *Indenter, |
78 | 130k | bool DryRun) { |
79 | 130k | if (State.NextToken == nullptr || !State.NextToken->Previous130k ) |
80 | 1 | return 0; |
81 | | |
82 | 130k | if (Formats.size() == 1) |
83 | 108 | return 0; // Handled by formatFromToken |
84 | | |
85 | | // Ensure that we start on the opening brace. |
86 | 130k | const FormatToken *LBrace = |
87 | 130k | State.NextToken->Previous->getPreviousNonComment(); |
88 | 130k | if (!LBrace || !LBrace->isOneOf(tok::l_brace, TT_ArrayInitializerLSquare) || |
89 | 7.07k | LBrace->is(BK_Block) || LBrace->is(TT_DictLiteral)6.68k || |
90 | 6.20k | LBrace->Next->is(TT_DesignatedInitializerPeriod)) |
91 | 123k | return 0; |
92 | | |
93 | | // Calculate the number of code points we have to format this list. As the |
94 | | // first token is already placed, we have to subtract it. |
95 | 6.15k | unsigned RemainingCodePoints = |
96 | 6.15k | Style.ColumnLimit - State.Column + State.NextToken->Previous->ColumnWidth; |
97 | | |
98 | | // Find the best ColumnFormat, i.e. the best number of columns to use. |
99 | 6.15k | const ColumnFormat *Format = getColumnFormat(RemainingCodePoints); |
100 | | |
101 | | // If no ColumnFormat can be used, the braced list would generally be |
102 | | // bin-packed. Add a severe penalty to this so that column layouts are |
103 | | // preferred if possible. |
104 | 6.15k | if (!Format) |
105 | 5.73k | return 10000; |
106 | | |
107 | | // Format the entire list. |
108 | 424 | unsigned Penalty = 0; |
109 | 424 | unsigned Column = 0; |
110 | 424 | unsigned Item = 0; |
111 | 20.1k | while (State.NextToken != LBrace->MatchingParen) { |
112 | 19.7k | bool NewLine = false; |
113 | 19.7k | unsigned ExtraSpaces = 0; |
114 | | |
115 | | // If the previous token was one of our commas, we are now on the next item. |
116 | 19.7k | if (Item < Commas.size() && State.NextToken->Previous == Commas[Item]19.1k ) { |
117 | 6.63k | if (!State.NextToken->isTrailingComment()) { |
118 | 6.42k | ExtraSpaces += Format->ColumnSizes[Column] - ItemLengths[Item]; |
119 | 6.42k | ++Column; |
120 | 6.42k | } |
121 | 6.63k | ++Item; |
122 | 6.63k | } |
123 | | |
124 | 19.7k | if (Column == Format->Columns || State.NextToken->MustBreakBefore19.1k ) { |
125 | 851 | Column = 0; |
126 | 851 | NewLine = true; |
127 | 851 | } |
128 | | |
129 | | // Place token using the continuation indenter and store the penalty. |
130 | 19.7k | Penalty += Indenter->addTokenToState(State, NewLine, DryRun, ExtraSpaces); |
131 | 19.7k | } |
132 | 424 | return Penalty; |
133 | 424 | } |
134 | | |
135 | | unsigned CommaSeparatedList::formatFromToken(LineState &State, |
136 | | ContinuationIndenter *Indenter, |
137 | 72.3k | bool DryRun) { |
138 | | // Formatting with 1 Column isn't really a column layout, so we don't need the |
139 | | // special logic here. We can just avoid bin packing any of the parameters. |
140 | 72.3k | if (Formats.size() == 1 || HasNestedBracedList72.2k ) |
141 | 461 | State.Stack.back().AvoidBinPacking = true; |
142 | 72.3k | return 0; |
143 | 72.3k | } |
144 | | |
145 | | // Returns the lengths in code points between Begin and End (both included), |
146 | | // assuming that the entire sequence is put on a single line. |
147 | | static unsigned CodePointsBetween(const FormatToken *Begin, |
148 | 7.33k | const FormatToken *End) { |
149 | 7.33k | assert(End->TotalLength >= Begin->TotalLength); |
150 | 7.33k | return End->TotalLength - Begin->TotalLength + Begin->ColumnWidth; |
151 | 7.33k | } |
152 | | |
153 | 4.02k | void CommaSeparatedList::precomputeFormattingInfos(const FormatToken *Token) { |
154 | | // FIXME: At some point we might want to do this for other lists, too. |
155 | 4.02k | if (!Token->MatchingParen || |
156 | 4.01k | !Token->isOneOf(tok::l_brace, TT_ArrayInitializerLSquare)) |
157 | 2.82k | return; |
158 | | |
159 | | // In C++11 braced list style, we should not format in columns unless they |
160 | | // have many items (20 or more) or we allow bin-packing of function call |
161 | | // arguments. |
162 | 1.20k | if (Style.Cpp11BracedListStyle && !Style.BinPackArguments1.03k && |
163 | 34 | Commas.size() < 19) |
164 | 31 | return; |
165 | | |
166 | | // Limit column layout for JavaScript array initializers to 20 or more items |
167 | | // for now to introduce it carefully. We can become more aggressive if this |
168 | | // necessary. |
169 | 1.17k | if (Token->is(TT_ArrayInitializerLSquare) && Commas.size() < 19127 ) |
170 | 123 | return; |
171 | | |
172 | | // Column format doesn't really make sense if we don't align after brackets. |
173 | 1.05k | if (Style.AlignAfterOpenBracket == FormatStyle::BAS_DontAlign) |
174 | 18 | return; |
175 | | |
176 | 1.03k | FormatToken *ItemBegin = Token->Next; |
177 | 1.07k | while (ItemBegin->isTrailingComment()) |
178 | 42 | ItemBegin = ItemBegin->Next; |
179 | 1.03k | SmallVector<bool, 8> MustBreakBeforeItem; |
180 | | |
181 | | // The lengths of an item if it is put at the end of the line. This includes |
182 | | // trailing comments which are otherwise ignored for column alignment. |
183 | 1.03k | SmallVector<unsigned, 8> EndOfLineItemLength; |
184 | | |
185 | 1.03k | bool HasSeparatingComment = false; |
186 | 4.37k | for (unsigned i = 0, e = Commas.size() + 1; i != e; ++i3.33k ) { |
187 | | // Skip comments on their own line. |
188 | 3.71k | while (ItemBegin->HasUnescapedNewline && ItemBegin->isTrailingComment()670 ) { |
189 | 43 | ItemBegin = ItemBegin->Next; |
190 | 43 | HasSeparatingComment = i > 0; |
191 | 43 | } |
192 | | |
193 | 3.66k | MustBreakBeforeItem.push_back(ItemBegin->MustBreakBefore); |
194 | 3.66k | if (ItemBegin->is(tok::l_brace)) |
195 | 231 | HasNestedBracedList = true; |
196 | 3.66k | const FormatToken *ItemEnd = nullptr; |
197 | 3.66k | if (i == Commas.size()) { |
198 | 841 | ItemEnd = Token->MatchingParen; |
199 | 841 | const FormatToken *NonCommentEnd = ItemEnd->getPreviousNonComment(); |
200 | 841 | ItemLengths.push_back(CodePointsBetween(ItemBegin, NonCommentEnd)); |
201 | 841 | if (Style.Cpp11BracedListStyle && |
202 | 719 | !ItemEnd->Previous->isTrailingComment()) { |
203 | | // In Cpp11 braced list style, the } and possibly other subsequent |
204 | | // tokens will need to stay on a line with the last element. |
205 | 1.50k | while (ItemEnd->Next && !ItemEnd->Next->CanBreakBefore985 ) |
206 | 806 | ItemEnd = ItemEnd->Next; |
207 | 138 | } else { |
208 | | // In other braced lists styles, the "}" can be wrapped to the new line. |
209 | 138 | ItemEnd = Token->MatchingParen->Previous; |
210 | 138 | } |
211 | 2.82k | } else { |
212 | 2.82k | ItemEnd = Commas[i]; |
213 | | // The comma is counted as part of the item when calculating the length. |
214 | 2.82k | ItemLengths.push_back(CodePointsBetween(ItemBegin, ItemEnd)); |
215 | | |
216 | | // Consume trailing comments so the are included in EndOfLineItemLength. |
217 | 2.82k | if (ItemEnd->Next && !ItemEnd->Next->HasUnescapedNewline && |
218 | 2.41k | ItemEnd->Next->isTrailingComment()) |
219 | 93 | ItemEnd = ItemEnd->Next; |
220 | 2.82k | } |
221 | 3.66k | EndOfLineItemLength.push_back(CodePointsBetween(ItemBegin, ItemEnd)); |
222 | | // If there is a trailing comma in the list, the next item will start at the |
223 | | // closing brace. Don't create an extra item for this. |
224 | 3.66k | if (ItemEnd->getNextNonComment() == Token->MatchingParen) |
225 | 331 | break; |
226 | 3.33k | ItemBegin = ItemEnd->Next; |
227 | 3.33k | } |
228 | | |
229 | | // Don't use column layout for lists with few elements and in presence of |
230 | | // separating comments. |
231 | 1.03k | if (Commas.size() < 5 || HasSeparatingComment100 ) |
232 | 937 | return; |
233 | | |
234 | 97 | if (Token->NestingLevel != 0 && Token->is(tok::l_brace)14 && Commas.size() < 1912 ) |
235 | 9 | return; |
236 | | |
237 | | // We can never place more than ColumnLimit / 3 items in a row (because of the |
238 | | // spaces and the comma). |
239 | 88 | unsigned MaxItems = Style.ColumnLimit / 3; |
240 | 88 | std::vector<unsigned> MinSizeInColumn; |
241 | 88 | MinSizeInColumn.reserve(MaxItems); |
242 | 1.04k | for (unsigned Columns = 1; Columns <= MaxItems; ++Columns959 ) { |
243 | 1.03k | ColumnFormat Format; |
244 | 1.03k | Format.Columns = Columns; |
245 | 1.03k | Format.ColumnSizes.resize(Columns); |
246 | 1.03k | MinSizeInColumn.assign(Columns, UINT_MAX); |
247 | 1.03k | Format.LineCount = 1; |
248 | 1.03k | bool HasRowWithSufficientColumns = false; |
249 | 1.03k | unsigned Column = 0; |
250 | 21.8k | for (unsigned i = 0, e = ItemLengths.size(); i != e; ++i20.7k ) { |
251 | 20.7k | assert(i < MustBreakBeforeItem.size()); |
252 | 20.7k | if (MustBreakBeforeItem[i] || Column == Columns20.2k ) { |
253 | 3.80k | ++Format.LineCount; |
254 | 3.80k | Column = 0; |
255 | 3.80k | } |
256 | 20.7k | if (Column == Columns - 1) |
257 | 3.68k | HasRowWithSufficientColumns = true; |
258 | 20.7k | unsigned Length = |
259 | 17.1k | (Column == Columns - 1) ? EndOfLineItemLength[i]3.68k : ItemLengths[i]; |
260 | 20.7k | Format.ColumnSizes[Column] = std::max(Format.ColumnSizes[Column], Length); |
261 | 20.7k | MinSizeInColumn[Column] = std::min(MinSizeInColumn[Column], Length); |
262 | 20.7k | ++Column; |
263 | 20.7k | } |
264 | | // If all rows are terminated early (e.g. by trailing comments), we don't |
265 | | // need to look further. |
266 | 1.03k | if (!HasRowWithSufficientColumns) |
267 | 78 | break; |
268 | 959 | Format.TotalWidth = Columns - 1; // Width of the N-1 spaces. |
269 | | |
270 | 8.76k | for (unsigned i = 0; i < Columns; ++i7.80k ) |
271 | 7.80k | Format.TotalWidth += Format.ColumnSizes[i]; |
272 | | |
273 | | // Don't use this Format, if the difference between the longest and shortest |
274 | | // element in a column exceeds a threshold to avoid excessive spaces. |
275 | 959 | if ([&] { |
276 | 7.44k | for (unsigned i = 0; i < Columns - 1; ++i6.49k ) |
277 | 6.61k | if (Format.ColumnSizes[i] - MinSizeInColumn[i] > 10) |
278 | 129 | return true; |
279 | 830 | return false; |
280 | 959 | }()) |
281 | 129 | continue; |
282 | | |
283 | | // Ignore layouts that are bound to violate the column limit. |
284 | 830 | if (Format.TotalWidth > Style.ColumnLimit && Columns > 1234 ) |
285 | 234 | continue; |
286 | | |
287 | 596 | Formats.push_back(Format); |
288 | 596 | } |
289 | 88 | } |
290 | | |
291 | | const CommaSeparatedList::ColumnFormat * |
292 | 6.15k | CommaSeparatedList::getColumnFormat(unsigned RemainingCharacters) const { |
293 | 6.15k | const ColumnFormat *BestFormat = nullptr; |
294 | 6.15k | for (SmallVector<ColumnFormat, 4>::const_reverse_iterator |
295 | 6.15k | I = Formats.rbegin(), |
296 | 6.15k | E = Formats.rend(); |
297 | 7.56k | I != E; ++I1.40k ) { |
298 | 1.83k | if (I->TotalWidth <= RemainingCharacters || I->Columns == 1485 ) { |
299 | 1.34k | if (BestFormat && I->LineCount > BestFormat->LineCount923 ) |
300 | 424 | break; |
301 | 923 | BestFormat = &*I; |
302 | 923 | } |
303 | 1.83k | } |
304 | 6.15k | return BestFormat; |
305 | 6.15k | } |
306 | | |
307 | | } // namespace format |
308 | | } // namespace clang |