1use rustc_ast::{ast, token};
2use rustc_span::Span;
3
4use crate::config::IndentStyle;
5use crate::config::lists::*;
6use crate::rewrite::{Rewrite, RewriteContext, RewriteErrorExt, RewriteResult};
7use crate::shape::Shape;
8use crate::spanned::Spanned;
9use crate::utils::{
10 first_line_width, is_single_line, last_line_width, trimmed_last_line_width, wrap_str,
11};
12
13#[derive(Clone, Copy)]
15pub(crate) struct PairParts<'a> {
16 prefix: &'a str,
17 infix: &'a str,
18 suffix: &'a str,
19}
20
21impl<'a> PairParts<'a> {
22 pub(crate) const fn new(prefix: &'a str, infix: &'a str, suffix: &'a str) -> Self {
23 Self {
24 prefix,
25 infix,
26 suffix,
27 }
28 }
29 pub(crate) fn infix(infix: &'a str) -> PairParts<'a> {
30 PairParts {
31 prefix: "",
32 infix,
33 suffix: "",
34 }
35 }
36}
37
38pub(crate) fn rewrite_all_pairs(
42 expr: &ast::Expr,
43 shape: Shape,
44 context: &RewriteContext<'_>,
45) -> RewriteResult {
46 expr.flatten(context, shape)
47 .unknown_error()
48 .and_then(|list| {
49 if list.let_chain_count() > 0 && !list.can_rewrite_let_chain_single_line() {
50 rewrite_pairs_multiline(&list, shape, context)
51 } else {
52 rewrite_pairs_one_line(&list, shape, context)
54 .unknown_error()
55 .or_else(|_| rewrite_pairs_multiline(&list, shape, context))
56 }
57 })
58}
59
60fn rewrite_pairs_one_line<T: Rewrite>(
63 list: &PairList<'_, '_, T>,
64 shape: Shape,
65 context: &RewriteContext<'_>,
66) -> Option<String> {
67 assert!(list.list.len() >= 2, "Not a pair?");
68
69 let mut result = String::new();
70 let base_shape = shape.block();
71
72 for ((_, rewrite), s) in list.list.iter().zip(list.separators.iter()) {
73 if let Ok(rewrite) = rewrite {
74 if !is_single_line(rewrite) || result.len() > shape.width {
75 return None;
76 }
77
78 result.push_str(rewrite);
79 result.push(' ');
80 result.push_str(s);
81 result.push(' ');
82 } else {
83 return None;
84 }
85 }
86
87 let prefix_len = result.len();
88 let last = list.list.last()?.0;
89 let cur_shape = base_shape.offset_left(last_line_width(&result))?;
90 let last_rewrite = last.rewrite(context, cur_shape)?;
91 result.push_str(&last_rewrite);
92
93 if first_line_width(&result) > shape.width {
94 return None;
95 }
96
97 if !(is_single_line(&result) || last_rewrite.starts_with('{'))
100 && (last_rewrite.starts_with('(') || prefix_len > context.config.tab_spaces())
101 {
102 return None;
103 }
104
105 wrap_str(result, context.config.max_width(), shape)
106}
107
108fn rewrite_pairs_multiline<T: Rewrite>(
109 list: &PairList<'_, '_, T>,
110 shape: Shape,
111 context: &RewriteContext<'_>,
112) -> RewriteResult {
113 let rhs_offset = shape.rhs_overhead(context.config);
114 let nested_shape = (match context.config.indent_style() {
115 IndentStyle::Visual => shape.visual_indent(0),
116 IndentStyle::Block => shape.block_indent(context.config.tab_spaces()),
117 })
118 .with_max_width(context.config)
119 .sub_width(rhs_offset)
120 .max_width_error(shape.width, list.span)?;
121
122 let indent_str = nested_shape.indent.to_string_with_newline(context.config);
123 let mut result = String::new();
124
125 result.push_str(list.list[0].1.as_ref().map_err(|err| err.clone())?);
126
127 for ((e, default_rw), s) in list.list[1..].iter().zip(list.separators.iter()) {
128 let offset = if result.contains('\n') {
132 0
133 } else {
134 shape.used_width()
135 };
136 if last_line_width(&result) + offset <= nested_shape.used_width() {
137 if let Some(line_shape) =
139 shape.offset_left(s.len() + 2 + trimmed_last_line_width(&result))
140 {
141 if let Ok(rewrite) = e.rewrite_result(context, line_shape) {
142 result.push(' ');
143 result.push_str(s);
144 result.push(' ');
145 result.push_str(&rewrite);
146 continue;
147 }
148 }
149 }
150
151 match context.config.binop_separator() {
152 SeparatorPlace::Back => {
153 result.push(' ');
154 result.push_str(s);
155 result.push_str(&indent_str);
156 }
157 SeparatorPlace::Front => {
158 result.push_str(&indent_str);
159 result.push_str(s);
160 result.push(' ');
161 }
162 }
163
164 result.push_str(default_rw.as_ref().map_err(|err| err.clone())?);
165 }
166 Ok(result)
167}
168
169pub(crate) fn rewrite_pair<LHS, RHS>(
171 lhs: &LHS,
172 rhs: &RHS,
173 pp: PairParts<'_>,
174 context: &RewriteContext<'_>,
175 shape: Shape,
176 separator_place: SeparatorPlace,
177) -> RewriteResult
178where
179 LHS: Rewrite + Spanned,
180 RHS: Rewrite + Spanned,
181{
182 let tab_spaces = context.config.tab_spaces();
183 let lhs_overhead = match separator_place {
184 SeparatorPlace::Back => shape.used_width() + pp.prefix.len() + pp.infix.trim_end().len(),
185 SeparatorPlace::Front => shape.used_width(),
186 };
187 let lhs_shape = Shape {
188 width: context.budget(lhs_overhead),
189 ..shape
190 };
191 let lhs_result = lhs
192 .rewrite_result(context, lhs_shape)
193 .map(|lhs_str| format!("{}{}", pp.prefix, lhs_str))?;
194
195 let rhs_orig_result = shape
197 .offset_left(last_line_width(&lhs_result) + pp.infix.len())
198 .and_then(|s| s.sub_width(pp.suffix.len()))
199 .max_width_error(shape.width, rhs.span())
200 .and_then(|rhs_shape| rhs.rewrite_result(context, rhs_shape));
201
202 if let Ok(ref rhs_result) = rhs_orig_result {
203 let allow_same_line = lhs_result.len() <= tab_spaces
207 || rhs_result
208 .lines()
209 .next()
210 .map(|first_line| first_line.ends_with('{'))
211 .unwrap_or(false);
212 if !rhs_result.contains('\n') || allow_same_line {
213 let one_line_width = last_line_width(&lhs_result)
214 + pp.infix.len()
215 + first_line_width(rhs_result)
216 + pp.suffix.len();
217 if one_line_width <= shape.width {
218 return Ok(format!(
219 "{}{}{}{}",
220 lhs_result, pp.infix, rhs_result, pp.suffix
221 ));
222 }
223 }
224 }
225
226 let mut rhs_shape = match context.config.indent_style() {
229 IndentStyle::Visual => shape
230 .sub_width(pp.suffix.len() + pp.prefix.len())
231 .max_width_error(shape.width, rhs.span())?
232 .visual_indent(pp.prefix.len()),
233 IndentStyle::Block => {
234 let rhs_overhead = shape.rhs_overhead(context.config);
236 Shape::indented(shape.indent.block_indent(context.config), context.config)
237 .sub_width(rhs_overhead)
238 .max_width_error(shape.width, rhs.span())?
239 }
240 };
241 let infix = match separator_place {
242 SeparatorPlace::Back => pp.infix.trim_end(),
243 SeparatorPlace::Front => pp.infix.trim_start(),
244 };
245 if separator_place == SeparatorPlace::Front {
246 rhs_shape = rhs_shape
247 .offset_left(infix.len())
248 .max_width_error(rhs_shape.width, rhs.span())?;
249 }
250 let rhs_result = rhs.rewrite_result(context, rhs_shape)?;
251 let indent_str = rhs_shape.indent.to_string_with_newline(context.config);
252 let infix_with_sep = match separator_place {
253 SeparatorPlace::Back => format!("{infix}{indent_str}"),
254 SeparatorPlace::Front => format!("{indent_str}{infix}"),
255 };
256 Ok(format!(
257 "{}{}{}{}",
258 lhs_result, infix_with_sep, rhs_result, pp.suffix
259 ))
260}
261
262trait FlattenPair: Rewrite + Sized {
264 fn flatten(&self, _: &RewriteContext<'_>, _: Shape) -> Option<PairList<'_, '_, Self>> {
265 None
266 }
267}
268
269struct PairList<'a, 'b, T: Rewrite> {
270 list: Vec<(&'b T, RewriteResult)>,
271 separators: Vec<&'a str>,
272 span: Span,
273}
274
275fn is_ident_or_bool_lit(expr: &ast::Expr) -> bool {
276 match &expr.kind {
277 ast::ExprKind::Path(None, path) if path.segments.len() == 1 => true,
278 ast::ExprKind::Lit(token::Lit {
279 kind: token::LitKind::Bool,
280 ..
281 }) => true,
282 ast::ExprKind::Unary(_, expr)
283 | ast::ExprKind::AddrOf(_, _, expr)
284 | ast::ExprKind::Paren(expr)
285 | ast::ExprKind::Try(expr) => is_ident_or_bool_lit(expr),
286 _ => false,
287 }
288}
289
290impl<'a, 'b> PairList<'a, 'b, ast::Expr> {
291 fn let_chain_count(&self) -> usize {
292 self.list
293 .iter()
294 .filter(|(expr, _)| matches!(expr.kind, ast::ExprKind::Let(..)))
295 .count()
296 }
297
298 fn can_rewrite_let_chain_single_line(&self) -> bool {
299 if self.list.len() != 2 {
300 return false;
301 }
302
303 let fist_item_is_ident_or_bool_lit = is_ident_or_bool_lit(self.list[0].0);
304 let second_item_is_let_chain = matches!(self.list[1].0.kind, ast::ExprKind::Let(..));
305
306 fist_item_is_ident_or_bool_lit && second_item_is_let_chain
307 }
308}
309
310impl FlattenPair for ast::Expr {
311 fn flatten(
312 &self,
313 context: &RewriteContext<'_>,
314 shape: Shape,
315 ) -> Option<PairList<'_, '_, ast::Expr>> {
316 let top_op = match self.kind {
317 ast::ExprKind::Binary(op, _, _) => op.node,
318 _ => return None,
319 };
320
321 let default_rewrite = |node: &ast::Expr, sep: usize, is_first: bool| {
322 if is_first {
323 return node.rewrite_result(context, shape);
324 }
325 let nested_overhead = sep + 1;
326 let rhs_offset = shape.rhs_overhead(context.config);
327 let nested_shape = (match context.config.indent_style() {
328 IndentStyle::Visual => shape.visual_indent(0),
329 IndentStyle::Block => shape.block_indent(context.config.tab_spaces()),
330 })
331 .with_max_width(context.config)
332 .sub_width(rhs_offset)
333 .max_width_error(shape.width, node.span)?;
334 let default_shape = match context.config.binop_separator() {
335 SeparatorPlace::Back => nested_shape
336 .sub_width(nested_overhead)
337 .max_width_error(nested_shape.width, node.span)?,
338 SeparatorPlace::Front => nested_shape
339 .offset_left(nested_overhead)
340 .max_width_error(nested_shape.width, node.span)?,
341 };
342 node.rewrite_result(context, default_shape)
343 };
344
345 let mut stack = vec![];
348 let mut list = vec![];
349 let mut separators = vec![];
350 let mut node = self;
351 let span = self.span();
352 loop {
353 match node.kind {
354 ast::ExprKind::Binary(op, ref lhs, _) if op.node == top_op => {
355 stack.push(node);
356 node = lhs;
357 }
358 _ => {
359 let op_len = separators.last().map_or(0, |s: &&str| s.len());
360 let rw = default_rewrite(node, op_len, list.is_empty());
361 list.push((node, rw));
362 if let Some(pop) = stack.pop() {
363 match pop.kind {
364 ast::ExprKind::Binary(op, _, ref rhs) => {
365 separators.push(op.node.as_str());
366 node = rhs;
367 }
368 _ => unreachable!(),
369 }
370 } else {
371 break;
372 }
373 }
374 }
375 }
376
377 assert_eq!(list.len() - 1, separators.len());
378 Some(PairList {
379 list,
380 separators,
381 span,
382 })
383 }
384}
385
386impl FlattenPair for ast::Ty {}
387impl FlattenPair for ast::Pat {}