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