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