]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | // Formatting top-level items - functions, structs, enums, traits, impls. |
2 | ||
3 | use std::borrow::Cow; | |
4 | use std::cmp::{max, min, Ordering}; | |
5 | ||
6 | use regex::Regex; | |
7 | use rustc_ast::visit; | |
8 | use rustc_ast::{ast, ptr}; | |
94222f64 | 9 | use rustc_span::{symbol, BytePos, Span, DUMMY_SP}; |
f20569fa XL |
10 | |
11 | use crate::attr::filter_inline_attrs; | |
12 | use crate::comment::{ | |
13 | combine_strs_with_missing_comments, contains_comment, is_last_comment_block, | |
14 | recover_comment_removed, recover_missing_comment_in_span, rewrite_missing_comment, | |
15 | FindUncommented, | |
16 | }; | |
17 | use crate::config::lists::*; | |
18 | use crate::config::{BraceStyle, Config, IndentStyle, Version}; | |
19 | use crate::expr::{ | |
cdc7bbd5 | 20 | is_empty_block, is_simple_block_stmt, rewrite_assign_rhs, rewrite_assign_rhs_with, |
a2a8927a | 21 | rewrite_assign_rhs_with_comments, RhsAssignKind, RhsTactics, |
f20569fa XL |
22 | }; |
23 | use crate::lists::{definitive_tactic, itemize_list, write_list, ListFormatting, Separator}; | |
24 | use crate::macros::{rewrite_macro, MacroPosition}; | |
25 | use crate::overflow; | |
26 | use crate::rewrite::{Rewrite, RewriteContext}; | |
27 | use crate::shape::{Indent, Shape}; | |
28 | use crate::source_map::{LineRangeUtils, SpanUtils}; | |
29 | use crate::spanned::Spanned; | |
30 | use crate::stmt::Stmt; | |
a2a8927a | 31 | use crate::types::opaque_ty; |
f20569fa XL |
32 | use crate::utils::*; |
33 | use crate::vertical::rewrite_with_alignment; | |
34 | use crate::visitor::FmtVisitor; | |
94222f64 XL |
35 | |
36 | const DEFAULT_VISIBILITY: ast::Visibility = ast::Visibility { | |
37 | kind: ast::VisibilityKind::Inherited, | |
38 | span: DUMMY_SP, | |
39 | tokens: None, | |
40 | }; | |
f20569fa XL |
41 | |
42 | fn type_annotation_separator(config: &Config) -> &str { | |
43 | colon_spaces(config) | |
44 | } | |
45 | ||
46 | // Statements of the form | |
47 | // let pat: ty = init; | |
48 | impl Rewrite for ast::Local { | |
49 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
50 | debug!( | |
51 | "Local::rewrite {:?} {} {:?}", | |
52 | self, shape.width, shape.indent | |
53 | ); | |
54 | ||
55 | skip_out_of_file_lines_range!(context, self.span); | |
56 | ||
94222f64 | 57 | if contains_skip(&self.attrs) || matches!(self.kind, ast::LocalKind::InitElse(..)) { |
f20569fa XL |
58 | return None; |
59 | } | |
60 | ||
61 | let attrs_str = self.attrs.rewrite(context, shape)?; | |
62 | let mut result = if attrs_str.is_empty() { | |
63 | "let ".to_owned() | |
64 | } else { | |
65 | combine_strs_with_missing_comments( | |
66 | context, | |
67 | &attrs_str, | |
68 | "let ", | |
69 | mk_sp( | |
70 | self.attrs.last().map(|a| a.span.hi()).unwrap(), | |
71 | self.span.lo(), | |
72 | ), | |
73 | shape, | |
74 | false, | |
75 | )? | |
76 | }; | |
77 | ||
78 | // 4 = "let ".len() | |
79 | let pat_shape = shape.offset_left(4)?; | |
80 | // 1 = ; | |
81 | let pat_shape = pat_shape.sub_width(1)?; | |
82 | let pat_str = self.pat.rewrite(context, pat_shape)?; | |
83 | result.push_str(&pat_str); | |
84 | ||
85 | // String that is placed within the assignment pattern and expression. | |
86 | let infix = { | |
87 | let mut infix = String::with_capacity(32); | |
88 | ||
89 | if let Some(ref ty) = self.ty { | |
90 | let separator = type_annotation_separator(context.config); | |
91 | let ty_shape = if pat_str.contains('\n') { | |
92 | shape.with_max_width(context.config) | |
93 | } else { | |
94 | shape | |
95 | } | |
96 | .offset_left(last_line_width(&result) + separator.len())? | |
97 | // 2 = ` =` | |
98 | .sub_width(2)?; | |
99 | ||
100 | let rewrite = ty.rewrite(context, ty_shape)?; | |
101 | ||
102 | infix.push_str(separator); | |
103 | infix.push_str(&rewrite); | |
104 | } | |
105 | ||
94222f64 | 106 | if self.kind.init().is_some() { |
f20569fa XL |
107 | infix.push_str(" ="); |
108 | } | |
109 | ||
110 | infix | |
111 | }; | |
112 | ||
113 | result.push_str(&infix); | |
114 | ||
94222f64 | 115 | if let Some((init, _els)) = self.kind.init_else_opt() { |
f20569fa XL |
116 | // 1 = trailing semicolon; |
117 | let nested_shape = shape.sub_width(1)?; | |
118 | ||
a2a8927a XL |
119 | result = rewrite_assign_rhs( |
120 | context, | |
121 | result, | |
122 | init, | |
123 | &RhsAssignKind::Expr(&init.kind, init.span), | |
124 | nested_shape, | |
125 | )?; | |
94222f64 | 126 | // todo else |
f20569fa XL |
127 | } |
128 | ||
129 | result.push(';'); | |
130 | Some(result) | |
131 | } | |
132 | } | |
133 | ||
134 | // FIXME convert to using rewrite style rather than visitor | |
135 | // FIXME format modules in this style | |
136 | #[allow(dead_code)] | |
137 | #[derive(Debug)] | |
138 | struct Item<'a> { | |
139 | unsafety: ast::Unsafe, | |
140 | abi: Cow<'static, str>, | |
141 | vis: Option<&'a ast::Visibility>, | |
142 | body: Vec<BodyElement<'a>>, | |
143 | span: Span, | |
144 | } | |
145 | ||
146 | impl<'a> Item<'a> { | |
147 | fn from_foreign_mod(fm: &'a ast::ForeignMod, span: Span, config: &Config) -> Item<'a> { | |
148 | Item { | |
149 | unsafety: fm.unsafety, | |
150 | abi: format_extern( | |
064997fb | 151 | ast::Extern::from_abi(fm.abi, DUMMY_SP), |
f20569fa XL |
152 | config.force_explicit_abi(), |
153 | true, | |
154 | ), | |
155 | vis: None, | |
156 | body: fm | |
157 | .items | |
158 | .iter() | |
159 | .map(|i| BodyElement::ForeignItem(i)) | |
160 | .collect(), | |
161 | span, | |
162 | } | |
163 | } | |
164 | } | |
165 | ||
166 | #[derive(Debug)] | |
167 | enum BodyElement<'a> { | |
168 | // Stmt(&'a ast::Stmt), | |
cdc7bbd5 | 169 | // Field(&'a ast::ExprField), |
f20569fa XL |
170 | // Variant(&'a ast::Variant), |
171 | // Item(&'a ast::Item), | |
172 | ForeignItem(&'a ast::ForeignItem), | |
173 | } | |
174 | ||
175 | /// Represents a fn's signature. | |
176 | pub(crate) struct FnSig<'a> { | |
177 | decl: &'a ast::FnDecl, | |
178 | generics: &'a ast::Generics, | |
179 | ext: ast::Extern, | |
180 | is_async: Cow<'a, ast::Async>, | |
181 | constness: ast::Const, | |
182 | defaultness: ast::Defaultness, | |
183 | unsafety: ast::Unsafe, | |
3c0e092e | 184 | visibility: &'a ast::Visibility, |
f20569fa XL |
185 | } |
186 | ||
187 | impl<'a> FnSig<'a> { | |
188 | pub(crate) fn from_method_sig( | |
189 | method_sig: &'a ast::FnSig, | |
190 | generics: &'a ast::Generics, | |
3c0e092e | 191 | visibility: &'a ast::Visibility, |
f20569fa XL |
192 | ) -> FnSig<'a> { |
193 | FnSig { | |
194 | unsafety: method_sig.header.unsafety, | |
195 | is_async: Cow::Borrowed(&method_sig.header.asyncness), | |
196 | constness: method_sig.header.constness, | |
197 | defaultness: ast::Defaultness::Final, | |
198 | ext: method_sig.header.ext, | |
199 | decl: &*method_sig.decl, | |
200 | generics, | |
201 | visibility, | |
202 | } | |
203 | } | |
204 | ||
205 | pub(crate) fn from_fn_kind( | |
206 | fn_kind: &'a visit::FnKind<'_>, | |
f20569fa XL |
207 | decl: &'a ast::FnDecl, |
208 | defaultness: ast::Defaultness, | |
209 | ) -> FnSig<'a> { | |
210 | match *fn_kind { | |
04454e1e | 211 | visit::FnKind::Fn(fn_ctxt, _, fn_sig, vis, generics, _) => match fn_ctxt { |
f20569fa | 212 | visit::FnCtxt::Assoc(..) => { |
3c0e092e | 213 | let mut fn_sig = FnSig::from_method_sig(fn_sig, generics, vis); |
f20569fa XL |
214 | fn_sig.defaultness = defaultness; |
215 | fn_sig | |
216 | } | |
217 | _ => FnSig { | |
218 | decl, | |
219 | generics, | |
220 | ext: fn_sig.header.ext, | |
221 | constness: fn_sig.header.constness, | |
222 | is_async: Cow::Borrowed(&fn_sig.header.asyncness), | |
223 | defaultness, | |
224 | unsafety: fn_sig.header.unsafety, | |
3c0e092e | 225 | visibility: vis, |
f20569fa XL |
226 | }, |
227 | }, | |
228 | _ => unreachable!(), | |
229 | } | |
230 | } | |
231 | ||
232 | fn to_str(&self, context: &RewriteContext<'_>) -> String { | |
233 | let mut result = String::with_capacity(128); | |
234 | // Vis defaultness constness unsafety abi. | |
3c0e092e | 235 | result.push_str(&*format_visibility(context, self.visibility)); |
f20569fa XL |
236 | result.push_str(format_defaultness(self.defaultness)); |
237 | result.push_str(format_constness(self.constness)); | |
238 | result.push_str(format_async(&self.is_async)); | |
239 | result.push_str(format_unsafety(self.unsafety)); | |
240 | result.push_str(&format_extern( | |
241 | self.ext, | |
242 | context.config.force_explicit_abi(), | |
243 | false, | |
244 | )); | |
245 | result | |
246 | } | |
247 | } | |
248 | ||
249 | impl<'a> FmtVisitor<'a> { | |
250 | fn format_item(&mut self, item: &Item<'_>) { | |
251 | self.buffer.push_str(format_unsafety(item.unsafety)); | |
252 | self.buffer.push_str(&item.abi); | |
253 | ||
254 | let snippet = self.snippet(item.span); | |
255 | let brace_pos = snippet.find_uncommented("{").unwrap(); | |
256 | ||
257 | self.push_str("{"); | |
258 | if !item.body.is_empty() || contains_comment(&snippet[brace_pos..]) { | |
259 | // FIXME: this skips comments between the extern keyword and the opening | |
260 | // brace. | |
261 | self.last_pos = item.span.lo() + BytePos(brace_pos as u32 + 1); | |
262 | self.block_indent = self.block_indent.block_indent(self.config); | |
263 | ||
264 | if !item.body.is_empty() { | |
265 | for item in &item.body { | |
266 | self.format_body_element(item); | |
267 | } | |
268 | } | |
269 | ||
270 | self.format_missing_no_indent(item.span.hi() - BytePos(1)); | |
271 | self.block_indent = self.block_indent.block_unindent(self.config); | |
272 | let indent_str = self.block_indent.to_string(self.config); | |
273 | self.push_str(&indent_str); | |
274 | } | |
275 | ||
276 | self.push_str("}"); | |
277 | self.last_pos = item.span.hi(); | |
278 | } | |
279 | ||
280 | fn format_body_element(&mut self, element: &BodyElement<'_>) { | |
281 | match *element { | |
282 | BodyElement::ForeignItem(item) => self.format_foreign_item(item), | |
283 | } | |
284 | } | |
285 | ||
286 | pub(crate) fn format_foreign_mod(&mut self, fm: &ast::ForeignMod, span: Span) { | |
287 | let item = Item::from_foreign_mod(fm, span, self.config); | |
288 | self.format_item(&item); | |
289 | } | |
290 | ||
291 | fn format_foreign_item(&mut self, item: &ast::ForeignItem) { | |
292 | let rewrite = item.rewrite(&self.get_context(), self.shape()); | |
293 | let hi = item.span.hi(); | |
294 | let span = if item.attrs.is_empty() { | |
295 | item.span | |
296 | } else { | |
297 | mk_sp(item.attrs[0].span.lo(), hi) | |
298 | }; | |
299 | self.push_rewrite(span, rewrite); | |
300 | self.last_pos = hi; | |
301 | } | |
302 | ||
303 | pub(crate) fn rewrite_fn_before_block( | |
304 | &mut self, | |
305 | indent: Indent, | |
306 | ident: symbol::Ident, | |
307 | fn_sig: &FnSig<'_>, | |
308 | span: Span, | |
309 | ) -> Option<(String, FnBraceStyle)> { | |
310 | let context = self.get_context(); | |
311 | ||
312 | let mut fn_brace_style = newline_for_brace(self.config, &fn_sig.generics.where_clause); | |
313 | let (result, _, force_newline_brace) = | |
314 | rewrite_fn_base(&context, indent, ident, fn_sig, span, fn_brace_style)?; | |
315 | ||
316 | // 2 = ` {` | |
317 | if self.config.brace_style() == BraceStyle::AlwaysNextLine | |
318 | || force_newline_brace | |
319 | || last_line_width(&result) + 2 > self.shape().width | |
320 | { | |
321 | fn_brace_style = FnBraceStyle::NextLine | |
322 | } | |
323 | ||
324 | Some((result, fn_brace_style)) | |
325 | } | |
326 | ||
327 | pub(crate) fn rewrite_required_fn( | |
328 | &mut self, | |
329 | indent: Indent, | |
330 | ident: symbol::Ident, | |
331 | sig: &ast::FnSig, | |
3c0e092e | 332 | vis: &ast::Visibility, |
f20569fa XL |
333 | generics: &ast::Generics, |
334 | span: Span, | |
335 | ) -> Option<String> { | |
336 | // Drop semicolon or it will be interpreted as comment. | |
337 | let span = mk_sp(span.lo(), span.hi() - BytePos(1)); | |
338 | let context = self.get_context(); | |
339 | ||
340 | let (mut result, ends_with_comment, _) = rewrite_fn_base( | |
341 | &context, | |
342 | indent, | |
343 | ident, | |
3c0e092e | 344 | &FnSig::from_method_sig(sig, generics, vis), |
f20569fa XL |
345 | span, |
346 | FnBraceStyle::None, | |
347 | )?; | |
348 | ||
349 | // If `result` ends with a comment, then remember to add a newline | |
350 | if ends_with_comment { | |
351 | result.push_str(&indent.to_string_with_newline(context.config)); | |
352 | } | |
353 | ||
354 | // Re-attach semicolon | |
355 | result.push(';'); | |
356 | ||
357 | Some(result) | |
358 | } | |
359 | ||
360 | pub(crate) fn single_line_fn( | |
361 | &self, | |
362 | fn_str: &str, | |
363 | block: &ast::Block, | |
364 | inner_attrs: Option<&[ast::Attribute]>, | |
365 | ) -> Option<String> { | |
366 | if fn_str.contains('\n') || inner_attrs.map_or(false, |a| !a.is_empty()) { | |
367 | return None; | |
368 | } | |
369 | ||
370 | let context = self.get_context(); | |
371 | ||
372 | if self.config.empty_item_single_line() | |
373 | && is_empty_block(&context, block, None) | |
374 | && self.block_indent.width() + fn_str.len() + 3 <= self.config.max_width() | |
375 | && !last_line_contains_single_line_comment(fn_str) | |
376 | { | |
377 | return Some(format!("{} {{}}", fn_str)); | |
378 | } | |
379 | ||
380 | if !self.config.fn_single_line() || !is_simple_block_stmt(&context, block, None) { | |
381 | return None; | |
382 | } | |
383 | ||
384 | let res = Stmt::from_ast_node(block.stmts.first()?, true) | |
385 | .rewrite(&self.get_context(), self.shape())?; | |
386 | ||
387 | let width = self.block_indent.width() + fn_str.len() + res.len() + 5; | |
388 | if !res.contains('\n') && width <= self.config.max_width() { | |
389 | Some(format!("{} {{ {} }}", fn_str, res)) | |
390 | } else { | |
391 | None | |
392 | } | |
393 | } | |
394 | ||
395 | pub(crate) fn visit_static(&mut self, static_parts: &StaticParts<'_>) { | |
396 | let rewrite = rewrite_static(&self.get_context(), static_parts, self.block_indent); | |
397 | self.push_rewrite(static_parts.span, rewrite); | |
398 | } | |
399 | ||
400 | pub(crate) fn visit_struct(&mut self, struct_parts: &StructParts<'_>) { | |
401 | let is_tuple = match struct_parts.def { | |
402 | ast::VariantData::Tuple(..) => true, | |
403 | _ => false, | |
404 | }; | |
405 | let rewrite = format_struct(&self.get_context(), struct_parts, self.block_indent, None) | |
406 | .map(|s| if is_tuple { s + ";" } else { s }); | |
407 | self.push_rewrite(struct_parts.span, rewrite); | |
408 | } | |
409 | ||
410 | pub(crate) fn visit_enum( | |
411 | &mut self, | |
412 | ident: symbol::Ident, | |
413 | vis: &ast::Visibility, | |
414 | enum_def: &ast::EnumDef, | |
415 | generics: &ast::Generics, | |
416 | span: Span, | |
417 | ) { | |
418 | let enum_header = | |
419 | format_header(&self.get_context(), "enum ", ident, vis, self.block_indent); | |
420 | self.push_str(&enum_header); | |
421 | ||
422 | let enum_snippet = self.snippet(span); | |
423 | let brace_pos = enum_snippet.find_uncommented("{").unwrap(); | |
424 | let body_start = span.lo() + BytePos(brace_pos as u32 + 1); | |
425 | let generics_str = format_generics( | |
426 | &self.get_context(), | |
427 | generics, | |
428 | self.config.brace_style(), | |
429 | if enum_def.variants.is_empty() { | |
430 | BracePos::ForceSameLine | |
431 | } else { | |
432 | BracePos::Auto | |
433 | }, | |
434 | self.block_indent, | |
435 | // make a span that starts right after `enum Foo` | |
436 | mk_sp(ident.span.hi(), body_start), | |
437 | last_line_width(&enum_header), | |
438 | ) | |
439 | .unwrap(); | |
440 | self.push_str(&generics_str); | |
441 | ||
442 | self.last_pos = body_start; | |
443 | ||
444 | match self.format_variant_list(enum_def, body_start, span.hi()) { | |
445 | Some(ref s) if enum_def.variants.is_empty() => self.push_str(s), | |
446 | rw => { | |
447 | self.push_rewrite(mk_sp(body_start, span.hi()), rw); | |
448 | self.block_indent = self.block_indent.block_unindent(self.config); | |
449 | } | |
450 | } | |
451 | } | |
452 | ||
453 | // Format the body of an enum definition | |
454 | fn format_variant_list( | |
455 | &mut self, | |
456 | enum_def: &ast::EnumDef, | |
457 | body_lo: BytePos, | |
458 | body_hi: BytePos, | |
459 | ) -> Option<String> { | |
460 | if enum_def.variants.is_empty() { | |
461 | let mut buffer = String::with_capacity(128); | |
462 | // 1 = "}" | |
463 | let span = mk_sp(body_lo, body_hi - BytePos(1)); | |
464 | format_empty_struct_or_tuple( | |
465 | &self.get_context(), | |
466 | span, | |
467 | self.block_indent, | |
468 | &mut buffer, | |
469 | "", | |
470 | "}", | |
471 | ); | |
472 | return Some(buffer); | |
473 | } | |
474 | let mut result = String::with_capacity(1024); | |
475 | let original_offset = self.block_indent; | |
476 | self.block_indent = self.block_indent.block_indent(self.config); | |
477 | ||
478 | // If enum variants have discriminants, try to vertically align those, | |
479 | // provided the discrims are not shifted too much to the right | |
480 | let align_threshold: usize = self.config.enum_discrim_align_threshold(); | |
481 | let discr_ident_lens: Vec<usize> = enum_def | |
482 | .variants | |
483 | .iter() | |
484 | .filter(|var| var.disr_expr.is_some()) | |
485 | .map(|var| rewrite_ident(&self.get_context(), var.ident).len()) | |
486 | .collect(); | |
487 | // cut the list at the point of longest discrim shorter than the threshold | |
488 | // All of the discrims under the threshold will get padded, and all above - left as is. | |
489 | let pad_discrim_ident_to = *discr_ident_lens | |
490 | .iter() | |
491 | .filter(|&l| *l <= align_threshold) | |
492 | .max() | |
493 | .unwrap_or(&0); | |
494 | ||
495 | let itemize_list_with = |one_line_width: usize| { | |
496 | itemize_list( | |
497 | self.snippet_provider, | |
498 | enum_def.variants.iter(), | |
499 | "}", | |
500 | ",", | |
501 | |f| { | |
502 | if !f.attrs.is_empty() { | |
503 | f.attrs[0].span.lo() | |
504 | } else { | |
505 | f.span.lo() | |
506 | } | |
507 | }, | |
508 | |f| f.span.hi(), | |
509 | |f| self.format_variant(f, one_line_width, pad_discrim_ident_to), | |
510 | body_lo, | |
511 | body_hi, | |
512 | false, | |
513 | ) | |
514 | .collect() | |
515 | }; | |
cdc7bbd5 XL |
516 | let mut items: Vec<_> = itemize_list_with(self.config.struct_variant_width()); |
517 | ||
f20569fa XL |
518 | // If one of the variants use multiple lines, use multi-lined formatting for all variants. |
519 | let has_multiline_variant = items.iter().any(|item| item.inner_as_ref().contains('\n')); | |
520 | let has_single_line_variant = items.iter().any(|item| !item.inner_as_ref().contains('\n')); | |
521 | if has_multiline_variant && has_single_line_variant { | |
522 | items = itemize_list_with(0); | |
523 | } | |
524 | ||
525 | let shape = self.shape().sub_width(2)?; | |
526 | let fmt = ListFormatting::new(shape, self.config) | |
527 | .trailing_separator(self.config.trailing_comma()) | |
528 | .preserve_newline(true); | |
529 | ||
530 | let list = write_list(&items, &fmt)?; | |
531 | result.push_str(&list); | |
532 | result.push_str(&original_offset.to_string_with_newline(self.config)); | |
533 | result.push('}'); | |
534 | Some(result) | |
535 | } | |
536 | ||
537 | // Variant of an enum. | |
538 | fn format_variant( | |
539 | &self, | |
540 | field: &ast::Variant, | |
541 | one_line_width: usize, | |
542 | pad_discrim_ident_to: usize, | |
543 | ) -> Option<String> { | |
544 | if contains_skip(&field.attrs) { | |
545 | let lo = field.attrs[0].span.lo(); | |
546 | let span = mk_sp(lo, field.span.hi()); | |
547 | return Some(self.snippet(span).to_owned()); | |
548 | } | |
549 | ||
550 | let context = self.get_context(); | |
551 | // 1 = ',' | |
552 | let shape = self.shape().sub_width(1)?; | |
553 | let attrs_str = field.attrs.rewrite(&context, shape)?; | |
554 | let lo = field | |
555 | .attrs | |
556 | .last() | |
557 | .map_or(field.span.lo(), |attr| attr.span.hi()); | |
558 | let span = mk_sp(lo, field.span.lo()); | |
559 | ||
560 | let variant_body = match field.data { | |
561 | ast::VariantData::Tuple(..) | ast::VariantData::Struct(..) => format_struct( | |
562 | &context, | |
563 | &StructParts::from_variant(field), | |
564 | self.block_indent, | |
565 | Some(one_line_width), | |
566 | )?, | |
567 | ast::VariantData::Unit(..) => rewrite_ident(&context, field.ident).to_owned(), | |
568 | }; | |
569 | ||
570 | let variant_body = if let Some(ref expr) = field.disr_expr { | |
571 | let lhs = format!("{:1$} =", variant_body, pad_discrim_ident_to); | |
a2a8927a | 572 | let ex = &*expr.value; |
f20569fa XL |
573 | rewrite_assign_rhs_with( |
574 | &context, | |
575 | lhs, | |
a2a8927a | 576 | ex, |
f20569fa | 577 | shape, |
a2a8927a | 578 | &RhsAssignKind::Expr(&ex.kind, ex.span), |
f20569fa XL |
579 | RhsTactics::AllowOverflow, |
580 | )? | |
581 | } else { | |
582 | variant_body | |
583 | }; | |
584 | ||
585 | combine_strs_with_missing_comments(&context, &attrs_str, &variant_body, span, shape, false) | |
586 | } | |
587 | ||
588 | fn visit_impl_items(&mut self, items: &[ptr::P<ast::AssocItem>]) { | |
589 | if self.get_context().config.reorder_impl_items() { | |
a2a8927a XL |
590 | type TyOpt = Option<ptr::P<ast::Ty>>; |
591 | use crate::ast::AssocItemKind::*; | |
592 | let is_type = |ty: &TyOpt| opaque_ty(ty).is_none(); | |
593 | let is_opaque = |ty: &TyOpt| opaque_ty(ty).is_some(); | |
594 | let both_type = |l: &TyOpt, r: &TyOpt| is_type(l) && is_type(r); | |
595 | let both_opaque = |l: &TyOpt, r: &TyOpt| is_opaque(l) && is_opaque(r); | |
596 | let need_empty_line = |a: &ast::AssocItemKind, b: &ast::AssocItemKind| match (a, b) { | |
597 | (TyAlias(lty), TyAlias(rty)) | |
598 | if both_type(<y.ty, &rty.ty) || both_opaque(<y.ty, &rty.ty) => | |
599 | { | |
600 | false | |
601 | } | |
602 | (Const(..), Const(..)) => false, | |
603 | _ => true, | |
604 | }; | |
605 | ||
f20569fa XL |
606 | // Create visitor for each items, then reorder them. |
607 | let mut buffer = vec![]; | |
608 | for item in items { | |
609 | self.visit_impl_item(item); | |
610 | buffer.push((self.buffer.clone(), item.clone())); | |
611 | self.buffer.clear(); | |
612 | } | |
613 | ||
f20569fa XL |
614 | buffer.sort_by(|(_, a), (_, b)| match (&a.kind, &b.kind) { |
615 | (TyAlias(lty), TyAlias(rty)) | |
3c0e092e | 616 | if both_type(<y.ty, &rty.ty) || both_opaque(<y.ty, &rty.ty) => |
f20569fa | 617 | { |
a2a8927a | 618 | a.ident.as_str().cmp(b.ident.as_str()) |
f20569fa XL |
619 | } |
620 | (Const(..), Const(..)) | (MacCall(..), MacCall(..)) => { | |
a2a8927a | 621 | a.ident.as_str().cmp(b.ident.as_str()) |
f20569fa XL |
622 | } |
623 | (Fn(..), Fn(..)) => a.span.lo().cmp(&b.span.lo()), | |
3c0e092e XL |
624 | (TyAlias(ty), _) if is_type(&ty.ty) => Ordering::Less, |
625 | (_, TyAlias(ty)) if is_type(&ty.ty) => Ordering::Greater, | |
f20569fa XL |
626 | (TyAlias(..), _) => Ordering::Less, |
627 | (_, TyAlias(..)) => Ordering::Greater, | |
628 | (Const(..), _) => Ordering::Less, | |
629 | (_, Const(..)) => Ordering::Greater, | |
630 | (MacCall(..), _) => Ordering::Less, | |
631 | (_, MacCall(..)) => Ordering::Greater, | |
632 | }); | |
633 | let mut prev_kind = None; | |
634 | for (buf, item) in buffer { | |
635 | // Make sure that there are at least a single empty line between | |
636 | // different impl items. | |
637 | if prev_kind | |
638 | .as_ref() | |
639 | .map_or(false, |prev_kind| need_empty_line(prev_kind, &item.kind)) | |
640 | { | |
641 | self.push_str("\n"); | |
642 | } | |
643 | let indent_str = self.block_indent.to_string_with_newline(self.config); | |
644 | self.push_str(&indent_str); | |
645 | self.push_str(buf.trim()); | |
646 | prev_kind = Some(item.kind.clone()); | |
647 | } | |
648 | } else { | |
649 | for item in items { | |
650 | self.visit_impl_item(item); | |
651 | } | |
652 | } | |
653 | } | |
654 | } | |
655 | ||
656 | pub(crate) fn format_impl( | |
657 | context: &RewriteContext<'_>, | |
658 | item: &ast::Item, | |
a2a8927a | 659 | iimpl: &ast::Impl, |
f20569fa XL |
660 | offset: Indent, |
661 | ) -> Option<String> { | |
a2a8927a XL |
662 | let ast::Impl { |
663 | generics, | |
664 | self_ty, | |
665 | items, | |
666 | .. | |
667 | } = iimpl; | |
668 | let mut result = String::with_capacity(128); | |
669 | let ref_and_type = format_impl_ref_and_type(context, item, iimpl, offset)?; | |
670 | let sep = offset.to_string_with_newline(context.config); | |
671 | result.push_str(&ref_and_type); | |
f20569fa | 672 | |
a2a8927a XL |
673 | let where_budget = if result.contains('\n') { |
674 | context.config.max_width() | |
675 | } else { | |
676 | context.budget(last_line_width(&result)) | |
677 | }; | |
f20569fa | 678 | |
a2a8927a XL |
679 | let mut option = WhereClauseOption::snuggled(&ref_and_type); |
680 | let snippet = context.snippet(item.span); | |
681 | let open_pos = snippet.find_uncommented("{")? + 1; | |
682 | if !contains_comment(&snippet[open_pos..]) | |
683 | && items.is_empty() | |
684 | && generics.where_clause.predicates.len() == 1 | |
685 | && !result.contains('\n') | |
686 | { | |
687 | option.suppress_comma(); | |
688 | option.snuggle(); | |
689 | option.allow_single_line(); | |
690 | } | |
f20569fa | 691 | |
a2a8927a XL |
692 | let missing_span = mk_sp(self_ty.span.hi(), item.span.hi()); |
693 | let where_span_end = context.snippet_provider.opt_span_before(missing_span, "{"); | |
694 | let where_clause_str = rewrite_where_clause( | |
695 | context, | |
5e7ed085 FG |
696 | &generics.where_clause.predicates, |
697 | generics.where_clause.span, | |
a2a8927a XL |
698 | context.config.brace_style(), |
699 | Shape::legacy(where_budget, offset.block_only()), | |
700 | false, | |
701 | "{", | |
702 | where_span_end, | |
703 | self_ty.span.hi(), | |
704 | option, | |
705 | )?; | |
f20569fa | 706 | |
a2a8927a XL |
707 | // If there is no where-clause, we may have missing comments between the trait name and |
708 | // the opening brace. | |
709 | if generics.where_clause.predicates.is_empty() { | |
710 | if let Some(hi) = where_span_end { | |
711 | match recover_missing_comment_in_span( | |
712 | mk_sp(self_ty.span.hi(), hi), | |
713 | Shape::indented(offset, context.config), | |
714 | context, | |
715 | last_line_width(&result), | |
716 | ) { | |
717 | Some(ref missing_comment) if !missing_comment.is_empty() => { | |
718 | result.push_str(missing_comment); | |
f20569fa | 719 | } |
a2a8927a | 720 | _ => (), |
f20569fa XL |
721 | } |
722 | } | |
a2a8927a | 723 | } |
f20569fa | 724 | |
a2a8927a XL |
725 | if is_impl_single_line(context, items.as_slice(), &result, &where_clause_str, item)? { |
726 | result.push_str(&where_clause_str); | |
727 | if where_clause_str.contains('\n') || last_line_contains_single_line_comment(&result) { | |
728 | // if the where_clause contains extra comments AND | |
729 | // there is only one where-clause predicate | |
730 | // recover the suppressed comma in single line where_clause formatting | |
731 | if generics.where_clause.predicates.len() == 1 { | |
732 | result.push(','); | |
f20569fa | 733 | } |
a2a8927a XL |
734 | result.push_str(&format!("{}{{{}}}", sep, sep)); |
735 | } else { | |
736 | result.push_str(" {}"); | |
f20569fa | 737 | } |
a2a8927a XL |
738 | return Some(result); |
739 | } | |
f20569fa | 740 | |
a2a8927a | 741 | result.push_str(&where_clause_str); |
f20569fa | 742 | |
a2a8927a XL |
743 | let need_newline = last_line_contains_single_line_comment(&result) || result.contains('\n'); |
744 | match context.config.brace_style() { | |
745 | _ if need_newline => result.push_str(&sep), | |
746 | BraceStyle::AlwaysNextLine => result.push_str(&sep), | |
747 | BraceStyle::PreferSameLine => result.push(' '), | |
748 | BraceStyle::SameLineWhere => { | |
749 | if !where_clause_str.is_empty() { | |
750 | result.push_str(&sep); | |
751 | } else { | |
752 | result.push(' '); | |
f20569fa XL |
753 | } |
754 | } | |
a2a8927a | 755 | } |
f20569fa | 756 | |
a2a8927a XL |
757 | result.push('{'); |
758 | // this is an impl body snippet(impl SampleImpl { /* here */ }) | |
759 | let lo = max(self_ty.span.hi(), generics.where_clause.span.hi()); | |
760 | let snippet = context.snippet(mk_sp(lo, item.span.hi())); | |
761 | let open_pos = snippet.find_uncommented("{")? + 1; | |
f20569fa | 762 | |
a2a8927a XL |
763 | if !items.is_empty() || contains_comment(&snippet[open_pos..]) { |
764 | let mut visitor = FmtVisitor::from_context(context); | |
765 | let item_indent = offset.block_only().block_indent(context.config); | |
766 | visitor.block_indent = item_indent; | |
767 | visitor.last_pos = lo + BytePos(open_pos as u32); | |
f20569fa | 768 | |
a2a8927a XL |
769 | visitor.visit_attrs(&item.attrs, ast::AttrStyle::Inner); |
770 | visitor.visit_impl_items(items); | |
f20569fa | 771 | |
a2a8927a | 772 | visitor.format_missing(item.span.hi() - BytePos(1)); |
f20569fa | 773 | |
a2a8927a XL |
774 | let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config); |
775 | let outer_indent_str = offset.block_only().to_string_with_newline(context.config); | |
f20569fa | 776 | |
a2a8927a XL |
777 | result.push_str(&inner_indent_str); |
778 | result.push_str(visitor.buffer.trim()); | |
779 | result.push_str(&outer_indent_str); | |
780 | } else if need_newline || !context.config.empty_item_single_line() { | |
781 | result.push_str(&sep); | |
782 | } | |
f20569fa | 783 | |
a2a8927a | 784 | result.push('}'); |
f20569fa | 785 | |
a2a8927a | 786 | Some(result) |
f20569fa XL |
787 | } |
788 | ||
789 | fn is_impl_single_line( | |
790 | context: &RewriteContext<'_>, | |
791 | items: &[ptr::P<ast::AssocItem>], | |
792 | result: &str, | |
793 | where_clause_str: &str, | |
794 | item: &ast::Item, | |
795 | ) -> Option<bool> { | |
796 | let snippet = context.snippet(item.span); | |
797 | let open_pos = snippet.find_uncommented("{")? + 1; | |
798 | ||
799 | Some( | |
800 | context.config.empty_item_single_line() | |
801 | && items.is_empty() | |
802 | && !result.contains('\n') | |
803 | && result.len() + where_clause_str.len() <= context.config.max_width() | |
804 | && !contains_comment(&snippet[open_pos..]), | |
805 | ) | |
806 | } | |
807 | ||
808 | fn format_impl_ref_and_type( | |
809 | context: &RewriteContext<'_>, | |
810 | item: &ast::Item, | |
a2a8927a | 811 | iimpl: &ast::Impl, |
f20569fa XL |
812 | offset: Indent, |
813 | ) -> Option<String> { | |
a2a8927a XL |
814 | let ast::Impl { |
815 | unsafety, | |
816 | polarity, | |
817 | defaultness, | |
818 | constness, | |
819 | ref generics, | |
820 | of_trait: ref trait_ref, | |
821 | ref self_ty, | |
822 | .. | |
823 | } = *iimpl; | |
824 | let mut result = String::with_capacity(128); | |
f20569fa | 825 | |
a2a8927a XL |
826 | result.push_str(&format_visibility(context, &item.vis)); |
827 | result.push_str(format_defaultness(defaultness)); | |
828 | result.push_str(format_unsafety(unsafety)); | |
f20569fa | 829 | |
a2a8927a XL |
830 | let shape = if context.config.version() == Version::Two { |
831 | Shape::indented(offset + last_line_width(&result), context.config) | |
832 | } else { | |
833 | generics_shape_from_config( | |
834 | context.config, | |
835 | Shape::indented(offset + last_line_width(&result), context.config), | |
836 | 0, | |
837 | )? | |
838 | }; | |
839 | let generics_str = rewrite_generics(context, "impl", generics, shape)?; | |
840 | result.push_str(&generics_str); | |
841 | result.push_str(format_constness_right(constness)); | |
f20569fa | 842 | |
a2a8927a XL |
843 | let polarity_str = match polarity { |
844 | ast::ImplPolarity::Negative(_) => "!", | |
845 | ast::ImplPolarity::Positive => "", | |
846 | }; | |
f20569fa | 847 | |
a2a8927a XL |
848 | let polarity_overhead; |
849 | let trait_ref_overhead; | |
850 | if let Some(ref trait_ref) = *trait_ref { | |
851 | let result_len = last_line_width(&result); | |
852 | result.push_str(&rewrite_trait_ref( | |
853 | context, | |
854 | trait_ref, | |
855 | offset, | |
856 | polarity_str, | |
857 | result_len, | |
858 | )?); | |
859 | polarity_overhead = 0; // already written | |
860 | trait_ref_overhead = " for".len(); | |
861 | } else { | |
862 | polarity_overhead = polarity_str.len(); | |
863 | trait_ref_overhead = 0; | |
864 | } | |
f20569fa | 865 | |
a2a8927a XL |
866 | // Try to put the self type in a single line. |
867 | let curly_brace_overhead = if generics.where_clause.predicates.is_empty() { | |
868 | // If there is no where-clause adapt budget for type formatting to take space and curly | |
869 | // brace into account. | |
870 | match context.config.brace_style() { | |
871 | BraceStyle::AlwaysNextLine => 0, | |
872 | _ => 2, | |
873 | } | |
874 | } else { | |
875 | 0 | |
876 | }; | |
877 | let used_space = | |
878 | last_line_width(&result) + polarity_overhead + trait_ref_overhead + curly_brace_overhead; | |
879 | // 1 = space before the type. | |
880 | let budget = context.budget(used_space + 1); | |
881 | if let Some(self_ty_str) = self_ty.rewrite(context, Shape::legacy(budget, offset)) { | |
882 | if !self_ty_str.contains('\n') { | |
883 | if trait_ref.is_some() { | |
884 | result.push_str(" for "); | |
885 | } else { | |
886 | result.push(' '); | |
887 | result.push_str(polarity_str); | |
f20569fa | 888 | } |
a2a8927a XL |
889 | result.push_str(&self_ty_str); |
890 | return Some(result); | |
f20569fa | 891 | } |
a2a8927a | 892 | } |
f20569fa | 893 | |
a2a8927a XL |
894 | // Couldn't fit the self type on a single line, put it on a new line. |
895 | result.push('\n'); | |
896 | // Add indentation of one additional tab. | |
897 | let new_line_offset = offset.block_indent(context.config); | |
898 | result.push_str(&new_line_offset.to_string(context.config)); | |
899 | if trait_ref.is_some() { | |
900 | result.push_str("for "); | |
f20569fa | 901 | } else { |
a2a8927a | 902 | result.push_str(polarity_str); |
f20569fa | 903 | } |
a2a8927a XL |
904 | let budget = context.budget(last_line_width(&result) + polarity_overhead); |
905 | let type_offset = match context.config.indent_style() { | |
906 | IndentStyle::Visual => new_line_offset + trait_ref_overhead, | |
907 | IndentStyle::Block => new_line_offset, | |
908 | }; | |
909 | result.push_str(&*self_ty.rewrite(context, Shape::legacy(budget, type_offset))?); | |
910 | Some(result) | |
f20569fa XL |
911 | } |
912 | ||
913 | fn rewrite_trait_ref( | |
914 | context: &RewriteContext<'_>, | |
915 | trait_ref: &ast::TraitRef, | |
916 | offset: Indent, | |
917 | polarity_str: &str, | |
918 | result_len: usize, | |
919 | ) -> Option<String> { | |
920 | // 1 = space between generics and trait_ref | |
921 | let used_space = 1 + polarity_str.len() + result_len; | |
922 | let shape = Shape::indented(offset + used_space, context.config); | |
923 | if let Some(trait_ref_str) = trait_ref.rewrite(context, shape) { | |
924 | if !trait_ref_str.contains('\n') { | |
925 | return Some(format!(" {}{}", polarity_str, trait_ref_str)); | |
926 | } | |
927 | } | |
928 | // We could not make enough space for trait_ref, so put it on new line. | |
929 | let offset = offset.block_indent(context.config); | |
930 | let shape = Shape::indented(offset, context.config); | |
931 | let trait_ref_str = trait_ref.rewrite(context, shape)?; | |
932 | Some(format!( | |
933 | "{}{}{}", | |
934 | offset.to_string_with_newline(context.config), | |
935 | polarity_str, | |
936 | trait_ref_str | |
937 | )) | |
938 | } | |
939 | ||
940 | pub(crate) struct StructParts<'a> { | |
941 | prefix: &'a str, | |
942 | ident: symbol::Ident, | |
943 | vis: &'a ast::Visibility, | |
944 | def: &'a ast::VariantData, | |
945 | generics: Option<&'a ast::Generics>, | |
946 | span: Span, | |
947 | } | |
948 | ||
949 | impl<'a> StructParts<'a> { | |
950 | fn format_header(&self, context: &RewriteContext<'_>, offset: Indent) -> String { | |
951 | format_header(context, self.prefix, self.ident, self.vis, offset) | |
952 | } | |
953 | ||
94222f64 | 954 | fn from_variant(variant: &'a ast::Variant) -> Self { |
f20569fa XL |
955 | StructParts { |
956 | prefix: "", | |
957 | ident: variant.ident, | |
958 | vis: &DEFAULT_VISIBILITY, | |
959 | def: &variant.data, | |
960 | generics: None, | |
961 | span: variant.span, | |
962 | } | |
963 | } | |
964 | ||
965 | pub(crate) fn from_item(item: &'a ast::Item) -> Self { | |
966 | let (prefix, def, generics) = match item.kind { | |
967 | ast::ItemKind::Struct(ref def, ref generics) => ("struct ", def, generics), | |
968 | ast::ItemKind::Union(ref def, ref generics) => ("union ", def, generics), | |
969 | _ => unreachable!(), | |
970 | }; | |
971 | StructParts { | |
972 | prefix, | |
973 | ident: item.ident, | |
974 | vis: &item.vis, | |
975 | def, | |
976 | generics: Some(generics), | |
977 | span: item.span, | |
978 | } | |
979 | } | |
980 | } | |
981 | ||
982 | fn format_struct( | |
983 | context: &RewriteContext<'_>, | |
984 | struct_parts: &StructParts<'_>, | |
985 | offset: Indent, | |
986 | one_line_width: Option<usize>, | |
987 | ) -> Option<String> { | |
988 | match *struct_parts.def { | |
989 | ast::VariantData::Unit(..) => format_unit_struct(context, struct_parts, offset), | |
990 | ast::VariantData::Tuple(ref fields, _) => { | |
991 | format_tuple_struct(context, struct_parts, fields, offset) | |
992 | } | |
993 | ast::VariantData::Struct(ref fields, _) => { | |
994 | format_struct_struct(context, struct_parts, fields, offset, one_line_width) | |
995 | } | |
996 | } | |
997 | } | |
998 | ||
999 | pub(crate) fn format_trait( | |
1000 | context: &RewriteContext<'_>, | |
1001 | item: &ast::Item, | |
1002 | offset: Indent, | |
1003 | ) -> Option<String> { | |
1004 | if let ast::ItemKind::Trait(trait_kind) = &item.kind { | |
3c0e092e XL |
1005 | let ast::Trait { |
1006 | is_auto, | |
1007 | unsafety, | |
1008 | ref generics, | |
1009 | ref bounds, | |
1010 | ref items, | |
1011 | } = **trait_kind; | |
f20569fa XL |
1012 | let mut result = String::with_capacity(128); |
1013 | let header = format!( | |
1014 | "{}{}{}trait ", | |
1015 | format_visibility(context, &item.vis), | |
1016 | format_unsafety(unsafety), | |
1017 | format_auto(is_auto), | |
1018 | ); | |
1019 | result.push_str(&header); | |
1020 | ||
1021 | let body_lo = context.snippet_provider.span_after(item.span, "{"); | |
1022 | ||
1023 | let shape = Shape::indented(offset, context.config).offset_left(result.len())?; | |
1024 | let generics_str = | |
1025 | rewrite_generics(context, rewrite_ident(context, item.ident), generics, shape)?; | |
1026 | result.push_str(&generics_str); | |
1027 | ||
1028 | // FIXME(#2055): rustfmt fails to format when there are comments between trait bounds. | |
3c0e092e | 1029 | if !bounds.is_empty() { |
f20569fa XL |
1030 | let ident_hi = context |
1031 | .snippet_provider | |
a2a8927a | 1032 | .span_after(item.span, item.ident.as_str()); |
3c0e092e | 1033 | let bound_hi = bounds.last().unwrap().span().hi(); |
f20569fa XL |
1034 | let snippet = context.snippet(mk_sp(ident_hi, bound_hi)); |
1035 | if contains_comment(snippet) { | |
1036 | return None; | |
1037 | } | |
1038 | ||
1039 | result = rewrite_assign_rhs_with( | |
1040 | context, | |
1041 | result + ":", | |
3c0e092e | 1042 | bounds, |
f20569fa | 1043 | shape, |
a2a8927a | 1044 | &RhsAssignKind::Bounds, |
f20569fa XL |
1045 | RhsTactics::ForceNextLineWithoutIndent, |
1046 | )?; | |
1047 | } | |
1048 | ||
1049 | // Rewrite where-clause. | |
1050 | if !generics.where_clause.predicates.is_empty() { | |
1051 | let where_on_new_line = context.config.indent_style() != IndentStyle::Block; | |
1052 | ||
1053 | let where_budget = context.budget(last_line_width(&result)); | |
3c0e092e | 1054 | let pos_before_where = if bounds.is_empty() { |
f20569fa XL |
1055 | generics.where_clause.span.lo() |
1056 | } else { | |
3c0e092e | 1057 | bounds[bounds.len() - 1].span().hi() |
f20569fa XL |
1058 | }; |
1059 | let option = WhereClauseOption::snuggled(&generics_str); | |
1060 | let where_clause_str = rewrite_where_clause( | |
1061 | context, | |
5e7ed085 FG |
1062 | &generics.where_clause.predicates, |
1063 | generics.where_clause.span, | |
f20569fa XL |
1064 | context.config.brace_style(), |
1065 | Shape::legacy(where_budget, offset.block_only()), | |
1066 | where_on_new_line, | |
1067 | "{", | |
1068 | None, | |
1069 | pos_before_where, | |
1070 | option, | |
1071 | )?; | |
1072 | // If the where-clause cannot fit on the same line, | |
1073 | // put the where-clause on a new line | |
1074 | if !where_clause_str.contains('\n') | |
1075 | && last_line_width(&result) + where_clause_str.len() + offset.width() | |
1076 | > context.config.comment_width() | |
1077 | { | |
1078 | let width = offset.block_indent + context.config.tab_spaces() - 1; | |
1079 | let where_indent = Indent::new(0, width); | |
1080 | result.push_str(&where_indent.to_string_with_newline(context.config)); | |
1081 | } | |
1082 | result.push_str(&where_clause_str); | |
1083 | } else { | |
1084 | let item_snippet = context.snippet(item.span); | |
1085 | if let Some(lo) = item_snippet.find('/') { | |
1086 | // 1 = `{` | |
1087 | let comment_hi = body_lo - BytePos(1); | |
1088 | let comment_lo = item.span.lo() + BytePos(lo as u32); | |
1089 | if comment_lo < comment_hi { | |
1090 | match recover_missing_comment_in_span( | |
1091 | mk_sp(comment_lo, comment_hi), | |
1092 | Shape::indented(offset, context.config), | |
1093 | context, | |
1094 | last_line_width(&result), | |
1095 | ) { | |
1096 | Some(ref missing_comment) if !missing_comment.is_empty() => { | |
1097 | result.push_str(missing_comment); | |
1098 | } | |
1099 | _ => (), | |
1100 | } | |
1101 | } | |
1102 | } | |
1103 | } | |
1104 | ||
3c0e092e XL |
1105 | let block_span = mk_sp(generics.where_clause.span.hi(), item.span.hi()); |
1106 | let snippet = context.snippet(block_span); | |
1107 | let open_pos = snippet.find_uncommented("{")? + 1; | |
1108 | ||
f20569fa XL |
1109 | match context.config.brace_style() { |
1110 | _ if last_line_contains_single_line_comment(&result) | |
1111 | || last_line_width(&result) + 2 > context.budget(offset.width()) => | |
1112 | { | |
1113 | result.push_str(&offset.to_string_with_newline(context.config)); | |
1114 | } | |
3c0e092e XL |
1115 | _ if context.config.empty_item_single_line() |
1116 | && items.is_empty() | |
1117 | && !result.contains('\n') | |
1118 | && !contains_comment(&snippet[open_pos..]) => | |
1119 | { | |
1120 | result.push_str(" {}"); | |
1121 | return Some(result); | |
1122 | } | |
f20569fa XL |
1123 | BraceStyle::AlwaysNextLine => { |
1124 | result.push_str(&offset.to_string_with_newline(context.config)); | |
1125 | } | |
1126 | BraceStyle::PreferSameLine => result.push(' '), | |
1127 | BraceStyle::SameLineWhere => { | |
1128 | if result.contains('\n') | |
3c0e092e | 1129 | || (!generics.where_clause.predicates.is_empty() && !items.is_empty()) |
f20569fa XL |
1130 | { |
1131 | result.push_str(&offset.to_string_with_newline(context.config)); | |
1132 | } else { | |
1133 | result.push(' '); | |
1134 | } | |
1135 | } | |
1136 | } | |
1137 | result.push('{'); | |
1138 | ||
f20569fa XL |
1139 | let outer_indent_str = offset.block_only().to_string_with_newline(context.config); |
1140 | ||
3c0e092e | 1141 | if !items.is_empty() || contains_comment(&snippet[open_pos..]) { |
f20569fa XL |
1142 | let mut visitor = FmtVisitor::from_context(context); |
1143 | visitor.block_indent = offset.block_only().block_indent(context.config); | |
1144 | visitor.last_pos = block_span.lo() + BytePos(open_pos as u32); | |
1145 | ||
3c0e092e | 1146 | for item in items { |
f20569fa XL |
1147 | visitor.visit_trait_item(item); |
1148 | } | |
1149 | ||
1150 | visitor.format_missing(item.span.hi() - BytePos(1)); | |
1151 | ||
1152 | let inner_indent_str = visitor.block_indent.to_string_with_newline(context.config); | |
1153 | ||
1154 | result.push_str(&inner_indent_str); | |
1155 | result.push_str(visitor.buffer.trim()); | |
1156 | result.push_str(&outer_indent_str); | |
1157 | } else if result.contains('\n') { | |
1158 | result.push_str(&outer_indent_str); | |
1159 | } | |
1160 | ||
1161 | result.push('}'); | |
1162 | Some(result) | |
1163 | } else { | |
1164 | unreachable!(); | |
1165 | } | |
1166 | } | |
1167 | ||
f20569fa XL |
1168 | pub(crate) struct TraitAliasBounds<'a> { |
1169 | generic_bounds: &'a ast::GenericBounds, | |
1170 | generics: &'a ast::Generics, | |
1171 | } | |
1172 | ||
1173 | impl<'a> Rewrite for TraitAliasBounds<'a> { | |
1174 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
1175 | let generic_bounds_str = self.generic_bounds.rewrite(context, shape)?; | |
1176 | ||
1177 | let mut option = WhereClauseOption::new(true, WhereClauseSpace::None); | |
1178 | option.allow_single_line(); | |
1179 | ||
1180 | let where_str = rewrite_where_clause( | |
1181 | context, | |
5e7ed085 FG |
1182 | &self.generics.where_clause.predicates, |
1183 | self.generics.where_clause.span, | |
f20569fa XL |
1184 | context.config.brace_style(), |
1185 | shape, | |
1186 | false, | |
1187 | ";", | |
1188 | None, | |
1189 | self.generics.where_clause.span.lo(), | |
1190 | option, | |
1191 | )?; | |
1192 | ||
1193 | let fits_single_line = !generic_bounds_str.contains('\n') | |
1194 | && !where_str.contains('\n') | |
94222f64 | 1195 | && generic_bounds_str.len() + where_str.len() < shape.width; |
f20569fa XL |
1196 | let space = if generic_bounds_str.is_empty() || where_str.is_empty() { |
1197 | Cow::from("") | |
1198 | } else if fits_single_line { | |
1199 | Cow::from(" ") | |
1200 | } else { | |
3c0e092e | 1201 | shape.indent.to_string_with_newline(context.config) |
f20569fa XL |
1202 | }; |
1203 | ||
1204 | Some(format!("{}{}{}", generic_bounds_str, space, where_str)) | |
1205 | } | |
1206 | } | |
1207 | ||
1208 | pub(crate) fn format_trait_alias( | |
1209 | context: &RewriteContext<'_>, | |
1210 | ident: symbol::Ident, | |
1211 | vis: &ast::Visibility, | |
1212 | generics: &ast::Generics, | |
1213 | generic_bounds: &ast::GenericBounds, | |
1214 | shape: Shape, | |
1215 | ) -> Option<String> { | |
1216 | let alias = rewrite_ident(context, ident); | |
1217 | // 6 = "trait ", 2 = " =" | |
1218 | let g_shape = shape.offset_left(6)?.sub_width(2)?; | |
3c0e092e | 1219 | let generics_str = rewrite_generics(context, alias, generics, g_shape)?; |
f20569fa XL |
1220 | let vis_str = format_visibility(context, vis); |
1221 | let lhs = format!("{}trait {} =", vis_str, generics_str); | |
1222 | // 1 = ";" | |
1223 | let trait_alias_bounds = TraitAliasBounds { | |
f20569fa | 1224 | generic_bounds, |
94222f64 | 1225 | generics, |
f20569fa | 1226 | }; |
a2a8927a XL |
1227 | rewrite_assign_rhs( |
1228 | context, | |
1229 | lhs, | |
1230 | &trait_alias_bounds, | |
1231 | &RhsAssignKind::Bounds, | |
1232 | shape.sub_width(1)?, | |
1233 | ) | |
1234 | .map(|s| s + ";") | |
f20569fa XL |
1235 | } |
1236 | ||
1237 | fn format_unit_struct( | |
1238 | context: &RewriteContext<'_>, | |
1239 | p: &StructParts<'_>, | |
1240 | offset: Indent, | |
1241 | ) -> Option<String> { | |
1242 | let header_str = format_header(context, p.prefix, p.ident, p.vis, offset); | |
1243 | let generics_str = if let Some(generics) = p.generics { | |
1244 | let hi = context.snippet_provider.span_before(p.span, ";"); | |
1245 | format_generics( | |
1246 | context, | |
1247 | generics, | |
1248 | context.config.brace_style(), | |
1249 | BracePos::None, | |
1250 | offset, | |
1251 | // make a span that starts right after `struct Foo` | |
1252 | mk_sp(p.ident.span.hi(), hi), | |
1253 | last_line_width(&header_str), | |
1254 | )? | |
1255 | } else { | |
1256 | String::new() | |
1257 | }; | |
1258 | Some(format!("{}{};", header_str, generics_str)) | |
1259 | } | |
1260 | ||
1261 | pub(crate) fn format_struct_struct( | |
1262 | context: &RewriteContext<'_>, | |
1263 | struct_parts: &StructParts<'_>, | |
cdc7bbd5 | 1264 | fields: &[ast::FieldDef], |
f20569fa XL |
1265 | offset: Indent, |
1266 | one_line_width: Option<usize>, | |
1267 | ) -> Option<String> { | |
1268 | let mut result = String::with_capacity(1024); | |
1269 | let span = struct_parts.span; | |
1270 | ||
1271 | let header_str = struct_parts.format_header(context, offset); | |
1272 | result.push_str(&header_str); | |
1273 | ||
1274 | let header_hi = struct_parts.ident.span.hi(); | |
5e7ed085 FG |
1275 | let body_lo = if let Some(generics) = struct_parts.generics { |
1276 | // Adjust the span to start at the end of the generic arguments before searching for the '{' | |
1277 | let span = span.with_lo(generics.span.hi()); | |
1278 | context.snippet_provider.span_after(span, "{") | |
1279 | } else { | |
1280 | context.snippet_provider.span_after(span, "{") | |
1281 | }; | |
f20569fa XL |
1282 | |
1283 | let generics_str = match struct_parts.generics { | |
1284 | Some(g) => format_generics( | |
1285 | context, | |
1286 | g, | |
1287 | context.config.brace_style(), | |
1288 | if fields.is_empty() { | |
1289 | BracePos::ForceSameLine | |
1290 | } else { | |
1291 | BracePos::Auto | |
1292 | }, | |
1293 | offset, | |
1294 | // make a span that starts right after `struct Foo` | |
1295 | mk_sp(header_hi, body_lo), | |
1296 | last_line_width(&result), | |
1297 | )?, | |
1298 | None => { | |
1299 | // 3 = ` {}`, 2 = ` {`. | |
1300 | let overhead = if fields.is_empty() { 3 } else { 2 }; | |
1301 | if (context.config.brace_style() == BraceStyle::AlwaysNextLine && !fields.is_empty()) | |
1302 | || context.config.max_width() < overhead + result.len() | |
1303 | { | |
1304 | format!("\n{}{{", offset.block_only().to_string(context.config)) | |
1305 | } else { | |
1306 | " {".to_owned() | |
1307 | } | |
1308 | } | |
1309 | }; | |
1310 | // 1 = `}` | |
1311 | let overhead = if fields.is_empty() { 1 } else { 0 }; | |
1312 | let total_width = result.len() + generics_str.len() + overhead; | |
1313 | if !generics_str.is_empty() | |
1314 | && !generics_str.contains('\n') | |
1315 | && total_width > context.config.max_width() | |
1316 | { | |
1317 | result.push('\n'); | |
1318 | result.push_str(&offset.to_string(context.config)); | |
1319 | result.push_str(generics_str.trim_start()); | |
1320 | } else { | |
1321 | result.push_str(&generics_str); | |
1322 | } | |
1323 | ||
1324 | if fields.is_empty() { | |
1325 | let inner_span = mk_sp(body_lo, span.hi() - BytePos(1)); | |
1326 | format_empty_struct_or_tuple(context, inner_span, offset, &mut result, "", "}"); | |
1327 | return Some(result); | |
1328 | } | |
1329 | ||
1330 | // 3 = ` ` and ` }` | |
1331 | let one_line_budget = context.budget(result.len() + 3 + offset.width()); | |
1332 | let one_line_budget = | |
1333 | one_line_width.map_or(0, |one_line_width| min(one_line_width, one_line_budget)); | |
1334 | ||
1335 | let items_str = rewrite_with_alignment( | |
1336 | fields, | |
1337 | context, | |
1338 | Shape::indented(offset.block_indent(context.config), context.config).sub_width(1)?, | |
1339 | mk_sp(body_lo, span.hi()), | |
1340 | one_line_budget, | |
1341 | )?; | |
1342 | ||
1343 | if !items_str.contains('\n') | |
1344 | && !result.contains('\n') | |
1345 | && items_str.len() <= one_line_budget | |
1346 | && !last_line_contains_single_line_comment(&items_str) | |
1347 | { | |
1348 | Some(format!("{} {} }}", result, items_str)) | |
1349 | } else { | |
1350 | Some(format!( | |
1351 | "{}\n{}{}\n{}}}", | |
1352 | result, | |
1353 | offset | |
1354 | .block_indent(context.config) | |
1355 | .to_string(context.config), | |
1356 | items_str, | |
1357 | offset.to_string(context.config) | |
1358 | )) | |
1359 | } | |
1360 | } | |
1361 | ||
1362 | fn get_bytepos_after_visibility(vis: &ast::Visibility, default_span: Span) -> BytePos { | |
1363 | match vis.kind { | |
923072b8 | 1364 | ast::VisibilityKind::Restricted { .. } => vis.span.hi(), |
f20569fa XL |
1365 | _ => default_span.lo(), |
1366 | } | |
1367 | } | |
1368 | ||
1369 | // Format tuple or struct without any fields. We need to make sure that the comments | |
1370 | // inside the delimiters are preserved. | |
1371 | fn format_empty_struct_or_tuple( | |
1372 | context: &RewriteContext<'_>, | |
1373 | span: Span, | |
1374 | offset: Indent, | |
1375 | result: &mut String, | |
1376 | opener: &str, | |
1377 | closer: &str, | |
1378 | ) { | |
1379 | // 3 = " {}" or "();" | |
3c0e092e | 1380 | let used_width = last_line_used_width(result, offset.width()) + 3; |
f20569fa XL |
1381 | if used_width > context.config.max_width() { |
1382 | result.push_str(&offset.to_string_with_newline(context.config)) | |
1383 | } | |
1384 | result.push_str(opener); | |
5e7ed085 FG |
1385 | |
1386 | // indented shape for proper indenting of multi-line comments | |
1387 | let shape = Shape::indented(offset.block_indent(context.config), context.config); | |
1388 | match rewrite_missing_comment(span, shape, context) { | |
f20569fa XL |
1389 | Some(ref s) if s.is_empty() => (), |
1390 | Some(ref s) => { | |
5e7ed085 FG |
1391 | let is_multi_line = !is_single_line(s); |
1392 | if is_multi_line || first_line_contains_single_line_comment(s) { | |
f20569fa XL |
1393 | let nested_indent_str = offset |
1394 | .block_indent(context.config) | |
1395 | .to_string_with_newline(context.config); | |
1396 | result.push_str(&nested_indent_str); | |
1397 | } | |
1398 | result.push_str(s); | |
5e7ed085 | 1399 | if is_multi_line || last_line_contains_single_line_comment(s) { |
f20569fa XL |
1400 | result.push_str(&offset.to_string_with_newline(context.config)); |
1401 | } | |
1402 | } | |
1403 | None => result.push_str(context.snippet(span)), | |
1404 | } | |
1405 | result.push_str(closer); | |
1406 | } | |
1407 | ||
1408 | fn format_tuple_struct( | |
1409 | context: &RewriteContext<'_>, | |
1410 | struct_parts: &StructParts<'_>, | |
cdc7bbd5 | 1411 | fields: &[ast::FieldDef], |
f20569fa XL |
1412 | offset: Indent, |
1413 | ) -> Option<String> { | |
1414 | let mut result = String::with_capacity(1024); | |
1415 | let span = struct_parts.span; | |
1416 | ||
1417 | let header_str = struct_parts.format_header(context, offset); | |
1418 | result.push_str(&header_str); | |
1419 | ||
1420 | let body_lo = if fields.is_empty() { | |
1421 | let lo = get_bytepos_after_visibility(struct_parts.vis, span); | |
1422 | context | |
1423 | .snippet_provider | |
1424 | .span_after(mk_sp(lo, span.hi()), "(") | |
1425 | } else { | |
1426 | fields[0].span.lo() | |
1427 | }; | |
1428 | let body_hi = if fields.is_empty() { | |
1429 | context | |
1430 | .snippet_provider | |
1431 | .span_after(mk_sp(body_lo, span.hi()), ")") | |
1432 | } else { | |
1433 | // This is a dirty hack to work around a missing `)` from the span of the last field. | |
1434 | let last_arg_span = fields[fields.len() - 1].span; | |
1435 | context | |
1436 | .snippet_provider | |
1437 | .opt_span_after(mk_sp(last_arg_span.hi(), span.hi()), ")") | |
1438 | .unwrap_or_else(|| last_arg_span.hi()) | |
1439 | }; | |
1440 | ||
1441 | let where_clause_str = match struct_parts.generics { | |
1442 | Some(generics) => { | |
1443 | let budget = context.budget(last_line_width(&header_str)); | |
1444 | let shape = Shape::legacy(budget, offset); | |
1445 | let generics_str = rewrite_generics(context, "", generics, shape)?; | |
1446 | result.push_str(&generics_str); | |
1447 | ||
1448 | let where_budget = context.budget(last_line_width(&result)); | |
1449 | let option = WhereClauseOption::new(true, WhereClauseSpace::Newline); | |
1450 | rewrite_where_clause( | |
1451 | context, | |
5e7ed085 FG |
1452 | &generics.where_clause.predicates, |
1453 | generics.where_clause.span, | |
f20569fa XL |
1454 | context.config.brace_style(), |
1455 | Shape::legacy(where_budget, offset.block_only()), | |
1456 | false, | |
1457 | ";", | |
1458 | None, | |
1459 | body_hi, | |
1460 | option, | |
1461 | )? | |
1462 | } | |
1463 | None => "".to_owned(), | |
1464 | }; | |
1465 | ||
1466 | if fields.is_empty() { | |
1467 | let body_hi = context | |
1468 | .snippet_provider | |
1469 | .span_before(mk_sp(body_lo, span.hi()), ")"); | |
1470 | let inner_span = mk_sp(body_lo, body_hi); | |
1471 | format_empty_struct_or_tuple(context, inner_span, offset, &mut result, "(", ")"); | |
1472 | } else { | |
1473 | let shape = Shape::indented(offset, context.config).sub_width(1)?; | |
3c0e092e XL |
1474 | let lo = if let Some(generics) = struct_parts.generics { |
1475 | generics.span.hi() | |
1476 | } else { | |
1477 | struct_parts.ident.span.hi() | |
1478 | }; | |
f20569fa XL |
1479 | result = overflow::rewrite_with_parens( |
1480 | context, | |
1481 | &result, | |
1482 | fields.iter(), | |
1483 | shape, | |
3c0e092e | 1484 | mk_sp(lo, span.hi()), |
cdc7bbd5 | 1485 | context.config.fn_call_width(), |
f20569fa XL |
1486 | None, |
1487 | )?; | |
1488 | } | |
1489 | ||
1490 | if !where_clause_str.is_empty() | |
1491 | && !where_clause_str.contains('\n') | |
1492 | && (result.contains('\n') | |
1493 | || offset.block_indent + result.len() + where_clause_str.len() + 1 | |
1494 | > context.config.max_width()) | |
1495 | { | |
1496 | // We need to put the where-clause on a new line, but we didn't | |
1497 | // know that earlier, so the where-clause will not be indented properly. | |
1498 | result.push('\n'); | |
1499 | result.push_str( | |
1500 | &(offset.block_only() + (context.config.tab_spaces() - 1)).to_string(context.config), | |
1501 | ); | |
1502 | } | |
1503 | result.push_str(&where_clause_str); | |
1504 | ||
1505 | Some(result) | |
1506 | } | |
1507 | ||
3c0e092e XL |
1508 | pub(crate) enum ItemVisitorKind<'a> { |
1509 | Item(&'a ast::Item), | |
1510 | AssocTraitItem(&'a ast::AssocItem), | |
1511 | AssocImplItem(&'a ast::AssocItem), | |
1512 | ForeignItem(&'a ast::ForeignItem), | |
1513 | } | |
1514 | ||
1515 | struct TyAliasRewriteInfo<'c, 'g>( | |
1516 | &'c RewriteContext<'c>, | |
1517 | Indent, | |
1518 | &'g ast::Generics, | |
5e7ed085 FG |
1519 | (ast::TyAliasWhereClause, ast::TyAliasWhereClause), |
1520 | usize, | |
3c0e092e XL |
1521 | symbol::Ident, |
1522 | Span, | |
1523 | ); | |
1524 | ||
1525 | pub(crate) fn rewrite_type_alias<'a, 'b>( | |
1526 | ty_alias_kind: &ast::TyAlias, | |
1527 | context: &RewriteContext<'a>, | |
f20569fa | 1528 | indent: Indent, |
3c0e092e XL |
1529 | visitor_kind: &ItemVisitorKind<'b>, |
1530 | span: Span, | |
1531 | ) -> Option<String> { | |
1532 | use ItemVisitorKind::*; | |
1533 | ||
1534 | let ast::TyAlias { | |
1535 | defaultness, | |
1536 | ref generics, | |
1537 | ref bounds, | |
1538 | ref ty, | |
5e7ed085 FG |
1539 | where_clauses, |
1540 | where_predicates_split, | |
3c0e092e | 1541 | } = *ty_alias_kind; |
a2a8927a | 1542 | let ty_opt = ty.as_ref(); |
3c0e092e XL |
1543 | let (ident, vis) = match visitor_kind { |
1544 | Item(i) => (i.ident, &i.vis), | |
1545 | AssocTraitItem(i) | AssocImplItem(i) => (i.ident, &i.vis), | |
1546 | ForeignItem(i) => (i.ident, &i.vis), | |
1547 | }; | |
5e7ed085 FG |
1548 | let rw_info = &TyAliasRewriteInfo( |
1549 | context, | |
1550 | indent, | |
1551 | generics, | |
1552 | where_clauses, | |
1553 | where_predicates_split, | |
1554 | ident, | |
1555 | span, | |
1556 | ); | |
a2a8927a | 1557 | let op_ty = opaque_ty(ty); |
3c0e092e XL |
1558 | // Type Aliases are formatted slightly differently depending on the context |
1559 | // in which they appear, whether they are opaque, and whether they are associated. | |
1560 | // https://rustc-dev-guide.rust-lang.org/opaque-types-type-alias-impl-trait.html | |
1561 | // https://github.com/rust-dev-tools/fmt-rfcs/blob/master/guide/items.md#type-aliases | |
a2a8927a XL |
1562 | match (visitor_kind, &op_ty) { |
1563 | (Item(_) | AssocTraitItem(_) | ForeignItem(_), Some(op_bounds)) => { | |
1564 | let op = OpaqueType { bounds: op_bounds }; | |
1565 | rewrite_ty(rw_info, Some(bounds), Some(&op), vis) | |
1566 | } | |
1567 | (Item(_) | AssocTraitItem(_) | ForeignItem(_), None) => { | |
1568 | rewrite_ty(rw_info, Some(bounds), ty_opt, vis) | |
3c0e092e | 1569 | } |
3c0e092e | 1570 | (AssocImplItem(_), _) => { |
a2a8927a XL |
1571 | let result = if let Some(op_bounds) = op_ty { |
1572 | let op = OpaqueType { bounds: op_bounds }; | |
1573 | rewrite_ty(rw_info, Some(bounds), Some(&op), &DEFAULT_VISIBILITY) | |
3c0e092e | 1574 | } else { |
a2a8927a | 1575 | rewrite_ty(rw_info, Some(bounds), ty_opt, vis) |
3c0e092e XL |
1576 | }?; |
1577 | match defaultness { | |
1578 | ast::Defaultness::Default(..) => Some(format!("default {}", result)), | |
1579 | _ => Some(result), | |
1580 | } | |
1581 | } | |
3c0e092e XL |
1582 | } |
1583 | } | |
1584 | ||
1585 | fn rewrite_ty<R: Rewrite>( | |
1586 | rw_info: &TyAliasRewriteInfo<'_, '_>, | |
f20569fa XL |
1587 | generic_bounds_opt: Option<&ast::GenericBounds>, |
1588 | rhs: Option<&R>, | |
3c0e092e | 1589 | vis: &ast::Visibility, |
f20569fa XL |
1590 | ) -> Option<String> { |
1591 | let mut result = String::with_capacity(128); | |
5e7ed085 FG |
1592 | let TyAliasRewriteInfo( |
1593 | context, | |
1594 | indent, | |
1595 | generics, | |
1596 | where_clauses, | |
1597 | where_predicates_split, | |
1598 | ident, | |
1599 | span, | |
1600 | ) = *rw_info; | |
1601 | let (before_where_predicates, after_where_predicates) = generics | |
1602 | .where_clause | |
1603 | .predicates | |
1604 | .split_at(where_predicates_split); | |
1605 | if !after_where_predicates.is_empty() { | |
1606 | return None; | |
1607 | } | |
f20569fa XL |
1608 | result.push_str(&format!("{}type ", format_visibility(context, vis))); |
1609 | let ident_str = rewrite_ident(context, ident); | |
1610 | ||
1611 | if generics.params.is_empty() { | |
1612 | result.push_str(ident_str) | |
1613 | } else { | |
1614 | // 2 = `= ` | |
1615 | let g_shape = Shape::indented(indent, context.config) | |
1616 | .offset_left(result.len())? | |
1617 | .sub_width(2)?; | |
1618 | let generics_str = rewrite_generics(context, ident_str, generics, g_shape)?; | |
1619 | result.push_str(&generics_str); | |
1620 | } | |
1621 | ||
1622 | if let Some(bounds) = generic_bounds_opt { | |
1623 | if !bounds.is_empty() { | |
1624 | // 2 = `: ` | |
1625 | let shape = Shape::indented(indent, context.config).offset_left(result.len() + 2)?; | |
1626 | let type_bounds = bounds.rewrite(context, shape).map(|s| format!(": {}", s))?; | |
1627 | result.push_str(&type_bounds); | |
1628 | } | |
1629 | } | |
1630 | ||
1631 | let where_budget = context.budget(last_line_width(&result)); | |
1632 | let mut option = WhereClauseOption::snuggled(&result); | |
1633 | if rhs.is_none() { | |
1634 | option.suppress_comma(); | |
1635 | } | |
1636 | let where_clause_str = rewrite_where_clause( | |
1637 | context, | |
5e7ed085 FG |
1638 | before_where_predicates, |
1639 | where_clauses.0.1, | |
f20569fa XL |
1640 | context.config.brace_style(), |
1641 | Shape::legacy(where_budget, indent), | |
1642 | false, | |
1643 | "=", | |
1644 | None, | |
1645 | generics.span.hi(), | |
1646 | option, | |
1647 | )?; | |
1648 | result.push_str(&where_clause_str); | |
1649 | ||
1650 | if let Some(ty) = rhs { | |
1651 | // If there's a where clause, add a newline before the assignment. Otherwise just add a | |
1652 | // space. | |
5e7ed085 | 1653 | let has_where = !before_where_predicates.is_empty(); |
f20569fa XL |
1654 | if has_where { |
1655 | result.push_str(&indent.to_string_with_newline(context.config)); | |
1656 | } else { | |
1657 | result.push(' '); | |
1658 | } | |
1659 | ||
1660 | let comment_span = context | |
1661 | .snippet_provider | |
1662 | .opt_span_before(span, "=") | |
5e7ed085 | 1663 | .map(|op_lo| mk_sp(where_clauses.0.1.hi(), op_lo)); |
f20569fa XL |
1664 | |
1665 | let lhs = match comment_span { | |
1666 | Some(comment_span) | |
1667 | if contains_comment(context.snippet_provider.span_to_snippet(comment_span)?) => | |
1668 | { | |
1669 | let comment_shape = if has_where { | |
1670 | Shape::indented(indent, context.config) | |
1671 | } else { | |
1672 | Shape::indented(indent, context.config) | |
1673 | .block_left(context.config.tab_spaces())? | |
1674 | }; | |
1675 | ||
1676 | combine_strs_with_missing_comments( | |
1677 | context, | |
1678 | result.trim_end(), | |
1679 | "=", | |
1680 | comment_span, | |
1681 | comment_shape, | |
1682 | true, | |
1683 | )? | |
1684 | } | |
1685 | _ => format!("{}=", result), | |
1686 | }; | |
1687 | ||
1688 | // 1 = `;` | |
1689 | let shape = Shape::indented(indent, context.config).sub_width(1)?; | |
a2a8927a | 1690 | rewrite_assign_rhs(context, lhs, &*ty, &RhsAssignKind::Ty, shape).map(|s| s + ";") |
f20569fa XL |
1691 | } else { |
1692 | Some(format!("{};", result)) | |
1693 | } | |
1694 | } | |
1695 | ||
f20569fa XL |
1696 | fn type_annotation_spacing(config: &Config) -> (&str, &str) { |
1697 | ( | |
1698 | if config.space_before_colon() { " " } else { "" }, | |
1699 | if config.space_after_colon() { " " } else { "" }, | |
1700 | ) | |
1701 | } | |
1702 | ||
1703 | pub(crate) fn rewrite_struct_field_prefix( | |
1704 | context: &RewriteContext<'_>, | |
cdc7bbd5 | 1705 | field: &ast::FieldDef, |
f20569fa XL |
1706 | ) -> Option<String> { |
1707 | let vis = format_visibility(context, &field.vis); | |
1708 | let type_annotation_spacing = type_annotation_spacing(context.config); | |
1709 | Some(match field.ident { | |
1710 | Some(name) => format!( | |
1711 | "{}{}{}:", | |
1712 | vis, | |
1713 | rewrite_ident(context, name), | |
1714 | type_annotation_spacing.0 | |
1715 | ), | |
1716 | None => vis.to_string(), | |
1717 | }) | |
1718 | } | |
1719 | ||
cdc7bbd5 | 1720 | impl Rewrite for ast::FieldDef { |
f20569fa XL |
1721 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { |
1722 | rewrite_struct_field(context, self, shape, 0) | |
1723 | } | |
1724 | } | |
1725 | ||
1726 | pub(crate) fn rewrite_struct_field( | |
1727 | context: &RewriteContext<'_>, | |
cdc7bbd5 | 1728 | field: &ast::FieldDef, |
f20569fa XL |
1729 | shape: Shape, |
1730 | lhs_max_width: usize, | |
1731 | ) -> Option<String> { | |
1732 | if contains_skip(&field.attrs) { | |
1733 | return Some(context.snippet(field.span()).to_owned()); | |
1734 | } | |
1735 | ||
1736 | let type_annotation_spacing = type_annotation_spacing(context.config); | |
1737 | let prefix = rewrite_struct_field_prefix(context, field)?; | |
1738 | ||
1739 | let attrs_str = field.attrs.rewrite(context, shape)?; | |
1740 | let attrs_extendable = field.ident.is_none() && is_attributes_extendable(&attrs_str); | |
1741 | let missing_span = if field.attrs.is_empty() { | |
1742 | mk_sp(field.span.lo(), field.span.lo()) | |
1743 | } else { | |
1744 | mk_sp(field.attrs.last().unwrap().span.hi(), field.span.lo()) | |
1745 | }; | |
1746 | let mut spacing = String::from(if field.ident.is_some() { | |
1747 | type_annotation_spacing.1 | |
1748 | } else { | |
1749 | "" | |
1750 | }); | |
1751 | // Try to put everything on a single line. | |
1752 | let attr_prefix = combine_strs_with_missing_comments( | |
1753 | context, | |
1754 | &attrs_str, | |
1755 | &prefix, | |
1756 | missing_span, | |
1757 | shape, | |
1758 | attrs_extendable, | |
1759 | )?; | |
1760 | let overhead = trimmed_last_line_width(&attr_prefix); | |
1761 | let lhs_offset = lhs_max_width.saturating_sub(overhead); | |
1762 | for _ in 0..lhs_offset { | |
1763 | spacing.push(' '); | |
1764 | } | |
1765 | // In this extreme case we will be missing a space between an attribute and a field. | |
1766 | if prefix.is_empty() && !attrs_str.is_empty() && attrs_extendable && spacing.is_empty() { | |
1767 | spacing.push(' '); | |
1768 | } | |
1769 | let orig_ty = shape | |
1770 | .offset_left(overhead + spacing.len()) | |
1771 | .and_then(|ty_shape| field.ty.rewrite(context, ty_shape)); | |
1772 | if let Some(ref ty) = orig_ty { | |
923072b8 | 1773 | if !ty.contains('\n') && !contains_comment(context.snippet(missing_span)) { |
f20569fa XL |
1774 | return Some(attr_prefix + &spacing + ty); |
1775 | } | |
1776 | } | |
1777 | ||
1778 | let is_prefix_empty = prefix.is_empty(); | |
1779 | // We must use multiline. We are going to put attributes and a field on different lines. | |
a2a8927a | 1780 | let field_str = rewrite_assign_rhs(context, prefix, &*field.ty, &RhsAssignKind::Ty, shape)?; |
f20569fa XL |
1781 | // Remove a leading white-space from `rewrite_assign_rhs()` when rewriting a tuple struct. |
1782 | let field_str = if is_prefix_empty { | |
1783 | field_str.trim_start() | |
1784 | } else { | |
1785 | &field_str | |
1786 | }; | |
1787 | combine_strs_with_missing_comments(context, &attrs_str, field_str, missing_span, shape, false) | |
1788 | } | |
1789 | ||
1790 | pub(crate) struct StaticParts<'a> { | |
1791 | prefix: &'a str, | |
1792 | vis: &'a ast::Visibility, | |
1793 | ident: symbol::Ident, | |
1794 | ty: &'a ast::Ty, | |
1795 | mutability: ast::Mutability, | |
1796 | expr_opt: Option<&'a ptr::P<ast::Expr>>, | |
1797 | defaultness: Option<ast::Defaultness>, | |
1798 | span: Span, | |
1799 | } | |
1800 | ||
1801 | impl<'a> StaticParts<'a> { | |
1802 | pub(crate) fn from_item(item: &'a ast::Item) -> Self { | |
1803 | let (defaultness, prefix, ty, mutability, expr) = match item.kind { | |
1804 | ast::ItemKind::Static(ref ty, mutability, ref expr) => { | |
1805 | (None, "static", ty, mutability, expr) | |
1806 | } | |
1807 | ast::ItemKind::Const(defaultness, ref ty, ref expr) => { | |
1808 | (Some(defaultness), "const", ty, ast::Mutability::Not, expr) | |
1809 | } | |
1810 | _ => unreachable!(), | |
1811 | }; | |
1812 | StaticParts { | |
1813 | prefix, | |
1814 | vis: &item.vis, | |
1815 | ident: item.ident, | |
1816 | ty, | |
1817 | mutability, | |
1818 | expr_opt: expr.as_ref(), | |
1819 | defaultness, | |
1820 | span: item.span, | |
1821 | } | |
1822 | } | |
1823 | ||
1824 | pub(crate) fn from_trait_item(ti: &'a ast::AssocItem) -> Self { | |
1825 | let (defaultness, ty, expr_opt) = match ti.kind { | |
1826 | ast::AssocItemKind::Const(defaultness, ref ty, ref expr_opt) => { | |
1827 | (defaultness, ty, expr_opt) | |
1828 | } | |
1829 | _ => unreachable!(), | |
1830 | }; | |
1831 | StaticParts { | |
1832 | prefix: "const", | |
3c0e092e | 1833 | vis: &ti.vis, |
f20569fa XL |
1834 | ident: ti.ident, |
1835 | ty, | |
1836 | mutability: ast::Mutability::Not, | |
1837 | expr_opt: expr_opt.as_ref(), | |
1838 | defaultness: Some(defaultness), | |
1839 | span: ti.span, | |
1840 | } | |
1841 | } | |
1842 | ||
1843 | pub(crate) fn from_impl_item(ii: &'a ast::AssocItem) -> Self { | |
1844 | let (defaultness, ty, expr) = match ii.kind { | |
1845 | ast::AssocItemKind::Const(defaultness, ref ty, ref expr) => (defaultness, ty, expr), | |
1846 | _ => unreachable!(), | |
1847 | }; | |
1848 | StaticParts { | |
1849 | prefix: "const", | |
1850 | vis: &ii.vis, | |
1851 | ident: ii.ident, | |
1852 | ty, | |
1853 | mutability: ast::Mutability::Not, | |
1854 | expr_opt: expr.as_ref(), | |
1855 | defaultness: Some(defaultness), | |
1856 | span: ii.span, | |
1857 | } | |
1858 | } | |
1859 | } | |
1860 | ||
1861 | fn rewrite_static( | |
1862 | context: &RewriteContext<'_>, | |
1863 | static_parts: &StaticParts<'_>, | |
1864 | offset: Indent, | |
1865 | ) -> Option<String> { | |
1866 | let colon = colon_spaces(context.config); | |
1867 | let mut prefix = format!( | |
1868 | "{}{}{} {}{}{}", | |
1869 | format_visibility(context, static_parts.vis), | |
1870 | static_parts.defaultness.map_or("", format_defaultness), | |
1871 | static_parts.prefix, | |
1872 | format_mutability(static_parts.mutability), | |
1873 | rewrite_ident(context, static_parts.ident), | |
1874 | colon, | |
1875 | ); | |
1876 | // 2 = " =".len() | |
1877 | let ty_shape = | |
1878 | Shape::indented(offset.block_only(), context.config).offset_left(prefix.len() + 2)?; | |
1879 | let ty_str = match static_parts.ty.rewrite(context, ty_shape) { | |
1880 | Some(ty_str) => ty_str, | |
1881 | None => { | |
1882 | if prefix.ends_with(' ') { | |
1883 | prefix.pop(); | |
1884 | } | |
1885 | let nested_indent = offset.block_indent(context.config); | |
1886 | let nested_shape = Shape::indented(nested_indent, context.config); | |
1887 | let ty_str = static_parts.ty.rewrite(context, nested_shape)?; | |
1888 | format!( | |
1889 | "{}{}", | |
1890 | nested_indent.to_string_with_newline(context.config), | |
1891 | ty_str | |
1892 | ) | |
1893 | } | |
1894 | }; | |
1895 | ||
1896 | if let Some(expr) = static_parts.expr_opt { | |
cdc7bbd5 XL |
1897 | let comments_lo = context.snippet_provider.span_after(static_parts.span, "="); |
1898 | let expr_lo = expr.span.lo(); | |
1899 | let comments_span = mk_sp(comments_lo, expr_lo); | |
1900 | ||
f20569fa | 1901 | let lhs = format!("{}{} =", prefix, ty_str); |
cdc7bbd5 | 1902 | |
f20569fa XL |
1903 | // 1 = ; |
1904 | let remaining_width = context.budget(offset.block_indent + 1); | |
cdc7bbd5 | 1905 | rewrite_assign_rhs_with_comments( |
f20569fa | 1906 | context, |
cdc7bbd5 | 1907 | &lhs, |
f20569fa XL |
1908 | &**expr, |
1909 | Shape::legacy(remaining_width, offset.block_only()), | |
a2a8927a | 1910 | &RhsAssignKind::Expr(&expr.kind, expr.span), |
cdc7bbd5 XL |
1911 | RhsTactics::Default, |
1912 | comments_span, | |
1913 | true, | |
f20569fa XL |
1914 | ) |
1915 | .and_then(|res| recover_comment_removed(res, static_parts.span, context)) | |
1916 | .map(|s| if s.ends_with(';') { s } else { s + ";" }) | |
1917 | } else { | |
1918 | Some(format!("{}{};", prefix, ty_str)) | |
1919 | } | |
1920 | } | |
a2a8927a XL |
1921 | |
1922 | // FIXME(calebcartwright) - This is a hack around a bug in the handling of TyKind::ImplTrait. | |
1923 | // This should be removed once that bug is resolved, with the type alias formatting using the | |
1924 | // defined Ty for the RHS directly. | |
1925 | // https://github.com/rust-lang/rustfmt/issues/4373 | |
1926 | // https://github.com/rust-lang/rustfmt/issues/5027 | |
f20569fa XL |
1927 | struct OpaqueType<'a> { |
1928 | bounds: &'a ast::GenericBounds, | |
1929 | } | |
1930 | ||
1931 | impl<'a> Rewrite for OpaqueType<'a> { | |
1932 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
1933 | let shape = shape.offset_left(5)?; // `impl ` | |
1934 | self.bounds | |
1935 | .rewrite(context, shape) | |
1936 | .map(|s| format!("impl {}", s)) | |
1937 | } | |
1938 | } | |
1939 | ||
f20569fa XL |
1940 | impl Rewrite for ast::FnRetTy { |
1941 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
1942 | match *self { | |
1943 | ast::FnRetTy::Default(_) => Some(String::new()), | |
1944 | ast::FnRetTy::Ty(ref ty) => { | |
1945 | if context.config.version() == Version::One | |
1946 | || context.config.indent_style() == IndentStyle::Visual | |
1947 | { | |
1948 | let inner_width = shape.width.checked_sub(3)?; | |
1949 | return ty | |
1950 | .rewrite(context, Shape::legacy(inner_width, shape.indent + 3)) | |
1951 | .map(|r| format!("-> {}", r)); | |
1952 | } | |
1953 | ||
1954 | ty.rewrite(context, shape.offset_left(3)?) | |
1955 | .map(|s| format!("-> {}", s)) | |
1956 | } | |
1957 | } | |
1958 | } | |
1959 | } | |
1960 | ||
1961 | fn is_empty_infer(ty: &ast::Ty, pat_span: Span) -> bool { | |
1962 | match ty.kind { | |
1963 | ast::TyKind::Infer => ty.span.hi() == pat_span.hi(), | |
1964 | _ => false, | |
1965 | } | |
1966 | } | |
1967 | ||
1968 | /// Recover any missing comments between the param and the type. | |
1969 | /// | |
1970 | /// # Returns | |
1971 | /// | |
1972 | /// A 2-len tuple with the comment before the colon in first position, and the comment after the | |
1973 | /// colon in second position. | |
1974 | fn get_missing_param_comments( | |
1975 | context: &RewriteContext<'_>, | |
1976 | pat_span: Span, | |
1977 | ty_span: Span, | |
1978 | shape: Shape, | |
1979 | ) -> (String, String) { | |
1980 | let missing_comment_span = mk_sp(pat_span.hi(), ty_span.lo()); | |
1981 | ||
1982 | let span_before_colon = { | |
1983 | let missing_comment_span_hi = context | |
1984 | .snippet_provider | |
1985 | .span_before(missing_comment_span, ":"); | |
1986 | mk_sp(pat_span.hi(), missing_comment_span_hi) | |
1987 | }; | |
1988 | let span_after_colon = { | |
1989 | let missing_comment_span_lo = context | |
1990 | .snippet_provider | |
1991 | .span_after(missing_comment_span, ":"); | |
1992 | mk_sp(missing_comment_span_lo, ty_span.lo()) | |
1993 | }; | |
1994 | ||
1995 | let comment_before_colon = rewrite_missing_comment(span_before_colon, shape, context) | |
1996 | .filter(|comment| !comment.is_empty()) | |
1997 | .map_or(String::new(), |comment| format!(" {}", comment)); | |
1998 | let comment_after_colon = rewrite_missing_comment(span_after_colon, shape, context) | |
1999 | .filter(|comment| !comment.is_empty()) | |
2000 | .map_or(String::new(), |comment| format!("{} ", comment)); | |
2001 | (comment_before_colon, comment_after_colon) | |
2002 | } | |
2003 | ||
2004 | impl Rewrite for ast::Param { | |
2005 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
2006 | let param_attrs_result = self | |
2007 | .attrs | |
2008 | .rewrite(context, Shape::legacy(shape.width, shape.indent))?; | |
3c0e092e XL |
2009 | // N.B. Doc comments aren't typically valid syntax, but could appear |
2010 | // in the presence of certain macros - https://github.com/rust-lang/rustfmt/issues/4936 | |
2011 | let (span, has_multiple_attr_lines, has_doc_comments) = if !self.attrs.is_empty() { | |
f20569fa XL |
2012 | let num_attrs = self.attrs.len(); |
2013 | ( | |
2014 | mk_sp(self.attrs[num_attrs - 1].span.hi(), self.pat.span.lo()), | |
94222f64 | 2015 | param_attrs_result.contains('\n'), |
3c0e092e | 2016 | self.attrs.iter().any(|a| a.is_doc_comment()), |
f20569fa XL |
2017 | ) |
2018 | } else { | |
3c0e092e | 2019 | (mk_sp(self.span.lo(), self.span.lo()), false, false) |
f20569fa XL |
2020 | }; |
2021 | ||
2022 | if let Some(ref explicit_self) = self.to_self() { | |
2023 | rewrite_explicit_self( | |
2024 | context, | |
2025 | explicit_self, | |
2026 | ¶m_attrs_result, | |
2027 | span, | |
2028 | shape, | |
2029 | has_multiple_attr_lines, | |
2030 | ) | |
2031 | } else if is_named_param(self) { | |
3c0e092e XL |
2032 | let param_name = &self |
2033 | .pat | |
2034 | .rewrite(context, Shape::legacy(shape.width, shape.indent))?; | |
f20569fa XL |
2035 | let mut result = combine_strs_with_missing_comments( |
2036 | context, | |
2037 | ¶m_attrs_result, | |
3c0e092e | 2038 | param_name, |
f20569fa XL |
2039 | span, |
2040 | shape, | |
3c0e092e | 2041 | !has_multiple_attr_lines && !has_doc_comments, |
f20569fa XL |
2042 | )?; |
2043 | ||
2044 | if !is_empty_infer(&*self.ty, self.pat.span) { | |
2045 | let (before_comment, after_comment) = | |
2046 | get_missing_param_comments(context, self.pat.span, self.ty.span, shape); | |
2047 | result.push_str(&before_comment); | |
2048 | result.push_str(colon_spaces(context.config)); | |
2049 | result.push_str(&after_comment); | |
2050 | let overhead = last_line_width(&result); | |
2051 | let max_width = shape.width.checked_sub(overhead)?; | |
3c0e092e | 2052 | if let Some(ty_str) = self |
f20569fa | 2053 | .ty |
3c0e092e XL |
2054 | .rewrite(context, Shape::legacy(max_width, shape.indent)) |
2055 | { | |
2056 | result.push_str(&ty_str); | |
2057 | } else { | |
5e7ed085 FG |
2058 | let prev_str = if param_attrs_result.is_empty() { |
2059 | param_attrs_result | |
2060 | } else { | |
2061 | param_attrs_result + &shape.to_string_with_newline(context.config) | |
2062 | }; | |
2063 | ||
3c0e092e XL |
2064 | result = combine_strs_with_missing_comments( |
2065 | context, | |
5e7ed085 | 2066 | &prev_str, |
3c0e092e XL |
2067 | param_name, |
2068 | span, | |
2069 | shape, | |
2070 | !has_multiple_attr_lines, | |
2071 | )?; | |
2072 | result.push_str(&before_comment); | |
2073 | result.push_str(colon_spaces(context.config)); | |
2074 | result.push_str(&after_comment); | |
2075 | let overhead = last_line_width(&result); | |
2076 | let max_width = shape.width.checked_sub(overhead)?; | |
2077 | let ty_str = self | |
2078 | .ty | |
2079 | .rewrite(context, Shape::legacy(max_width, shape.indent))?; | |
2080 | result.push_str(&ty_str); | |
2081 | } | |
f20569fa XL |
2082 | } |
2083 | ||
2084 | Some(result) | |
2085 | } else { | |
2086 | self.ty.rewrite(context, shape) | |
2087 | } | |
2088 | } | |
2089 | } | |
2090 | ||
2091 | fn rewrite_explicit_self( | |
2092 | context: &RewriteContext<'_>, | |
2093 | explicit_self: &ast::ExplicitSelf, | |
2094 | param_attrs: &str, | |
2095 | span: Span, | |
2096 | shape: Shape, | |
2097 | has_multiple_attr_lines: bool, | |
2098 | ) -> Option<String> { | |
2099 | match explicit_self.node { | |
2100 | ast::SelfKind::Region(lt, m) => { | |
2101 | let mut_str = format_mutability(m); | |
2102 | match lt { | |
2103 | Some(ref l) => { | |
2104 | let lifetime_str = l.rewrite( | |
2105 | context, | |
2106 | Shape::legacy(context.config.max_width(), Indent::empty()), | |
2107 | )?; | |
2108 | Some(combine_strs_with_missing_comments( | |
2109 | context, | |
3c0e092e | 2110 | param_attrs, |
f20569fa XL |
2111 | &format!("&{} {}self", lifetime_str, mut_str), |
2112 | span, | |
2113 | shape, | |
2114 | !has_multiple_attr_lines, | |
2115 | )?) | |
2116 | } | |
2117 | None => Some(combine_strs_with_missing_comments( | |
2118 | context, | |
3c0e092e | 2119 | param_attrs, |
f20569fa XL |
2120 | &format!("&{}self", mut_str), |
2121 | span, | |
2122 | shape, | |
2123 | !has_multiple_attr_lines, | |
2124 | )?), | |
2125 | } | |
2126 | } | |
2127 | ast::SelfKind::Explicit(ref ty, mutability) => { | |
2128 | let type_str = ty.rewrite( | |
2129 | context, | |
2130 | Shape::legacy(context.config.max_width(), Indent::empty()), | |
2131 | )?; | |
2132 | ||
2133 | Some(combine_strs_with_missing_comments( | |
2134 | context, | |
3c0e092e | 2135 | param_attrs, |
f20569fa XL |
2136 | &format!("{}self: {}", format_mutability(mutability), type_str), |
2137 | span, | |
2138 | shape, | |
2139 | !has_multiple_attr_lines, | |
2140 | )?) | |
2141 | } | |
2142 | ast::SelfKind::Value(mutability) => Some(combine_strs_with_missing_comments( | |
2143 | context, | |
3c0e092e | 2144 | param_attrs, |
f20569fa XL |
2145 | &format!("{}self", format_mutability(mutability)), |
2146 | span, | |
2147 | shape, | |
2148 | !has_multiple_attr_lines, | |
2149 | )?), | |
2150 | } | |
2151 | } | |
2152 | ||
2153 | pub(crate) fn span_lo_for_param(param: &ast::Param) -> BytePos { | |
2154 | if param.attrs.is_empty() { | |
2155 | if is_named_param(param) { | |
2156 | param.pat.span.lo() | |
2157 | } else { | |
2158 | param.ty.span.lo() | |
2159 | } | |
2160 | } else { | |
2161 | param.attrs[0].span.lo() | |
2162 | } | |
2163 | } | |
2164 | ||
2165 | pub(crate) fn span_hi_for_param(context: &RewriteContext<'_>, param: &ast::Param) -> BytePos { | |
2166 | match param.ty.kind { | |
2167 | ast::TyKind::Infer if context.snippet(param.ty.span) == "_" => param.ty.span.hi(), | |
2168 | ast::TyKind::Infer if is_named_param(param) => param.pat.span.hi(), | |
2169 | _ => param.ty.span.hi(), | |
2170 | } | |
2171 | } | |
2172 | ||
2173 | pub(crate) fn is_named_param(param: &ast::Param) -> bool { | |
2174 | if let ast::PatKind::Ident(_, ident, _) = param.pat.kind { | |
2175 | ident.name != symbol::kw::Empty | |
2176 | } else { | |
2177 | true | |
2178 | } | |
2179 | } | |
2180 | ||
2181 | #[derive(Copy, Clone, Debug, PartialEq, Eq)] | |
2182 | pub(crate) enum FnBraceStyle { | |
2183 | SameLine, | |
2184 | NextLine, | |
2185 | None, | |
2186 | } | |
2187 | ||
2188 | // Return type is (result, force_new_line_for_brace) | |
2189 | fn rewrite_fn_base( | |
2190 | context: &RewriteContext<'_>, | |
2191 | indent: Indent, | |
2192 | ident: symbol::Ident, | |
2193 | fn_sig: &FnSig<'_>, | |
2194 | span: Span, | |
2195 | fn_brace_style: FnBraceStyle, | |
2196 | ) -> Option<(String, bool, bool)> { | |
2197 | let mut force_new_line_for_brace = false; | |
2198 | ||
2199 | let where_clause = &fn_sig.generics.where_clause; | |
2200 | ||
2201 | let mut result = String::with_capacity(1024); | |
2202 | result.push_str(&fn_sig.to_str(context)); | |
2203 | ||
2204 | // fn foo | |
2205 | result.push_str("fn "); | |
2206 | ||
2207 | // Generics. | |
2208 | let overhead = if let FnBraceStyle::SameLine = fn_brace_style { | |
2209 | // 4 = `() {` | |
2210 | 4 | |
2211 | } else { | |
2212 | // 2 = `()` | |
2213 | 2 | |
2214 | }; | |
2215 | let used_width = last_line_used_width(&result, indent.width()); | |
2216 | let one_line_budget = context.budget(used_width + overhead); | |
2217 | let shape = Shape { | |
2218 | width: one_line_budget, | |
2219 | indent, | |
2220 | offset: used_width, | |
2221 | }; | |
2222 | let fd = fn_sig.decl; | |
2223 | let generics_str = rewrite_generics( | |
2224 | context, | |
2225 | rewrite_ident(context, ident), | |
5e7ed085 | 2226 | &fn_sig.generics, |
f20569fa XL |
2227 | shape, |
2228 | )?; | |
2229 | result.push_str(&generics_str); | |
2230 | ||
2231 | let snuggle_angle_bracket = generics_str | |
2232 | .lines() | |
2233 | .last() | |
2234 | .map_or(false, |l| l.trim_start().len() == 1); | |
2235 | ||
2236 | // Note that the width and indent don't really matter, we'll re-layout the | |
2237 | // return type later anyway. | |
2238 | let ret_str = fd | |
2239 | .output | |
2240 | .rewrite(context, Shape::indented(indent, context.config))?; | |
2241 | ||
2242 | let multi_line_ret_str = ret_str.contains('\n'); | |
2243 | let ret_str_len = if multi_line_ret_str { 0 } else { ret_str.len() }; | |
2244 | ||
2245 | // Params. | |
2246 | let (one_line_budget, multi_line_budget, mut param_indent) = compute_budgets_for_params( | |
2247 | context, | |
2248 | &result, | |
2249 | indent, | |
2250 | ret_str_len, | |
2251 | fn_brace_style, | |
2252 | multi_line_ret_str, | |
2253 | )?; | |
2254 | ||
2255 | debug!( | |
2256 | "rewrite_fn_base: one_line_budget: {}, multi_line_budget: {}, param_indent: {:?}", | |
2257 | one_line_budget, multi_line_budget, param_indent | |
2258 | ); | |
2259 | ||
2260 | result.push('('); | |
2261 | // Check if vertical layout was forced. | |
2262 | if one_line_budget == 0 | |
2263 | && !snuggle_angle_bracket | |
2264 | && context.config.indent_style() == IndentStyle::Visual | |
2265 | { | |
2266 | result.push_str(¶m_indent.to_string_with_newline(context.config)); | |
2267 | } | |
2268 | ||
f20569fa XL |
2269 | let params_end = if fd.inputs.is_empty() { |
2270 | context | |
2271 | .snippet_provider | |
3c0e092e | 2272 | .span_after(mk_sp(fn_sig.generics.span.hi(), span.hi()), ")") |
f20569fa XL |
2273 | } else { |
2274 | let last_span = mk_sp(fd.inputs[fd.inputs.len() - 1].span().hi(), span.hi()); | |
2275 | context.snippet_provider.span_after(last_span, ")") | |
2276 | }; | |
2277 | let params_span = mk_sp( | |
2278 | context | |
2279 | .snippet_provider | |
3c0e092e | 2280 | .span_after(mk_sp(fn_sig.generics.span.hi(), span.hi()), "("), |
f20569fa XL |
2281 | params_end, |
2282 | ); | |
2283 | let param_str = rewrite_params( | |
2284 | context, | |
2285 | &fd.inputs, | |
2286 | one_line_budget, | |
2287 | multi_line_budget, | |
2288 | indent, | |
2289 | param_indent, | |
2290 | params_span, | |
2291 | fd.c_variadic(), | |
2292 | )?; | |
2293 | ||
2294 | let put_params_in_block = match context.config.indent_style() { | |
2295 | IndentStyle::Block => param_str.contains('\n') || param_str.len() > one_line_budget, | |
2296 | _ => false, | |
2297 | } && !fd.inputs.is_empty(); | |
2298 | ||
2299 | let mut params_last_line_contains_comment = false; | |
2300 | let mut no_params_and_over_max_width = false; | |
2301 | ||
2302 | if put_params_in_block { | |
2303 | param_indent = indent.block_indent(context.config); | |
2304 | result.push_str(¶m_indent.to_string_with_newline(context.config)); | |
2305 | result.push_str(¶m_str); | |
2306 | result.push_str(&indent.to_string_with_newline(context.config)); | |
2307 | result.push(')'); | |
2308 | } else { | |
2309 | result.push_str(¶m_str); | |
2310 | let used_width = last_line_used_width(&result, indent.width()) + first_line_width(&ret_str); | |
2311 | // Put the closing brace on the next line if it overflows the max width. | |
2312 | // 1 = `)` | |
2313 | let closing_paren_overflow_max_width = | |
2314 | fd.inputs.is_empty() && used_width + 1 > context.config.max_width(); | |
2315 | // If the last line of params contains comment, we cannot put the closing paren | |
2316 | // on the same line. | |
2317 | params_last_line_contains_comment = param_str | |
2318 | .lines() | |
2319 | .last() | |
2320 | .map_or(false, |last_line| last_line.contains("//")); | |
2321 | ||
2322 | if context.config.version() == Version::Two { | |
2323 | if closing_paren_overflow_max_width { | |
2324 | result.push(')'); | |
2325 | result.push_str(&indent.to_string_with_newline(context.config)); | |
2326 | no_params_and_over_max_width = true; | |
2327 | } else if params_last_line_contains_comment { | |
2328 | result.push_str(&indent.to_string_with_newline(context.config)); | |
2329 | result.push(')'); | |
2330 | no_params_and_over_max_width = true; | |
2331 | } else { | |
2332 | result.push(')'); | |
2333 | } | |
2334 | } else { | |
2335 | if closing_paren_overflow_max_width || params_last_line_contains_comment { | |
2336 | result.push_str(&indent.to_string_with_newline(context.config)); | |
2337 | } | |
2338 | result.push(')'); | |
2339 | } | |
2340 | } | |
2341 | ||
2342 | // Return type. | |
2343 | if let ast::FnRetTy::Ty(..) = fd.output { | |
2344 | let ret_should_indent = match context.config.indent_style() { | |
2345 | // If our params are block layout then we surely must have space. | |
2346 | IndentStyle::Block if put_params_in_block || fd.inputs.is_empty() => false, | |
2347 | _ if params_last_line_contains_comment => false, | |
2348 | _ if result.contains('\n') || multi_line_ret_str => true, | |
2349 | _ => { | |
2350 | // If the return type would push over the max width, then put the return type on | |
2351 | // a new line. With the +1 for the signature length an additional space between | |
2352 | // the closing parenthesis of the param and the arrow '->' is considered. | |
2353 | let mut sig_length = result.len() + indent.width() + ret_str_len + 1; | |
2354 | ||
2355 | // If there is no where-clause, take into account the space after the return type | |
2356 | // and the brace. | |
2357 | if where_clause.predicates.is_empty() { | |
2358 | sig_length += 2; | |
2359 | } | |
2360 | ||
2361 | sig_length > context.config.max_width() | |
2362 | } | |
2363 | }; | |
2364 | let ret_shape = if ret_should_indent { | |
2365 | if context.config.version() == Version::One | |
2366 | || context.config.indent_style() == IndentStyle::Visual | |
2367 | { | |
2368 | let indent = if param_str.is_empty() { | |
2369 | // Aligning with non-existent params looks silly. | |
2370 | force_new_line_for_brace = true; | |
2371 | indent + 4 | |
2372 | } else { | |
2373 | // FIXME: we might want to check that using the param indent | |
2374 | // doesn't blow our budget, and if it does, then fallback to | |
2375 | // the where-clause indent. | |
2376 | param_indent | |
2377 | }; | |
2378 | ||
2379 | result.push_str(&indent.to_string_with_newline(context.config)); | |
2380 | Shape::indented(indent, context.config) | |
2381 | } else { | |
2382 | let mut ret_shape = Shape::indented(indent, context.config); | |
2383 | if param_str.is_empty() { | |
2384 | // Aligning with non-existent params looks silly. | |
2385 | force_new_line_for_brace = true; | |
2386 | ret_shape = if context.use_block_indent() { | |
2387 | ret_shape.offset_left(4).unwrap_or(ret_shape) | |
2388 | } else { | |
2389 | ret_shape.indent = ret_shape.indent + 4; | |
2390 | ret_shape | |
2391 | }; | |
2392 | } | |
2393 | ||
2394 | result.push_str(&ret_shape.indent.to_string_with_newline(context.config)); | |
2395 | ret_shape | |
2396 | } | |
2397 | } else { | |
2398 | if context.config.version() == Version::Two { | |
2399 | if !param_str.is_empty() || !no_params_and_over_max_width { | |
2400 | result.push(' '); | |
2401 | } | |
2402 | } else { | |
2403 | result.push(' '); | |
2404 | } | |
2405 | ||
2406 | let ret_shape = Shape::indented(indent, context.config); | |
2407 | ret_shape | |
2408 | .offset_left(last_line_width(&result)) | |
2409 | .unwrap_or(ret_shape) | |
2410 | }; | |
2411 | ||
2412 | if multi_line_ret_str || ret_should_indent { | |
2413 | // Now that we know the proper indent and width, we need to | |
2414 | // re-layout the return type. | |
2415 | let ret_str = fd.output.rewrite(context, ret_shape)?; | |
2416 | result.push_str(&ret_str); | |
2417 | } else { | |
2418 | result.push_str(&ret_str); | |
2419 | } | |
2420 | ||
2421 | // Comment between return type and the end of the decl. | |
2422 | let snippet_lo = fd.output.span().hi(); | |
2423 | if where_clause.predicates.is_empty() { | |
2424 | let snippet_hi = span.hi(); | |
2425 | let snippet = context.snippet(mk_sp(snippet_lo, snippet_hi)); | |
2426 | // Try to preserve the layout of the original snippet. | |
2427 | let original_starts_with_newline = snippet | |
2428 | .find(|c| c != ' ') | |
2429 | .map_or(false, |i| starts_with_newline(&snippet[i..])); | |
2430 | let original_ends_with_newline = snippet | |
2431 | .rfind(|c| c != ' ') | |
2432 | .map_or(false, |i| snippet[i..].ends_with('\n')); | |
2433 | let snippet = snippet.trim(); | |
2434 | if !snippet.is_empty() { | |
2435 | result.push(if original_starts_with_newline { | |
2436 | '\n' | |
2437 | } else { | |
2438 | ' ' | |
2439 | }); | |
2440 | result.push_str(snippet); | |
2441 | if original_ends_with_newline { | |
2442 | force_new_line_for_brace = true; | |
2443 | } | |
2444 | } | |
2445 | } | |
2446 | } | |
2447 | ||
2448 | let pos_before_where = match fd.output { | |
2449 | ast::FnRetTy::Default(..) => params_span.hi(), | |
2450 | ast::FnRetTy::Ty(ref ty) => ty.span.hi(), | |
2451 | }; | |
2452 | ||
2453 | let is_params_multi_lined = param_str.contains('\n'); | |
2454 | ||
2455 | let space = if put_params_in_block && ret_str.is_empty() { | |
2456 | WhereClauseSpace::Space | |
2457 | } else { | |
2458 | WhereClauseSpace::Newline | |
2459 | }; | |
2460 | let mut option = WhereClauseOption::new(fn_brace_style == FnBraceStyle::None, space); | |
2461 | if is_params_multi_lined { | |
2462 | option.veto_single_line(); | |
2463 | } | |
2464 | let where_clause_str = rewrite_where_clause( | |
2465 | context, | |
5e7ed085 FG |
2466 | &where_clause.predicates, |
2467 | where_clause.span, | |
f20569fa XL |
2468 | context.config.brace_style(), |
2469 | Shape::indented(indent, context.config), | |
2470 | true, | |
2471 | "{", | |
2472 | Some(span.hi()), | |
2473 | pos_before_where, | |
2474 | option, | |
2475 | )?; | |
2476 | // If there are neither where-clause nor return type, we may be missing comments between | |
2477 | // params and `{`. | |
2478 | if where_clause_str.is_empty() { | |
2479 | if let ast::FnRetTy::Default(ret_span) = fd.output { | |
2480 | match recover_missing_comment_in_span( | |
2481 | mk_sp(params_span.hi(), ret_span.hi()), | |
2482 | shape, | |
2483 | context, | |
2484 | last_line_width(&result), | |
2485 | ) { | |
2486 | Some(ref missing_comment) if !missing_comment.is_empty() => { | |
2487 | result.push_str(missing_comment); | |
2488 | force_new_line_for_brace = true; | |
2489 | } | |
2490 | _ => (), | |
2491 | } | |
2492 | } | |
2493 | } | |
2494 | ||
2495 | result.push_str(&where_clause_str); | |
2496 | ||
2497 | let ends_with_comment = last_line_contains_single_line_comment(&result); | |
2498 | force_new_line_for_brace |= ends_with_comment; | |
2499 | force_new_line_for_brace |= | |
2500 | is_params_multi_lined && context.config.where_single_line() && !where_clause_str.is_empty(); | |
2501 | Some((result, ends_with_comment, force_new_line_for_brace)) | |
2502 | } | |
2503 | ||
2504 | /// Kind of spaces to put before `where`. | |
2505 | #[derive(Copy, Clone)] | |
2506 | enum WhereClauseSpace { | |
2507 | /// A single space. | |
2508 | Space, | |
2509 | /// A new line. | |
2510 | Newline, | |
2511 | /// Nothing. | |
2512 | None, | |
2513 | } | |
2514 | ||
2515 | #[derive(Copy, Clone)] | |
2516 | struct WhereClauseOption { | |
2517 | suppress_comma: bool, // Force no trailing comma | |
2518 | snuggle: WhereClauseSpace, | |
2519 | allow_single_line: bool, // Try single line where-clause instead of vertical layout | |
2520 | veto_single_line: bool, // Disallow a single-line where-clause. | |
2521 | } | |
2522 | ||
2523 | impl WhereClauseOption { | |
2524 | fn new(suppress_comma: bool, snuggle: WhereClauseSpace) -> WhereClauseOption { | |
2525 | WhereClauseOption { | |
2526 | suppress_comma, | |
2527 | snuggle, | |
2528 | allow_single_line: false, | |
2529 | veto_single_line: false, | |
2530 | } | |
2531 | } | |
2532 | ||
2533 | fn snuggled(current: &str) -> WhereClauseOption { | |
2534 | WhereClauseOption { | |
2535 | suppress_comma: false, | |
2536 | snuggle: if last_line_width(current) == 1 { | |
2537 | WhereClauseSpace::Space | |
2538 | } else { | |
2539 | WhereClauseSpace::Newline | |
2540 | }, | |
2541 | allow_single_line: false, | |
2542 | veto_single_line: false, | |
2543 | } | |
2544 | } | |
2545 | ||
2546 | fn suppress_comma(&mut self) { | |
2547 | self.suppress_comma = true | |
2548 | } | |
2549 | ||
2550 | fn allow_single_line(&mut self) { | |
2551 | self.allow_single_line = true | |
2552 | } | |
2553 | ||
2554 | fn snuggle(&mut self) { | |
2555 | self.snuggle = WhereClauseSpace::Space | |
2556 | } | |
2557 | ||
2558 | fn veto_single_line(&mut self) { | |
2559 | self.veto_single_line = true; | |
2560 | } | |
2561 | } | |
2562 | ||
2563 | fn rewrite_params( | |
2564 | context: &RewriteContext<'_>, | |
2565 | params: &[ast::Param], | |
2566 | one_line_budget: usize, | |
2567 | multi_line_budget: usize, | |
2568 | indent: Indent, | |
2569 | param_indent: Indent, | |
2570 | span: Span, | |
2571 | variadic: bool, | |
2572 | ) -> Option<String> { | |
2573 | if params.is_empty() { | |
2574 | let comment = context | |
2575 | .snippet(mk_sp( | |
2576 | span.lo(), | |
2577 | // to remove ')' | |
2578 | span.hi() - BytePos(1), | |
2579 | )) | |
2580 | .trim(); | |
2581 | return Some(comment.to_owned()); | |
2582 | } | |
2583 | let param_items: Vec<_> = itemize_list( | |
2584 | context.snippet_provider, | |
2585 | params.iter(), | |
2586 | ")", | |
2587 | ",", | |
2588 | |param| span_lo_for_param(param), | |
2589 | |param| param.ty.span.hi(), | |
2590 | |param| { | |
2591 | param | |
2592 | .rewrite(context, Shape::legacy(multi_line_budget, param_indent)) | |
2593 | .or_else(|| Some(context.snippet(param.span()).to_owned())) | |
2594 | }, | |
2595 | span.lo(), | |
2596 | span.hi(), | |
2597 | false, | |
2598 | ) | |
2599 | .collect(); | |
2600 | ||
2601 | let tactic = definitive_tactic( | |
2602 | ¶m_items, | |
2603 | context | |
2604 | .config | |
2605 | .fn_args_layout() | |
2606 | .to_list_tactic(param_items.len()), | |
2607 | Separator::Comma, | |
2608 | one_line_budget, | |
2609 | ); | |
2610 | let budget = match tactic { | |
2611 | DefinitiveListTactic::Horizontal => one_line_budget, | |
2612 | _ => multi_line_budget, | |
2613 | }; | |
2614 | let indent = match context.config.indent_style() { | |
2615 | IndentStyle::Block => indent.block_indent(context.config), | |
2616 | IndentStyle::Visual => param_indent, | |
2617 | }; | |
2618 | let trailing_separator = if variadic { | |
2619 | SeparatorTactic::Never | |
2620 | } else { | |
2621 | match context.config.indent_style() { | |
2622 | IndentStyle::Block => context.config.trailing_comma(), | |
2623 | IndentStyle::Visual => SeparatorTactic::Never, | |
2624 | } | |
2625 | }; | |
2626 | let fmt = ListFormatting::new(Shape::legacy(budget, indent), context.config) | |
2627 | .tactic(tactic) | |
2628 | .trailing_separator(trailing_separator) | |
2629 | .ends_with_newline(tactic.ends_with_newline(context.config.indent_style())) | |
2630 | .preserve_newline(true); | |
2631 | write_list(¶m_items, &fmt) | |
2632 | } | |
2633 | ||
2634 | fn compute_budgets_for_params( | |
2635 | context: &RewriteContext<'_>, | |
2636 | result: &str, | |
2637 | indent: Indent, | |
2638 | ret_str_len: usize, | |
2639 | fn_brace_style: FnBraceStyle, | |
2640 | force_vertical_layout: bool, | |
2641 | ) -> Option<(usize, usize, Indent)> { | |
2642 | debug!( | |
2643 | "compute_budgets_for_params {} {:?}, {}, {:?}", | |
2644 | result.len(), | |
2645 | indent, | |
2646 | ret_str_len, | |
2647 | fn_brace_style, | |
2648 | ); | |
2649 | // Try keeping everything on the same line. | |
2650 | if !result.contains('\n') && !force_vertical_layout { | |
2651 | // 2 = `()`, 3 = `() `, space is before ret_string. | |
2652 | let overhead = if ret_str_len == 0 { 2 } else { 3 }; | |
2653 | let mut used_space = indent.width() + result.len() + ret_str_len + overhead; | |
2654 | match fn_brace_style { | |
2655 | FnBraceStyle::None => used_space += 1, // 1 = `;` | |
2656 | FnBraceStyle::SameLine => used_space += 2, // 2 = `{}` | |
2657 | FnBraceStyle::NextLine => (), | |
2658 | } | |
2659 | let one_line_budget = context.budget(used_space); | |
2660 | ||
2661 | if one_line_budget > 0 { | |
2662 | // 4 = "() {".len() | |
2663 | let (indent, multi_line_budget) = match context.config.indent_style() { | |
2664 | IndentStyle::Block => { | |
2665 | let indent = indent.block_indent(context.config); | |
2666 | (indent, context.budget(indent.width() + 1)) | |
2667 | } | |
2668 | IndentStyle::Visual => { | |
2669 | let indent = indent + result.len() + 1; | |
2670 | let multi_line_overhead = match fn_brace_style { | |
2671 | FnBraceStyle::SameLine => 4, | |
2672 | _ => 2, | |
2673 | } + indent.width(); | |
2674 | (indent, context.budget(multi_line_overhead)) | |
2675 | } | |
2676 | }; | |
2677 | ||
2678 | return Some((one_line_budget, multi_line_budget, indent)); | |
2679 | } | |
2680 | } | |
2681 | ||
2682 | // Didn't work. we must force vertical layout and put params on a newline. | |
2683 | let new_indent = indent.block_indent(context.config); | |
2684 | let used_space = match context.config.indent_style() { | |
2685 | // 1 = `,` | |
2686 | IndentStyle::Block => new_indent.width() + 1, | |
2687 | // Account for `)` and possibly ` {`. | |
2688 | IndentStyle::Visual => new_indent.width() + if ret_str_len == 0 { 1 } else { 3 }, | |
2689 | }; | |
2690 | Some((0, context.budget(used_space), new_indent)) | |
2691 | } | |
2692 | ||
2693 | fn newline_for_brace(config: &Config, where_clause: &ast::WhereClause) -> FnBraceStyle { | |
2694 | let predicate_count = where_clause.predicates.len(); | |
2695 | ||
2696 | if config.where_single_line() && predicate_count == 1 { | |
2697 | return FnBraceStyle::SameLine; | |
2698 | } | |
2699 | let brace_style = config.brace_style(); | |
2700 | ||
2701 | let use_next_line = brace_style == BraceStyle::AlwaysNextLine | |
2702 | || (brace_style == BraceStyle::SameLineWhere && predicate_count > 0); | |
2703 | if use_next_line { | |
2704 | FnBraceStyle::NextLine | |
2705 | } else { | |
2706 | FnBraceStyle::SameLine | |
2707 | } | |
2708 | } | |
2709 | ||
2710 | fn rewrite_generics( | |
2711 | context: &RewriteContext<'_>, | |
2712 | ident: &str, | |
2713 | generics: &ast::Generics, | |
2714 | shape: Shape, | |
2715 | ) -> Option<String> { | |
2716 | // FIXME: convert bounds to where-clauses where they get too big or if | |
2717 | // there is a where-clause at all. | |
2718 | ||
2719 | if generics.params.is_empty() { | |
2720 | return Some(ident.to_owned()); | |
2721 | } | |
2722 | ||
2723 | let params = generics.params.iter(); | |
2724 | overflow::rewrite_with_angle_brackets(context, ident, params, shape, generics.span) | |
2725 | } | |
2726 | ||
2727 | fn generics_shape_from_config(config: &Config, shape: Shape, offset: usize) -> Option<Shape> { | |
2728 | match config.indent_style() { | |
2729 | IndentStyle::Visual => shape.visual_indent(1 + offset).sub_width(offset + 2), | |
2730 | IndentStyle::Block => { | |
2731 | // 1 = "," | |
2732 | shape | |
2733 | .block() | |
2734 | .block_indent(config.tab_spaces()) | |
2735 | .with_max_width(config) | |
2736 | .sub_width(1) | |
2737 | } | |
2738 | } | |
2739 | } | |
2740 | ||
2741 | fn rewrite_where_clause_rfc_style( | |
2742 | context: &RewriteContext<'_>, | |
5e7ed085 FG |
2743 | predicates: &[ast::WherePredicate], |
2744 | where_span: Span, | |
f20569fa XL |
2745 | shape: Shape, |
2746 | terminator: &str, | |
2747 | span_end: Option<BytePos>, | |
2748 | span_end_before_where: BytePos, | |
2749 | where_clause_option: WhereClauseOption, | |
2750 | ) -> Option<String> { | |
2751 | let (where_keyword, allow_single_line) = rewrite_where_keyword( | |
2752 | context, | |
5e7ed085 FG |
2753 | predicates, |
2754 | where_span, | |
f20569fa XL |
2755 | shape, |
2756 | span_end_before_where, | |
2757 | where_clause_option, | |
2758 | )?; | |
2759 | ||
2760 | // 1 = `,` | |
2761 | let clause_shape = shape | |
2762 | .block() | |
2763 | .with_max_width(context.config) | |
2764 | .block_left(context.config.tab_spaces())? | |
2765 | .sub_width(1)?; | |
2766 | let force_single_line = context.config.where_single_line() | |
5e7ed085 | 2767 | && predicates.len() == 1 |
f20569fa XL |
2768 | && !where_clause_option.veto_single_line; |
2769 | ||
2770 | let preds_str = rewrite_bounds_on_where_clause( | |
2771 | context, | |
5e7ed085 | 2772 | predicates, |
f20569fa XL |
2773 | clause_shape, |
2774 | terminator, | |
2775 | span_end, | |
2776 | where_clause_option, | |
2777 | force_single_line, | |
2778 | )?; | |
2779 | ||
2780 | // 6 = `where ` | |
2781 | let clause_sep = | |
2782 | if allow_single_line && !preds_str.contains('\n') && 6 + preds_str.len() <= shape.width | |
2783 | || force_single_line | |
2784 | { | |
2785 | Cow::from(" ") | |
2786 | } else { | |
2787 | clause_shape.indent.to_string_with_newline(context.config) | |
2788 | }; | |
2789 | ||
2790 | Some(format!("{}{}{}", where_keyword, clause_sep, preds_str)) | |
2791 | } | |
2792 | ||
2793 | /// Rewrite `where` and comment around it. | |
2794 | fn rewrite_where_keyword( | |
2795 | context: &RewriteContext<'_>, | |
5e7ed085 FG |
2796 | predicates: &[ast::WherePredicate], |
2797 | where_span: Span, | |
f20569fa XL |
2798 | shape: Shape, |
2799 | span_end_before_where: BytePos, | |
2800 | where_clause_option: WhereClauseOption, | |
2801 | ) -> Option<(String, bool)> { | |
2802 | let block_shape = shape.block().with_max_width(context.config); | |
2803 | // 1 = `,` | |
2804 | let clause_shape = block_shape | |
2805 | .block_left(context.config.tab_spaces())? | |
2806 | .sub_width(1)?; | |
2807 | ||
2808 | let comment_separator = |comment: &str, shape: Shape| { | |
2809 | if comment.is_empty() { | |
2810 | Cow::from("") | |
2811 | } else { | |
2812 | shape.indent.to_string_with_newline(context.config) | |
2813 | } | |
2814 | }; | |
2815 | ||
2816 | let (span_before, span_after) = | |
5e7ed085 | 2817 | missing_span_before_after_where(span_end_before_where, predicates, where_span); |
f20569fa XL |
2818 | let (comment_before, comment_after) = |
2819 | rewrite_comments_before_after_where(context, span_before, span_after, shape)?; | |
2820 | ||
2821 | let starting_newline = match where_clause_option.snuggle { | |
2822 | WhereClauseSpace::Space if comment_before.is_empty() => Cow::from(" "), | |
2823 | WhereClauseSpace::None => Cow::from(""), | |
2824 | _ => block_shape.indent.to_string_with_newline(context.config), | |
2825 | }; | |
2826 | ||
2827 | let newline_before_where = comment_separator(&comment_before, shape); | |
2828 | let newline_after_where = comment_separator(&comment_after, clause_shape); | |
2829 | let result = format!( | |
2830 | "{}{}{}where{}{}", | |
2831 | starting_newline, comment_before, newline_before_where, newline_after_where, comment_after | |
2832 | ); | |
2833 | let allow_single_line = where_clause_option.allow_single_line | |
2834 | && comment_before.is_empty() | |
2835 | && comment_after.is_empty(); | |
2836 | ||
2837 | Some((result, allow_single_line)) | |
2838 | } | |
2839 | ||
2840 | /// Rewrite bounds on a where clause. | |
2841 | fn rewrite_bounds_on_where_clause( | |
2842 | context: &RewriteContext<'_>, | |
5e7ed085 | 2843 | predicates: &[ast::WherePredicate], |
f20569fa XL |
2844 | shape: Shape, |
2845 | terminator: &str, | |
2846 | span_end: Option<BytePos>, | |
2847 | where_clause_option: WhereClauseOption, | |
2848 | force_single_line: bool, | |
2849 | ) -> Option<String> { | |
5e7ed085 | 2850 | let span_start = predicates[0].span().lo(); |
f20569fa XL |
2851 | // If we don't have the start of the next span, then use the end of the |
2852 | // predicates, but that means we miss comments. | |
5e7ed085 FG |
2853 | let len = predicates.len(); |
2854 | let end_of_preds = predicates[len - 1].span().hi(); | |
f20569fa XL |
2855 | let span_end = span_end.unwrap_or(end_of_preds); |
2856 | let items = itemize_list( | |
2857 | context.snippet_provider, | |
5e7ed085 | 2858 | predicates.iter(), |
f20569fa XL |
2859 | terminator, |
2860 | ",", | |
2861 | |pred| pred.span().lo(), | |
2862 | |pred| pred.span().hi(), | |
2863 | |pred| pred.rewrite(context, shape), | |
2864 | span_start, | |
2865 | span_end, | |
2866 | false, | |
2867 | ); | |
2868 | let comma_tactic = if where_clause_option.suppress_comma || force_single_line { | |
2869 | SeparatorTactic::Never | |
2870 | } else { | |
2871 | context.config.trailing_comma() | |
2872 | }; | |
2873 | ||
2874 | // shape should be vertical only and only if we have `force_single_line` option enabled | |
2875 | // and the number of items of the where-clause is equal to 1 | |
2876 | let shape_tactic = if force_single_line { | |
2877 | DefinitiveListTactic::Horizontal | |
2878 | } else { | |
2879 | DefinitiveListTactic::Vertical | |
2880 | }; | |
2881 | ||
2882 | let fmt = ListFormatting::new(shape, context.config) | |
2883 | .tactic(shape_tactic) | |
2884 | .trailing_separator(comma_tactic) | |
2885 | .preserve_newline(true); | |
2886 | write_list(&items.collect::<Vec<_>>(), &fmt) | |
2887 | } | |
2888 | ||
2889 | fn rewrite_where_clause( | |
2890 | context: &RewriteContext<'_>, | |
5e7ed085 FG |
2891 | predicates: &[ast::WherePredicate], |
2892 | where_span: Span, | |
f20569fa XL |
2893 | brace_style: BraceStyle, |
2894 | shape: Shape, | |
2895 | on_new_line: bool, | |
2896 | terminator: &str, | |
2897 | span_end: Option<BytePos>, | |
2898 | span_end_before_where: BytePos, | |
2899 | where_clause_option: WhereClauseOption, | |
2900 | ) -> Option<String> { | |
5e7ed085 | 2901 | if predicates.is_empty() { |
f20569fa XL |
2902 | return Some(String::new()); |
2903 | } | |
2904 | ||
2905 | if context.config.indent_style() == IndentStyle::Block { | |
2906 | return rewrite_where_clause_rfc_style( | |
2907 | context, | |
5e7ed085 FG |
2908 | predicates, |
2909 | where_span, | |
f20569fa XL |
2910 | shape, |
2911 | terminator, | |
2912 | span_end, | |
2913 | span_end_before_where, | |
2914 | where_clause_option, | |
2915 | ); | |
2916 | } | |
2917 | ||
2918 | let extra_indent = Indent::new(context.config.tab_spaces(), 0); | |
2919 | ||
2920 | let offset = match context.config.indent_style() { | |
2921 | IndentStyle::Block => shape.indent + extra_indent.block_indent(context.config), | |
2922 | // 6 = "where ".len() | |
2923 | IndentStyle::Visual => shape.indent + extra_indent + 6, | |
2924 | }; | |
2925 | // FIXME: if indent_style != Visual, then the budgets below might | |
2926 | // be out by a char or two. | |
2927 | ||
2928 | let budget = context.config.max_width() - offset.width(); | |
5e7ed085 | 2929 | let span_start = predicates[0].span().lo(); |
f20569fa XL |
2930 | // If we don't have the start of the next span, then use the end of the |
2931 | // predicates, but that means we miss comments. | |
5e7ed085 FG |
2932 | let len = predicates.len(); |
2933 | let end_of_preds = predicates[len - 1].span().hi(); | |
f20569fa XL |
2934 | let span_end = span_end.unwrap_or(end_of_preds); |
2935 | let items = itemize_list( | |
2936 | context.snippet_provider, | |
5e7ed085 | 2937 | predicates.iter(), |
f20569fa XL |
2938 | terminator, |
2939 | ",", | |
2940 | |pred| pred.span().lo(), | |
2941 | |pred| pred.span().hi(), | |
2942 | |pred| pred.rewrite(context, Shape::legacy(budget, offset)), | |
2943 | span_start, | |
2944 | span_end, | |
2945 | false, | |
2946 | ); | |
2947 | let item_vec = items.collect::<Vec<_>>(); | |
2948 | // FIXME: we don't need to collect here | |
2949 | let tactic = definitive_tactic(&item_vec, ListTactic::Vertical, Separator::Comma, budget); | |
2950 | ||
2951 | let mut comma_tactic = context.config.trailing_comma(); | |
2952 | // Kind of a hack because we don't usually have trailing commas in where-clauses. | |
2953 | if comma_tactic == SeparatorTactic::Vertical || where_clause_option.suppress_comma { | |
2954 | comma_tactic = SeparatorTactic::Never; | |
2955 | } | |
2956 | ||
2957 | let fmt = ListFormatting::new(Shape::legacy(budget, offset), context.config) | |
2958 | .tactic(tactic) | |
2959 | .trailing_separator(comma_tactic) | |
2960 | .ends_with_newline(tactic.ends_with_newline(context.config.indent_style())) | |
2961 | .preserve_newline(true); | |
2962 | let preds_str = write_list(&item_vec, &fmt)?; | |
2963 | ||
2964 | let end_length = if terminator == "{" { | |
2965 | // If the brace is on the next line we don't need to count it otherwise it needs two | |
2966 | // characters " {" | |
2967 | match brace_style { | |
2968 | BraceStyle::AlwaysNextLine | BraceStyle::SameLineWhere => 0, | |
2969 | BraceStyle::PreferSameLine => 2, | |
2970 | } | |
2971 | } else if terminator == "=" { | |
2972 | 2 | |
2973 | } else { | |
2974 | terminator.len() | |
2975 | }; | |
2976 | if on_new_line | |
2977 | || preds_str.contains('\n') | |
2978 | || shape.indent.width() + " where ".len() + preds_str.len() + end_length > shape.width | |
2979 | { | |
2980 | Some(format!( | |
2981 | "\n{}where {}", | |
2982 | (shape.indent + extra_indent).to_string(context.config), | |
2983 | preds_str | |
2984 | )) | |
2985 | } else { | |
2986 | Some(format!(" where {}", preds_str)) | |
2987 | } | |
2988 | } | |
2989 | ||
2990 | fn missing_span_before_after_where( | |
2991 | before_item_span_end: BytePos, | |
5e7ed085 FG |
2992 | predicates: &[ast::WherePredicate], |
2993 | where_span: Span, | |
f20569fa | 2994 | ) -> (Span, Span) { |
5e7ed085 | 2995 | let missing_span_before = mk_sp(before_item_span_end, where_span.lo()); |
f20569fa | 2996 | // 5 = `where` |
5e7ed085 FG |
2997 | let pos_after_where = where_span.lo() + BytePos(5); |
2998 | let missing_span_after = mk_sp(pos_after_where, predicates[0].span().lo()); | |
f20569fa XL |
2999 | (missing_span_before, missing_span_after) |
3000 | } | |
3001 | ||
3002 | fn rewrite_comments_before_after_where( | |
3003 | context: &RewriteContext<'_>, | |
3004 | span_before_where: Span, | |
3005 | span_after_where: Span, | |
3006 | shape: Shape, | |
3007 | ) -> Option<(String, String)> { | |
3008 | let before_comment = rewrite_missing_comment(span_before_where, shape, context)?; | |
3009 | let after_comment = rewrite_missing_comment( | |
3010 | span_after_where, | |
3011 | shape.block_indent(context.config.tab_spaces()), | |
3012 | context, | |
3013 | )?; | |
3014 | Some((before_comment, after_comment)) | |
3015 | } | |
3016 | ||
3017 | fn format_header( | |
3018 | context: &RewriteContext<'_>, | |
3019 | item_name: &str, | |
3020 | ident: symbol::Ident, | |
3021 | vis: &ast::Visibility, | |
3022 | offset: Indent, | |
3023 | ) -> String { | |
3024 | let mut result = String::with_capacity(128); | |
3025 | let shape = Shape::indented(offset, context.config); | |
3026 | ||
3c0e092e | 3027 | result.push_str(format_visibility(context, vis).trim()); |
f20569fa XL |
3028 | |
3029 | // Check for a missing comment between the visibility and the item name. | |
3030 | let after_vis = vis.span.hi(); | |
3031 | if let Some(before_item_name) = context | |
3032 | .snippet_provider | |
3033 | .opt_span_before(mk_sp(vis.span.lo(), ident.span.hi()), item_name.trim()) | |
3034 | { | |
3035 | let missing_span = mk_sp(after_vis, before_item_name); | |
3036 | if let Some(result_with_comment) = combine_strs_with_missing_comments( | |
3037 | context, | |
3038 | &result, | |
3039 | item_name, | |
3040 | missing_span, | |
3041 | shape, | |
3042 | /* allow_extend */ true, | |
3043 | ) { | |
3044 | result = result_with_comment; | |
3045 | } | |
3046 | } | |
3047 | ||
3c0e092e | 3048 | result.push_str(rewrite_ident(context, ident)); |
f20569fa XL |
3049 | |
3050 | result | |
3051 | } | |
3052 | ||
3053 | #[derive(PartialEq, Eq, Clone, Copy)] | |
3054 | enum BracePos { | |
3055 | None, | |
3056 | Auto, | |
3057 | ForceSameLine, | |
3058 | } | |
3059 | ||
3060 | fn format_generics( | |
3061 | context: &RewriteContext<'_>, | |
3062 | generics: &ast::Generics, | |
3063 | brace_style: BraceStyle, | |
3064 | brace_pos: BracePos, | |
3065 | offset: Indent, | |
3066 | span: Span, | |
3067 | used_width: usize, | |
3068 | ) -> Option<String> { | |
3069 | let shape = Shape::legacy(context.budget(used_width + offset.width()), offset); | |
3070 | let mut result = rewrite_generics(context, "", generics, shape)?; | |
3071 | ||
3072 | // If the generics are not parameterized then generics.span.hi() == 0, | |
3073 | // so we use span.lo(), which is the position after `struct Foo`. | |
3074 | let span_end_before_where = if !generics.params.is_empty() { | |
3075 | generics.span.hi() | |
3076 | } else { | |
3077 | span.lo() | |
3078 | }; | |
3079 | let (same_line_brace, missed_comments) = if !generics.where_clause.predicates.is_empty() { | |
3080 | let budget = context.budget(last_line_used_width(&result, offset.width())); | |
3081 | let mut option = WhereClauseOption::snuggled(&result); | |
3082 | if brace_pos == BracePos::None { | |
3083 | option.suppress_comma = true; | |
3084 | } | |
3085 | let where_clause_str = rewrite_where_clause( | |
3086 | context, | |
5e7ed085 FG |
3087 | &generics.where_clause.predicates, |
3088 | generics.where_clause.span, | |
f20569fa XL |
3089 | brace_style, |
3090 | Shape::legacy(budget, offset.block_only()), | |
3091 | true, | |
3092 | "{", | |
3093 | Some(span.hi()), | |
3094 | span_end_before_where, | |
3095 | option, | |
3096 | )?; | |
3097 | result.push_str(&where_clause_str); | |
3098 | ( | |
3099 | brace_pos == BracePos::ForceSameLine || brace_style == BraceStyle::PreferSameLine, | |
3100 | // missed comments are taken care of in #rewrite_where_clause | |
3101 | None, | |
3102 | ) | |
3103 | } else { | |
3104 | ( | |
3105 | brace_pos == BracePos::ForceSameLine | |
3106 | || (result.contains('\n') && brace_style == BraceStyle::PreferSameLine | |
3107 | || brace_style != BraceStyle::AlwaysNextLine) | |
3108 | || trimmed_last_line_width(&result) == 1, | |
3109 | rewrite_missing_comment( | |
3110 | mk_sp( | |
3111 | span_end_before_where, | |
3112 | if brace_pos == BracePos::None { | |
3113 | span.hi() | |
3114 | } else { | |
3115 | context.snippet_provider.span_before(span, "{") | |
3116 | }, | |
3117 | ), | |
3118 | shape, | |
3119 | context, | |
3120 | ), | |
3121 | ) | |
3122 | }; | |
3123 | // add missing comments | |
3124 | let missed_line_comments = missed_comments | |
3125 | .filter(|missed_comments| !missed_comments.is_empty()) | |
3126 | .map_or(false, |missed_comments| { | |
3127 | let is_block = is_last_comment_block(&missed_comments); | |
3128 | let sep = if is_block { " " } else { "\n" }; | |
3129 | result.push_str(sep); | |
3130 | result.push_str(&missed_comments); | |
3131 | !is_block | |
3132 | }); | |
3133 | if brace_pos == BracePos::None { | |
3134 | return Some(result); | |
3135 | } | |
3136 | let total_used_width = last_line_used_width(&result, used_width); | |
3137 | let remaining_budget = context.budget(total_used_width); | |
3138 | // If the same line brace if forced, it indicates that we are rewriting an item with empty body, | |
3139 | // and hence we take the closer into account as well for one line budget. | |
3140 | // We assume that the closer has the same length as the opener. | |
3141 | let overhead = if brace_pos == BracePos::ForceSameLine { | |
3142 | // 3 = ` {}` | |
3143 | 3 | |
3144 | } else { | |
3145 | // 2 = ` {` | |
3146 | 2 | |
3147 | }; | |
3148 | let forbid_same_line_brace = missed_line_comments || overhead > remaining_budget; | |
3149 | if !forbid_same_line_brace && same_line_brace { | |
3150 | result.push(' '); | |
3151 | } else { | |
3152 | result.push('\n'); | |
3153 | result.push_str(&offset.block_only().to_string(context.config)); | |
3154 | } | |
3155 | result.push('{'); | |
3156 | ||
3157 | Some(result) | |
3158 | } | |
3159 | ||
3160 | impl Rewrite for ast::ForeignItem { | |
3161 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
3162 | let attrs_str = self.attrs.rewrite(context, shape)?; | |
3163 | // Drop semicolon or it will be interpreted as comment. | |
3164 | // FIXME: this may be a faulty span from libsyntax. | |
3165 | let span = mk_sp(self.span.lo(), self.span.hi() - BytePos(1)); | |
3166 | ||
3167 | let item_str = match self.kind { | |
3168 | ast::ForeignItemKind::Fn(ref fn_kind) => { | |
3c0e092e XL |
3169 | let ast::Fn { |
3170 | defaultness, | |
3171 | ref sig, | |
3172 | ref generics, | |
3173 | ref body, | |
3174 | } = **fn_kind; | |
3175 | if let Some(ref body) = body { | |
f20569fa XL |
3176 | let mut visitor = FmtVisitor::from_context(context); |
3177 | visitor.block_indent = shape.indent; | |
3178 | visitor.last_pos = self.span.lo(); | |
3179 | let inner_attrs = inner_attributes(&self.attrs); | |
3180 | let fn_ctxt = visit::FnCtxt::Foreign; | |
3181 | visitor.visit_fn( | |
04454e1e FG |
3182 | visit::FnKind::Fn( |
3183 | fn_ctxt, | |
3184 | self.ident, | |
3185 | sig, | |
3186 | &self.vis, | |
3187 | generics, | |
3188 | Some(body), | |
3189 | ), | |
3c0e092e | 3190 | &sig.decl, |
f20569fa XL |
3191 | self.span, |
3192 | defaultness, | |
3193 | Some(&inner_attrs), | |
3194 | ); | |
3195 | Some(visitor.buffer.to_owned()) | |
3196 | } else { | |
3197 | rewrite_fn_base( | |
3198 | context, | |
3199 | shape.indent, | |
3200 | self.ident, | |
a2a8927a | 3201 | &FnSig::from_method_sig(sig, generics, &self.vis), |
f20569fa XL |
3202 | span, |
3203 | FnBraceStyle::None, | |
3204 | ) | |
3205 | .map(|(s, _, _)| format!("{};", s)) | |
3206 | } | |
3207 | } | |
3208 | ast::ForeignItemKind::Static(ref ty, mutability, _) => { | |
3209 | // FIXME(#21): we're dropping potential comments in between the | |
3210 | // function kw here. | |
3211 | let vis = format_visibility(context, &self.vis); | |
3212 | let mut_str = format_mutability(mutability); | |
3213 | let prefix = format!( | |
3214 | "{}static {}{}:", | |
3215 | vis, | |
3216 | mut_str, | |
3217 | rewrite_ident(context, self.ident) | |
3218 | ); | |
3219 | // 1 = ; | |
a2a8927a XL |
3220 | rewrite_assign_rhs( |
3221 | context, | |
3222 | prefix, | |
3223 | &**ty, | |
3224 | &RhsAssignKind::Ty, | |
3225 | shape.sub_width(1)?, | |
3226 | ) | |
3227 | .map(|s| s + ";") | |
f20569fa | 3228 | } |
3c0e092e | 3229 | ast::ForeignItemKind::TyAlias(ref ty_alias) => { |
a2a8927a | 3230 | let (kind, span) = (&ItemVisitorKind::ForeignItem(self), self.span); |
3c0e092e | 3231 | rewrite_type_alias(ty_alias, context, shape.indent, kind, span) |
f20569fa XL |
3232 | } |
3233 | ast::ForeignItemKind::MacCall(ref mac) => { | |
3234 | rewrite_macro(mac, None, context, shape, MacroPosition::Item) | |
3235 | } | |
3236 | }?; | |
3237 | ||
3238 | let missing_span = if self.attrs.is_empty() { | |
3239 | mk_sp(self.span.lo(), self.span.lo()) | |
3240 | } else { | |
3241 | mk_sp(self.attrs[self.attrs.len() - 1].span.hi(), self.span.lo()) | |
3242 | }; | |
3243 | combine_strs_with_missing_comments( | |
3244 | context, | |
3245 | &attrs_str, | |
3246 | &item_str, | |
3247 | missing_span, | |
3248 | shape, | |
3249 | false, | |
3250 | ) | |
3251 | } | |
3252 | } | |
3253 | ||
3254 | /// Rewrite the attributes of an item. | |
3255 | fn rewrite_attrs( | |
3256 | context: &RewriteContext<'_>, | |
3257 | item: &ast::Item, | |
3258 | item_str: &str, | |
3259 | shape: Shape, | |
3260 | ) -> Option<String> { | |
3261 | let attrs = filter_inline_attrs(&item.attrs, item.span()); | |
3262 | let attrs_str = attrs.rewrite(context, shape)?; | |
3263 | ||
3264 | let missed_span = if attrs.is_empty() { | |
3265 | mk_sp(item.span.lo(), item.span.lo()) | |
3266 | } else { | |
3267 | mk_sp(attrs[attrs.len() - 1].span.hi(), item.span.lo()) | |
3268 | }; | |
3269 | ||
3270 | let allow_extend = if attrs.len() == 1 { | |
3271 | let line_len = attrs_str.len() + 1 + item_str.len(); | |
3272 | !attrs.first().unwrap().is_doc_comment() | |
3273 | && context.config.inline_attribute_width() >= line_len | |
3274 | } else { | |
3275 | false | |
3276 | }; | |
3277 | ||
3278 | combine_strs_with_missing_comments( | |
3279 | context, | |
3280 | &attrs_str, | |
3c0e092e | 3281 | item_str, |
f20569fa XL |
3282 | missed_span, |
3283 | shape, | |
3284 | allow_extend, | |
3285 | ) | |
3286 | } | |
3287 | ||
3288 | /// Rewrite an inline mod. | |
3289 | /// The given shape is used to format the mod's attributes. | |
3290 | pub(crate) fn rewrite_mod( | |
3291 | context: &RewriteContext<'_>, | |
3292 | item: &ast::Item, | |
3293 | attrs_shape: Shape, | |
3294 | ) -> Option<String> { | |
3295 | let mut result = String::with_capacity(32); | |
3296 | result.push_str(&*format_visibility(context, &item.vis)); | |
3297 | result.push_str("mod "); | |
3298 | result.push_str(rewrite_ident(context, item.ident)); | |
3299 | result.push(';'); | |
3300 | rewrite_attrs(context, item, &result, attrs_shape) | |
3301 | } | |
3302 | ||
3303 | /// Rewrite `extern crate foo;`. | |
3304 | /// The given shape is used to format the extern crate's attributes. | |
3305 | pub(crate) fn rewrite_extern_crate( | |
3306 | context: &RewriteContext<'_>, | |
3307 | item: &ast::Item, | |
3308 | attrs_shape: Shape, | |
3309 | ) -> Option<String> { | |
3310 | assert!(is_extern_crate(item)); | |
3311 | let new_str = context.snippet(item.span); | |
3312 | let item_str = if contains_comment(new_str) { | |
3313 | new_str.to_owned() | |
3314 | } else { | |
3315 | let no_whitespace = &new_str.split_whitespace().collect::<Vec<&str>>().join(" "); | |
3316 | String::from(&*Regex::new(r"\s;").unwrap().replace(no_whitespace, ";")) | |
3317 | }; | |
3318 | rewrite_attrs(context, item, &item_str, attrs_shape) | |
3319 | } | |
3320 | ||
3321 | /// Returns `true` for `mod foo;`, false for `mod foo { .. }`. | |
3322 | pub(crate) fn is_mod_decl(item: &ast::Item) -> bool { | |
94222f64 XL |
3323 | !matches!( |
3324 | item.kind, | |
3325 | ast::ItemKind::Mod(_, ast::ModKind::Loaded(_, ast::Inline::Yes, _)) | |
3326 | ) | |
f20569fa XL |
3327 | } |
3328 | ||
3329 | pub(crate) fn is_use_item(item: &ast::Item) -> bool { | |
94222f64 | 3330 | matches!(item.kind, ast::ItemKind::Use(_)) |
f20569fa XL |
3331 | } |
3332 | ||
3333 | pub(crate) fn is_extern_crate(item: &ast::Item) -> bool { | |
94222f64 | 3334 | matches!(item.kind, ast::ItemKind::ExternCrate(..)) |
f20569fa | 3335 | } |