rustfmt_nightly/
stmt.rs

1use rustc_ast::ast;
2use rustc_span::Span;
3
4use crate::comment::recover_comment_removed;
5use crate::config::StyleEdition;
6use crate::expr::{ExprType, format_expr, is_simple_block};
7use crate::rewrite::{Rewrite, RewriteContext, RewriteError, RewriteErrorExt, RewriteResult};
8use crate::shape::Shape;
9use crate::source_map::LineRangeUtils;
10use crate::spanned::Spanned;
11use crate::utils::semicolon_for_stmt;
12
13pub(crate) struct Stmt<'a> {
14    inner: &'a ast::Stmt,
15    is_last: bool,
16}
17
18impl<'a> Spanned for Stmt<'a> {
19    fn span(&self) -> Span {
20        self.inner.span()
21    }
22}
23
24impl<'a> Stmt<'a> {
25    pub(crate) fn as_ast_node(&self) -> &ast::Stmt {
26        self.inner
27    }
28
29    pub(crate) fn to_item(&self) -> Option<&ast::Item> {
30        match self.inner.kind {
31            ast::StmtKind::Item(ref item) => Some(&**item),
32            _ => None,
33        }
34    }
35
36    pub(crate) fn from_simple_block(
37        context: &RewriteContext<'_>,
38        block: &'a ast::Block,
39        attrs: Option<&[ast::Attribute]>,
40    ) -> Option<Self> {
41        if is_simple_block(context, block, attrs) {
42            let inner = &block.stmts[0];
43            // Simple blocks only contain one expr and no stmts
44            let is_last = true;
45            Some(Stmt { inner, is_last })
46        } else {
47            None
48        }
49    }
50
51    pub(crate) fn from_ast_node(inner: &'a ast::Stmt, is_last: bool) -> Self {
52        Stmt { inner, is_last }
53    }
54
55    pub(crate) fn from_ast_nodes<I>(iter: I) -> Vec<Self>
56    where
57        I: Iterator<Item = &'a ast::Stmt>,
58    {
59        let mut result = vec![];
60        let mut iter = iter.peekable();
61        while iter.peek().is_some() {
62            result.push(Stmt {
63                inner: iter.next().unwrap(),
64                is_last: iter.peek().is_none(),
65            })
66        }
67        result
68    }
69
70    pub(crate) fn is_empty(&self) -> bool {
71        matches!(self.inner.kind, ast::StmtKind::Empty)
72    }
73
74    fn is_last_expr(&self) -> bool {
75        if !self.is_last {
76            return false;
77        }
78
79        match self.as_ast_node().kind {
80            ast::StmtKind::Expr(ref expr) => match expr.kind {
81                ast::ExprKind::Ret(..) | ast::ExprKind::Continue(..) | ast::ExprKind::Break(..) => {
82                    false
83                }
84                _ => true,
85            },
86            _ => false,
87        }
88    }
89}
90
91impl<'a> Rewrite for Stmt<'a> {
92    fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> {
93        self.rewrite_result(context, shape).ok()
94    }
95
96    fn rewrite_result(
97        &self,
98        context: &RewriteContext<'_>,
99        shape: Shape,
100    ) -> crate::rewrite::RewriteResult {
101        let expr_type =
102            if context.config.style_edition() >= StyleEdition::Edition2024 && self.is_last_expr() {
103                ExprType::SubExpression
104            } else {
105                ExprType::Statement
106            };
107        format_stmt(
108            context,
109            shape,
110            self.as_ast_node(),
111            expr_type,
112            self.is_last_expr(),
113        )
114    }
115}
116
117fn format_stmt(
118    context: &RewriteContext<'_>,
119    shape: Shape,
120    stmt: &ast::Stmt,
121    expr_type: ExprType,
122    is_last_expr: bool,
123) -> RewriteResult {
124    skip_out_of_file_lines_range_err!(context, stmt.span());
125
126    let result = match stmt.kind {
127        ast::StmtKind::Let(ref local) => local.rewrite_result(context, shape),
128        ast::StmtKind::Expr(ref ex) | ast::StmtKind::Semi(ref ex) => {
129            let suffix = if semicolon_for_stmt(context, stmt, is_last_expr) {
130                ";"
131            } else {
132                ""
133            };
134
135            let shape = shape
136                .sub_width(suffix.len())
137                .max_width_error(shape.width, ex.span())?;
138            format_expr(ex, expr_type, context, shape).map(|s| s + suffix)
139        }
140        ast::StmtKind::MacCall(..) | ast::StmtKind::Item(..) | ast::StmtKind::Empty => {
141            Err(RewriteError::Unknown)
142        }
143    };
144    result.map(|res| recover_comment_removed(res, stmt.span(), context))
145}