Skip to main content

rustfmt_nightly/
items.rs

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