]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use std::iter::ExactSizeIterator; |
2 | use std::ops::Deref; | |
3 | ||
4 | use rustc_ast::ast::{self, FnRetTy, Mutability}; | |
5 | use rustc_span::{symbol::kw, BytePos, Pos, Span}; | |
6 | ||
7 | use crate::comment::{combine_strs_with_missing_comments, contains_comment}; | |
8 | use crate::config::lists::*; | |
9 | use crate::config::{IndentStyle, TypeDensity, Version}; | |
10 | use crate::expr::{ | |
11 | format_expr, rewrite_assign_rhs, rewrite_call, rewrite_tuple, rewrite_unary_prefix, ExprType, | |
12 | }; | |
13 | use crate::lists::{ | |
14 | definitive_tactic, itemize_list, write_list, ListFormatting, ListItem, Separator, | |
15 | }; | |
16 | use crate::macros::{rewrite_macro, MacroPosition}; | |
17 | use crate::overflow; | |
18 | use crate::pairs::{rewrite_pair, PairParts}; | |
19 | use crate::rewrite::{Rewrite, RewriteContext}; | |
20 | use crate::shape::Shape; | |
21 | use crate::source_map::SpanUtils; | |
22 | use crate::spanned::Spanned; | |
23 | use crate::utils::{ | |
24 | colon_spaces, extra_offset, first_line_width, format_extern, format_mutability, | |
25 | last_line_extendable, last_line_width, mk_sp, rewrite_ident, | |
26 | }; | |
27 | ||
28 | #[derive(Copy, Clone, Debug, Eq, PartialEq)] | |
29 | pub(crate) enum PathContext { | |
30 | Expr, | |
31 | Type, | |
32 | Import, | |
33 | } | |
34 | ||
35 | // Does not wrap on simple segments. | |
36 | pub(crate) fn rewrite_path( | |
37 | context: &RewriteContext<'_>, | |
38 | path_context: PathContext, | |
39 | qself: Option<&ast::QSelf>, | |
40 | path: &ast::Path, | |
41 | shape: Shape, | |
42 | ) -> Option<String> { | |
43 | let skip_count = qself.map_or(0, |x| x.position); | |
44 | ||
45 | let mut result = if path.is_global() && qself.is_none() && path_context != PathContext::Import { | |
46 | "::".to_owned() | |
47 | } else { | |
48 | String::new() | |
49 | }; | |
50 | ||
51 | let mut span_lo = path.span.lo(); | |
52 | ||
53 | if let Some(qself) = qself { | |
54 | result.push('<'); | |
55 | ||
56 | let fmt_ty = qself.ty.rewrite(context, shape)?; | |
57 | result.push_str(&fmt_ty); | |
58 | ||
59 | if skip_count > 0 { | |
60 | result.push_str(" as "); | |
61 | if path.is_global() && path_context != PathContext::Import { | |
62 | result.push_str("::"); | |
63 | } | |
64 | ||
65 | // 3 = ">::".len() | |
66 | let shape = shape.sub_width(3)?; | |
67 | ||
68 | result = rewrite_path_segments( | |
69 | PathContext::Type, | |
70 | result, | |
71 | path.segments.iter().take(skip_count), | |
72 | span_lo, | |
73 | path.span.hi(), | |
74 | context, | |
75 | shape, | |
76 | )?; | |
77 | } | |
78 | ||
79 | result.push_str(">::"); | |
80 | span_lo = qself.ty.span.hi() + BytePos(1); | |
81 | } | |
82 | ||
83 | rewrite_path_segments( | |
84 | path_context, | |
85 | result, | |
86 | path.segments.iter().skip(skip_count), | |
87 | span_lo, | |
88 | path.span.hi(), | |
89 | context, | |
90 | shape, | |
91 | ) | |
92 | } | |
93 | ||
94 | fn rewrite_path_segments<'a, I>( | |
95 | path_context: PathContext, | |
96 | mut buffer: String, | |
97 | iter: I, | |
98 | mut span_lo: BytePos, | |
99 | span_hi: BytePos, | |
100 | context: &RewriteContext<'_>, | |
101 | shape: Shape, | |
102 | ) -> Option<String> | |
103 | where | |
104 | I: Iterator<Item = &'a ast::PathSegment>, | |
105 | { | |
106 | let mut first = true; | |
107 | let shape = shape.visual_indent(0); | |
108 | ||
109 | for segment in iter { | |
110 | // Indicates a global path, shouldn't be rendered. | |
111 | if segment.ident.name == kw::PathRoot { | |
112 | continue; | |
113 | } | |
114 | if first { | |
115 | first = false; | |
116 | } else { | |
117 | buffer.push_str("::"); | |
118 | } | |
119 | ||
120 | let extra_offset = extra_offset(&buffer, shape); | |
121 | let new_shape = shape.shrink_left(extra_offset)?; | |
122 | let segment_string = rewrite_segment( | |
123 | path_context, | |
124 | segment, | |
125 | &mut span_lo, | |
126 | span_hi, | |
127 | context, | |
128 | new_shape, | |
129 | )?; | |
130 | ||
131 | buffer.push_str(&segment_string); | |
132 | } | |
133 | ||
134 | Some(buffer) | |
135 | } | |
136 | ||
137 | #[derive(Debug)] | |
138 | pub(crate) enum SegmentParam<'a> { | |
139 | Const(&'a ast::AnonConst), | |
140 | LifeTime(&'a ast::Lifetime), | |
141 | Type(&'a ast::Ty), | |
142 | Binding(&'a ast::AssocTyConstraint), | |
143 | } | |
144 | ||
145 | impl<'a> SegmentParam<'a> { | |
146 | fn from_generic_arg(arg: &ast::GenericArg) -> SegmentParam<'_> { | |
147 | match arg { | |
148 | ast::GenericArg::Lifetime(ref lt) => SegmentParam::LifeTime(lt), | |
149 | ast::GenericArg::Type(ref ty) => SegmentParam::Type(ty), | |
150 | ast::GenericArg::Const(const_) => SegmentParam::Const(const_), | |
151 | } | |
152 | } | |
153 | } | |
154 | ||
155 | impl<'a> Spanned for SegmentParam<'a> { | |
156 | fn span(&self) -> Span { | |
157 | match *self { | |
158 | SegmentParam::Const(const_) => const_.value.span, | |
159 | SegmentParam::LifeTime(lt) => lt.ident.span, | |
160 | SegmentParam::Type(ty) => ty.span, | |
161 | SegmentParam::Binding(binding) => binding.span, | |
162 | } | |
163 | } | |
164 | } | |
165 | ||
166 | impl<'a> Rewrite for SegmentParam<'a> { | |
167 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
168 | match *self { | |
169 | SegmentParam::Const(const_) => const_.rewrite(context, shape), | |
170 | SegmentParam::LifeTime(lt) => lt.rewrite(context, shape), | |
171 | SegmentParam::Type(ty) => ty.rewrite(context, shape), | |
172 | SegmentParam::Binding(assoc_ty_constraint) => { | |
173 | let mut result = match assoc_ty_constraint.kind { | |
174 | ast::AssocTyConstraintKind::Bound { .. } => { | |
175 | format!("{}: ", rewrite_ident(context, assoc_ty_constraint.ident)) | |
176 | } | |
177 | ast::AssocTyConstraintKind::Equality { .. } => { | |
178 | match context.config.type_punctuation_density() { | |
179 | TypeDensity::Wide => { | |
180 | format!("{} = ", rewrite_ident(context, assoc_ty_constraint.ident)) | |
181 | } | |
182 | TypeDensity::Compressed => { | |
183 | format!("{}=", rewrite_ident(context, assoc_ty_constraint.ident)) | |
184 | } | |
185 | } | |
186 | } | |
187 | }; | |
188 | ||
189 | let budget = shape.width.checked_sub(result.len())?; | |
190 | let rewrite = assoc_ty_constraint | |
191 | .kind | |
192 | .rewrite(context, Shape::legacy(budget, shape.indent + result.len()))?; | |
193 | result.push_str(&rewrite); | |
194 | Some(result) | |
195 | } | |
196 | } | |
197 | } | |
198 | } | |
199 | ||
200 | impl Rewrite for ast::AssocTyConstraintKind { | |
201 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
202 | match self { | |
203 | ast::AssocTyConstraintKind::Equality { ty } => ty.rewrite(context, shape), | |
204 | ast::AssocTyConstraintKind::Bound { bounds } => bounds.rewrite(context, shape), | |
205 | } | |
206 | } | |
207 | } | |
208 | ||
209 | // Formats a path segment. There are some hacks involved to correctly determine | |
210 | // the segment's associated span since it's not part of the AST. | |
211 | // | |
212 | // The span_lo is assumed to be greater than the end of any previous segment's | |
213 | // parameters and lesser or equal than the start of current segment. | |
214 | // | |
215 | // span_hi is assumed equal to the end of the entire path. | |
216 | // | |
217 | // When the segment contains a positive number of parameters, we update span_lo | |
218 | // so that invariants described above will hold for the next segment. | |
219 | fn rewrite_segment( | |
220 | path_context: PathContext, | |
221 | segment: &ast::PathSegment, | |
222 | span_lo: &mut BytePos, | |
223 | span_hi: BytePos, | |
224 | context: &RewriteContext<'_>, | |
225 | shape: Shape, | |
226 | ) -> Option<String> { | |
227 | let mut result = String::with_capacity(128); | |
228 | result.push_str(rewrite_ident(context, segment.ident)); | |
229 | ||
230 | let ident_len = result.len(); | |
231 | let shape = if context.use_block_indent() { | |
232 | shape.offset_left(ident_len)? | |
233 | } else { | |
234 | shape.shrink_left(ident_len)? | |
235 | }; | |
236 | ||
237 | if let Some(ref args) = segment.args { | |
238 | match **args { | |
239 | ast::GenericArgs::AngleBracketed(ref data) if !data.args.is_empty() => { | |
240 | let param_list = data | |
241 | .args | |
242 | .iter() | |
243 | .map(|x| match x { | |
244 | ast::AngleBracketedArg::Arg(generic_arg) => { | |
245 | SegmentParam::from_generic_arg(generic_arg) | |
246 | } | |
247 | ast::AngleBracketedArg::Constraint(constraint) => { | |
248 | SegmentParam::Binding(constraint) | |
249 | } | |
250 | }) | |
251 | .collect::<Vec<_>>(); | |
252 | ||
253 | // HACK: squeeze out the span between the identifier and the parameters. | |
254 | // The hack is requried so that we don't remove the separator inside macro calls. | |
255 | // This does not work in the presence of comment, hoping that people are | |
256 | // sane about where to put their comment. | |
257 | let separator_snippet = context | |
258 | .snippet(mk_sp(segment.ident.span.hi(), data.span.lo())) | |
259 | .trim(); | |
260 | let force_separator = context.inside_macro() && separator_snippet.starts_with("::"); | |
261 | let separator = if path_context == PathContext::Expr || force_separator { | |
262 | "::" | |
263 | } else { | |
264 | "" | |
265 | }; | |
266 | result.push_str(separator); | |
267 | ||
268 | let generics_str = overflow::rewrite_with_angle_brackets( | |
269 | context, | |
270 | "", | |
271 | param_list.iter(), | |
272 | shape, | |
273 | mk_sp(*span_lo, span_hi), | |
274 | )?; | |
275 | ||
276 | // Update position of last bracket. | |
277 | *span_lo = context | |
278 | .snippet_provider | |
279 | .span_after(mk_sp(*span_lo, span_hi), "<"); | |
280 | ||
281 | result.push_str(&generics_str) | |
282 | } | |
283 | ast::GenericArgs::Parenthesized(ref data) => { | |
284 | result.push_str(&format_function_type( | |
285 | data.inputs.iter().map(|x| &**x), | |
286 | &data.output, | |
287 | false, | |
288 | data.span, | |
289 | context, | |
290 | shape, | |
291 | )?); | |
292 | } | |
293 | _ => (), | |
294 | } | |
295 | } | |
296 | ||
297 | Some(result) | |
298 | } | |
299 | ||
300 | fn format_function_type<'a, I>( | |
301 | inputs: I, | |
302 | output: &FnRetTy, | |
303 | variadic: bool, | |
304 | span: Span, | |
305 | context: &RewriteContext<'_>, | |
306 | shape: Shape, | |
307 | ) -> Option<String> | |
308 | where | |
309 | I: ExactSizeIterator, | |
310 | <I as Iterator>::Item: Deref, | |
311 | <I::Item as Deref>::Target: Rewrite + Spanned + 'a, | |
312 | { | |
313 | debug!("format_function_type {:#?}", shape); | |
314 | ||
315 | let ty_shape = match context.config.indent_style() { | |
316 | // 4 = " -> " | |
317 | IndentStyle::Block => shape.offset_left(4)?, | |
318 | IndentStyle::Visual => shape.block_left(4)?, | |
319 | }; | |
320 | let output = match *output { | |
321 | FnRetTy::Ty(ref ty) => { | |
322 | let type_str = ty.rewrite(context, ty_shape)?; | |
323 | format!(" -> {}", type_str) | |
324 | } | |
325 | FnRetTy::Default(..) => String::new(), | |
326 | }; | |
327 | ||
328 | let list_shape = if context.use_block_indent() { | |
329 | Shape::indented( | |
330 | shape.block().indent.block_indent(context.config), | |
331 | context.config, | |
332 | ) | |
333 | } else { | |
334 | // 2 for () | |
335 | let budget = shape.width.checked_sub(2)?; | |
336 | // 1 for ( | |
337 | let offset = shape.indent + 1; | |
338 | Shape::legacy(budget, offset) | |
339 | }; | |
340 | ||
341 | let is_inputs_empty = inputs.len() == 0; | |
342 | let list_lo = context.snippet_provider.span_after(span, "("); | |
343 | let (list_str, tactic) = if is_inputs_empty { | |
344 | let tactic = get_tactics(&[], &output, shape); | |
345 | let list_hi = context.snippet_provider.span_before(span, ")"); | |
346 | let comment = context | |
347 | .snippet_provider | |
348 | .span_to_snippet(mk_sp(list_lo, list_hi))? | |
349 | .trim(); | |
350 | let comment = if comment.starts_with("//") { | |
351 | format!( | |
352 | "{}{}{}", | |
353 | &list_shape.indent.to_string_with_newline(context.config), | |
354 | comment, | |
355 | &shape.block().indent.to_string_with_newline(context.config) | |
356 | ) | |
357 | } else { | |
358 | comment.to_string() | |
359 | }; | |
360 | (comment, tactic) | |
361 | } else { | |
362 | let items = itemize_list( | |
363 | context.snippet_provider, | |
364 | inputs, | |
365 | ")", | |
366 | ",", | |
367 | |arg| arg.span().lo(), | |
368 | |arg| arg.span().hi(), | |
369 | |arg| arg.rewrite(context, list_shape), | |
370 | list_lo, | |
371 | span.hi(), | |
372 | false, | |
373 | ); | |
374 | ||
375 | let item_vec: Vec<_> = items.collect(); | |
376 | let tactic = get_tactics(&item_vec, &output, shape); | |
377 | let trailing_separator = if !context.use_block_indent() || variadic { | |
378 | SeparatorTactic::Never | |
379 | } else { | |
380 | context.config.trailing_comma() | |
381 | }; | |
382 | ||
383 | let fmt = ListFormatting::new(list_shape, context.config) | |
384 | .tactic(tactic) | |
385 | .trailing_separator(trailing_separator) | |
386 | .ends_with_newline(tactic.ends_with_newline(context.config.indent_style())) | |
387 | .preserve_newline(true); | |
388 | (write_list(&item_vec, &fmt)?, tactic) | |
389 | }; | |
390 | ||
391 | let args = if tactic == DefinitiveListTactic::Horizontal | |
392 | || !context.use_block_indent() | |
393 | || is_inputs_empty | |
394 | { | |
395 | format!("({})", list_str) | |
396 | } else { | |
397 | format!( | |
398 | "({}{}{})", | |
399 | list_shape.indent.to_string_with_newline(context.config), | |
400 | list_str, | |
401 | shape.block().indent.to_string_with_newline(context.config), | |
402 | ) | |
403 | }; | |
404 | if output.is_empty() || last_line_width(&args) + first_line_width(&output) <= shape.width { | |
405 | Some(format!("{}{}", args, output)) | |
406 | } else { | |
407 | Some(format!( | |
408 | "{}\n{}{}", | |
409 | args, | |
410 | list_shape.indent.to_string(context.config), | |
411 | output.trim_start() | |
412 | )) | |
413 | } | |
414 | } | |
415 | ||
416 | fn type_bound_colon(context: &RewriteContext<'_>) -> &'static str { | |
417 | colon_spaces(context.config) | |
418 | } | |
419 | ||
420 | // If the return type is multi-lined, then force to use multiple lines for | |
421 | // arguments as well. | |
422 | fn get_tactics(item_vec: &[ListItem], output: &str, shape: Shape) -> DefinitiveListTactic { | |
423 | if output.contains('\n') { | |
424 | DefinitiveListTactic::Vertical | |
425 | } else { | |
426 | definitive_tactic( | |
427 | item_vec, | |
428 | ListTactic::HorizontalVertical, | |
429 | Separator::Comma, | |
430 | // 2 is for the case of ',\n' | |
431 | shape.width.saturating_sub(2 + output.len()), | |
432 | ) | |
433 | } | |
434 | } | |
435 | ||
436 | impl Rewrite for ast::WherePredicate { | |
437 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
438 | // FIXME: dead spans? | |
439 | let result = match *self { | |
440 | ast::WherePredicate::BoundPredicate(ast::WhereBoundPredicate { | |
441 | ref bound_generic_params, | |
442 | ref bounded_ty, | |
443 | ref bounds, | |
444 | .. | |
445 | }) => { | |
446 | let type_str = bounded_ty.rewrite(context, shape)?; | |
447 | let colon = type_bound_colon(context).trim_end(); | |
448 | let lhs = if let Some(lifetime_str) = | |
449 | rewrite_lifetime_param(context, shape, bound_generic_params) | |
450 | { | |
451 | format!("for<{}> {}{}", lifetime_str, type_str, colon) | |
452 | } else { | |
453 | format!("{}{}", type_str, colon) | |
454 | }; | |
455 | ||
456 | rewrite_assign_rhs(context, lhs, bounds, shape)? | |
457 | } | |
458 | ast::WherePredicate::RegionPredicate(ast::WhereRegionPredicate { | |
459 | ref lifetime, | |
460 | ref bounds, | |
461 | .. | |
462 | }) => rewrite_bounded_lifetime(lifetime, bounds, context, shape)?, | |
463 | ast::WherePredicate::EqPredicate(ast::WhereEqPredicate { | |
464 | ref lhs_ty, | |
465 | ref rhs_ty, | |
466 | .. | |
467 | }) => { | |
468 | let lhs_ty_str = lhs_ty.rewrite(context, shape).map(|lhs| lhs + " =")?; | |
469 | rewrite_assign_rhs(context, lhs_ty_str, &**rhs_ty, shape)? | |
470 | } | |
471 | }; | |
472 | ||
473 | Some(result) | |
474 | } | |
475 | } | |
476 | ||
477 | impl Rewrite for ast::GenericArg { | |
478 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
479 | match *self { | |
480 | ast::GenericArg::Lifetime(ref lt) => lt.rewrite(context, shape), | |
481 | ast::GenericArg::Type(ref ty) => ty.rewrite(context, shape), | |
482 | ast::GenericArg::Const(ref const_) => const_.rewrite(context, shape), | |
483 | } | |
484 | } | |
485 | } | |
486 | ||
487 | fn rewrite_bounded_lifetime( | |
488 | lt: &ast::Lifetime, | |
489 | bounds: &[ast::GenericBound], | |
490 | context: &RewriteContext<'_>, | |
491 | shape: Shape, | |
492 | ) -> Option<String> { | |
493 | let result = lt.rewrite(context, shape)?; | |
494 | ||
495 | if bounds.is_empty() { | |
496 | Some(result) | |
497 | } else { | |
498 | let colon = type_bound_colon(context); | |
499 | let overhead = last_line_width(&result) + colon.len(); | |
500 | let result = format!( | |
501 | "{}{}{}", | |
502 | result, | |
503 | colon, | |
504 | join_bounds(context, shape.sub_width(overhead)?, bounds, true)? | |
505 | ); | |
506 | Some(result) | |
507 | } | |
508 | } | |
509 | ||
510 | impl Rewrite for ast::AnonConst { | |
511 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
512 | format_expr(&self.value, ExprType::SubExpression, context, shape) | |
513 | } | |
514 | } | |
515 | ||
516 | impl Rewrite for ast::Lifetime { | |
517 | fn rewrite(&self, context: &RewriteContext<'_>, _: Shape) -> Option<String> { | |
518 | Some(rewrite_ident(context, self.ident).to_owned()) | |
519 | } | |
520 | } | |
521 | ||
522 | impl Rewrite for ast::GenericBound { | |
523 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
524 | match *self { | |
525 | ast::GenericBound::Trait(ref poly_trait_ref, trait_bound_modifier) => { | |
526 | let snippet = context.snippet(self.span()); | |
527 | let has_paren = snippet.starts_with('(') && snippet.ends_with(')'); | |
528 | let rewrite = match trait_bound_modifier { | |
529 | ast::TraitBoundModifier::None => poly_trait_ref.rewrite(context, shape), | |
530 | ast::TraitBoundModifier::Maybe => poly_trait_ref | |
531 | .rewrite(context, shape.offset_left(1)?) | |
532 | .map(|s| format!("?{}", s)), | |
533 | ast::TraitBoundModifier::MaybeConst => poly_trait_ref | |
534 | .rewrite(context, shape.offset_left(7)?) | |
535 | .map(|s| format!("?const {}", s)), | |
536 | ast::TraitBoundModifier::MaybeConstMaybe => poly_trait_ref | |
537 | .rewrite(context, shape.offset_left(8)?) | |
538 | .map(|s| format!("?const ?{}", s)), | |
539 | }; | |
540 | rewrite.map(|s| if has_paren { format!("({})", s) } else { s }) | |
541 | } | |
542 | ast::GenericBound::Outlives(ref lifetime) => lifetime.rewrite(context, shape), | |
543 | } | |
544 | } | |
545 | } | |
546 | ||
547 | impl Rewrite for ast::GenericBounds { | |
548 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
549 | if self.is_empty() { | |
550 | return Some(String::new()); | |
551 | } | |
552 | ||
553 | join_bounds(context, shape, self, true) | |
554 | } | |
555 | } | |
556 | ||
557 | impl Rewrite for ast::GenericParam { | |
558 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
559 | let mut result = String::with_capacity(128); | |
560 | // FIXME: If there are more than one attributes, this will force multiline. | |
561 | match self.attrs.rewrite(context, shape) { | |
562 | Some(ref rw) if !rw.is_empty() => result.push_str(&format!("{} ", rw)), | |
563 | _ => (), | |
564 | } | |
565 | ||
566 | if let ast::GenericParamKind::Const { | |
567 | ref ty, | |
568 | kw_span: _, | |
569 | default: _, | |
570 | } = &self.kind | |
571 | { | |
572 | result.push_str("const "); | |
573 | result.push_str(rewrite_ident(context, self.ident)); | |
574 | result.push_str(": "); | |
575 | result.push_str(&ty.rewrite(context, shape)?); | |
576 | } else { | |
577 | result.push_str(rewrite_ident(context, self.ident)); | |
578 | } | |
579 | ||
580 | if !self.bounds.is_empty() { | |
581 | result.push_str(type_bound_colon(context)); | |
582 | result.push_str(&self.bounds.rewrite(context, shape)?) | |
583 | } | |
584 | if let ast::GenericParamKind::Type { | |
585 | default: Some(ref def), | |
586 | } = self.kind | |
587 | { | |
588 | let eq_str = match context.config.type_punctuation_density() { | |
589 | TypeDensity::Compressed => "=", | |
590 | TypeDensity::Wide => " = ", | |
591 | }; | |
592 | result.push_str(eq_str); | |
593 | let budget = shape.width.checked_sub(result.len())?; | |
594 | let rewrite = | |
595 | def.rewrite(context, Shape::legacy(budget, shape.indent + result.len()))?; | |
596 | result.push_str(&rewrite); | |
597 | } | |
598 | ||
599 | Some(result) | |
600 | } | |
601 | } | |
602 | ||
603 | impl Rewrite for ast::PolyTraitRef { | |
604 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
605 | if let Some(lifetime_str) = | |
606 | rewrite_lifetime_param(context, shape, &self.bound_generic_params) | |
607 | { | |
608 | // 6 is "for<> ".len() | |
609 | let extra_offset = lifetime_str.len() + 6; | |
610 | let path_str = self | |
611 | .trait_ref | |
612 | .rewrite(context, shape.offset_left(extra_offset)?)?; | |
613 | ||
614 | Some(format!("for<{}> {}", lifetime_str, path_str)) | |
615 | } else { | |
616 | self.trait_ref.rewrite(context, shape) | |
617 | } | |
618 | } | |
619 | } | |
620 | ||
621 | impl Rewrite for ast::TraitRef { | |
622 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
623 | rewrite_path(context, PathContext::Type, None, &self.path, shape) | |
624 | } | |
625 | } | |
626 | ||
627 | impl Rewrite for ast::Ty { | |
628 | fn rewrite(&self, context: &RewriteContext<'_>, shape: Shape) -> Option<String> { | |
629 | match self.kind { | |
630 | ast::TyKind::TraitObject(ref bounds, tobj_syntax) => { | |
631 | // we have to consider 'dyn' keyword is used or not!!! | |
632 | let is_dyn = tobj_syntax == ast::TraitObjectSyntax::Dyn; | |
633 | // 4 is length of 'dyn ' | |
634 | let shape = if is_dyn { shape.offset_left(4)? } else { shape }; | |
635 | let mut res = bounds.rewrite(context, shape)?; | |
636 | // We may have falsely removed a trailing `+` inside macro call. | |
637 | if context.inside_macro() && bounds.len() == 1 { | |
638 | if context.snippet(self.span).ends_with('+') && !res.ends_with('+') { | |
639 | res.push('+'); | |
640 | } | |
641 | } | |
642 | if is_dyn { | |
643 | Some(format!("dyn {}", res)) | |
644 | } else { | |
645 | Some(res) | |
646 | } | |
647 | } | |
648 | ast::TyKind::Ptr(ref mt) => { | |
649 | let prefix = match mt.mutbl { | |
650 | Mutability::Mut => "*mut ", | |
651 | Mutability::Not => "*const ", | |
652 | }; | |
653 | ||
654 | rewrite_unary_prefix(context, prefix, &*mt.ty, shape) | |
655 | } | |
656 | ast::TyKind::Rptr(ref lifetime, ref mt) => { | |
657 | let mut_str = format_mutability(mt.mutbl); | |
658 | let mut_len = mut_str.len(); | |
659 | let mut result = String::with_capacity(128); | |
660 | result.push_str("&"); | |
661 | let ref_hi = context.snippet_provider.span_after(self.span(), "&"); | |
662 | let mut cmnt_lo = ref_hi; | |
663 | ||
664 | if let Some(ref lifetime) = *lifetime { | |
665 | let lt_budget = shape.width.checked_sub(2 + mut_len)?; | |
666 | let lt_str = lifetime.rewrite( | |
667 | context, | |
668 | Shape::legacy(lt_budget, shape.indent + 2 + mut_len), | |
669 | )?; | |
670 | let before_lt_span = mk_sp(cmnt_lo, lifetime.ident.span.lo()); | |
671 | if contains_comment(context.snippet(before_lt_span)) { | |
672 | result = combine_strs_with_missing_comments( | |
673 | context, | |
674 | &result, | |
675 | <_str, | |
676 | before_lt_span, | |
677 | shape, | |
678 | true, | |
679 | )?; | |
680 | } else { | |
681 | result.push_str(<_str); | |
682 | } | |
683 | result.push_str(" "); | |
684 | cmnt_lo = lifetime.ident.span.hi(); | |
685 | } | |
686 | ||
687 | if ast::Mutability::Mut == mt.mutbl { | |
688 | let mut_hi = context.snippet_provider.span_after(self.span(), "mut"); | |
689 | let before_mut_span = mk_sp(cmnt_lo, mut_hi - BytePos::from_usize(3)); | |
690 | if contains_comment(context.snippet(before_mut_span)) { | |
691 | result = combine_strs_with_missing_comments( | |
692 | context, | |
693 | result.trim_end(), | |
694 | mut_str, | |
695 | before_mut_span, | |
696 | shape, | |
697 | true, | |
698 | )?; | |
699 | } else { | |
700 | result.push_str(mut_str); | |
701 | } | |
702 | cmnt_lo = mut_hi; | |
703 | } | |
704 | ||
705 | let before_ty_span = mk_sp(cmnt_lo, mt.ty.span.lo()); | |
706 | if contains_comment(context.snippet(before_ty_span)) { | |
707 | result = combine_strs_with_missing_comments( | |
708 | context, | |
709 | result.trim_end(), | |
710 | &mt.ty.rewrite(&context, shape)?, | |
711 | before_ty_span, | |
712 | shape, | |
713 | true, | |
714 | )?; | |
715 | } else { | |
716 | let used_width = last_line_width(&result); | |
717 | let budget = shape.width.checked_sub(used_width)?; | |
718 | let ty_str = mt | |
719 | .ty | |
720 | .rewrite(&context, Shape::legacy(budget, shape.indent + used_width))?; | |
721 | result.push_str(&ty_str); | |
722 | } | |
723 | ||
724 | Some(result) | |
725 | } | |
726 | // FIXME: we drop any comments here, even though it's a silly place to put | |
727 | // comments. | |
728 | ast::TyKind::Paren(ref ty) => { | |
729 | if context.config.version() == Version::One | |
730 | || context.config.indent_style() == IndentStyle::Visual | |
731 | { | |
732 | let budget = shape.width.checked_sub(2)?; | |
733 | return ty | |
734 | .rewrite(context, Shape::legacy(budget, shape.indent + 1)) | |
735 | .map(|ty_str| format!("({})", ty_str)); | |
736 | } | |
737 | ||
738 | // 2 = () | |
739 | if let Some(sh) = shape.sub_width(2) { | |
740 | if let Some(ref s) = ty.rewrite(context, sh) { | |
741 | if !s.contains('\n') { | |
742 | return Some(format!("({})", s)); | |
743 | } | |
744 | } | |
745 | } | |
746 | ||
747 | let indent_str = shape.indent.to_string_with_newline(context.config); | |
748 | let shape = shape | |
749 | .block_indent(context.config.tab_spaces()) | |
750 | .with_max_width(context.config); | |
751 | let rw = ty.rewrite(context, shape)?; | |
752 | Some(format!( | |
753 | "({}{}{})", | |
754 | shape.to_string_with_newline(context.config), | |
755 | rw, | |
756 | indent_str | |
757 | )) | |
758 | } | |
759 | ast::TyKind::Slice(ref ty) => { | |
760 | let budget = shape.width.checked_sub(4)?; | |
761 | ty.rewrite(context, Shape::legacy(budget, shape.indent + 1)) | |
762 | .map(|ty_str| format!("[{}]", ty_str)) | |
763 | } | |
764 | ast::TyKind::Tup(ref items) => { | |
765 | rewrite_tuple(context, items.iter(), self.span, shape, items.len() == 1) | |
766 | } | |
767 | ast::TyKind::Path(ref q_self, ref path) => { | |
768 | rewrite_path(context, PathContext::Type, q_self.as_ref(), path, shape) | |
769 | } | |
770 | ast::TyKind::Array(ref ty, ref repeats) => rewrite_pair( | |
771 | &**ty, | |
772 | &*repeats.value, | |
773 | PairParts::new("[", "; ", "]"), | |
774 | context, | |
775 | shape, | |
776 | SeparatorPlace::Back, | |
777 | ), | |
778 | ast::TyKind::Infer => { | |
779 | if shape.width >= 1 { | |
780 | Some("_".to_owned()) | |
781 | } else { | |
782 | None | |
783 | } | |
784 | } | |
785 | ast::TyKind::BareFn(ref bare_fn) => rewrite_bare_fn(bare_fn, self.span, context, shape), | |
786 | ast::TyKind::Never => Some(String::from("!")), | |
787 | ast::TyKind::MacCall(ref mac) => { | |
788 | rewrite_macro(mac, None, context, shape, MacroPosition::Expression) | |
789 | } | |
790 | ast::TyKind::ImplicitSelf => Some(String::from("")), | |
791 | ast::TyKind::ImplTrait(_, ref it) => { | |
792 | // Empty trait is not a parser error. | |
793 | if it.is_empty() { | |
794 | return Some("impl".to_owned()); | |
795 | } | |
796 | let rw = if context.config.version() == Version::One { | |
797 | it.rewrite(context, shape) | |
798 | } else { | |
799 | join_bounds(context, shape, it, false) | |
800 | }; | |
801 | rw.map(|it_str| { | |
802 | let space = if it_str.is_empty() { "" } else { " " }; | |
803 | format!("impl{}{}", space, it_str) | |
804 | }) | |
805 | } | |
806 | ast::TyKind::CVarArgs => Some("...".to_owned()), | |
807 | ast::TyKind::Err => Some(context.snippet(self.span).to_owned()), | |
808 | ast::TyKind::Typeof(ref anon_const) => rewrite_call( | |
809 | context, | |
810 | "typeof", | |
811 | &[anon_const.value.clone()], | |
812 | self.span, | |
813 | shape, | |
814 | ), | |
815 | } | |
816 | } | |
817 | } | |
818 | ||
819 | fn rewrite_bare_fn( | |
820 | bare_fn: &ast::BareFnTy, | |
821 | span: Span, | |
822 | context: &RewriteContext<'_>, | |
823 | shape: Shape, | |
824 | ) -> Option<String> { | |
825 | debug!("rewrite_bare_fn {:#?}", shape); | |
826 | ||
827 | let mut result = String::with_capacity(128); | |
828 | ||
829 | if let Some(ref lifetime_str) = rewrite_lifetime_param(context, shape, &bare_fn.generic_params) | |
830 | { | |
831 | result.push_str("for<"); | |
832 | // 6 = "for<> ".len(), 4 = "for<". | |
833 | // This doesn't work out so nicely for multiline situation with lots of | |
834 | // rightward drift. If that is a problem, we could use the list stuff. | |
835 | result.push_str(lifetime_str); | |
836 | result.push_str("> "); | |
837 | } | |
838 | ||
839 | result.push_str(crate::utils::format_unsafety(bare_fn.unsafety)); | |
840 | ||
841 | result.push_str(&format_extern( | |
842 | bare_fn.ext, | |
843 | context.config.force_explicit_abi(), | |
844 | false, | |
845 | )); | |
846 | ||
847 | result.push_str("fn"); | |
848 | ||
849 | let func_ty_shape = if context.use_block_indent() { | |
850 | shape.offset_left(result.len())? | |
851 | } else { | |
852 | shape.visual_indent(result.len()).sub_width(result.len())? | |
853 | }; | |
854 | ||
855 | let rewrite = format_function_type( | |
856 | bare_fn.decl.inputs.iter(), | |
857 | &bare_fn.decl.output, | |
858 | bare_fn.decl.c_variadic(), | |
859 | span, | |
860 | context, | |
861 | func_ty_shape, | |
862 | )?; | |
863 | ||
864 | result.push_str(&rewrite); | |
865 | ||
866 | Some(result) | |
867 | } | |
868 | ||
869 | fn is_generic_bounds_in_order(generic_bounds: &[ast::GenericBound]) -> bool { | |
870 | let is_trait = |b: &ast::GenericBound| match b { | |
871 | ast::GenericBound::Outlives(..) => false, | |
872 | ast::GenericBound::Trait(..) => true, | |
873 | }; | |
874 | let is_lifetime = |b: &ast::GenericBound| !is_trait(b); | |
875 | let last_trait_index = generic_bounds.iter().rposition(is_trait); | |
876 | let first_lifetime_index = generic_bounds.iter().position(is_lifetime); | |
877 | match (last_trait_index, first_lifetime_index) { | |
878 | (Some(last_trait_index), Some(first_lifetime_index)) => { | |
879 | last_trait_index < first_lifetime_index | |
880 | } | |
881 | _ => true, | |
882 | } | |
883 | } | |
884 | ||
885 | fn join_bounds( | |
886 | context: &RewriteContext<'_>, | |
887 | shape: Shape, | |
888 | items: &[ast::GenericBound], | |
889 | need_indent: bool, | |
890 | ) -> Option<String> { | |
891 | join_bounds_inner(context, shape, items, need_indent, false) | |
892 | } | |
893 | ||
894 | fn join_bounds_inner( | |
895 | context: &RewriteContext<'_>, | |
896 | shape: Shape, | |
897 | items: &[ast::GenericBound], | |
898 | need_indent: bool, | |
899 | force_newline: bool, | |
900 | ) -> Option<String> { | |
901 | debug_assert!(!items.is_empty()); | |
902 | ||
903 | let generic_bounds_in_order = is_generic_bounds_in_order(items); | |
904 | let is_bound_extendable = |s: &str, b: &ast::GenericBound| match b { | |
905 | ast::GenericBound::Outlives(..) => true, | |
906 | ast::GenericBound::Trait(..) => last_line_extendable(s), | |
907 | }; | |
908 | ||
909 | let result = items.iter().enumerate().try_fold( | |
910 | (String::new(), None, false), | |
911 | |(strs, prev_trailing_span, prev_extendable), (i, item)| { | |
912 | let trailing_span = if i < items.len() - 1 { | |
913 | let hi = context | |
914 | .snippet_provider | |
915 | .span_before(mk_sp(items[i + 1].span().lo(), item.span().hi()), "+"); | |
916 | ||
917 | Some(mk_sp(item.span().hi(), hi)) | |
918 | } else { | |
919 | None | |
920 | }; | |
921 | let (leading_span, has_leading_comment) = if i > 0 { | |
922 | let lo = context | |
923 | .snippet_provider | |
924 | .span_after(mk_sp(items[i - 1].span().hi(), item.span().lo()), "+"); | |
925 | ||
926 | let span = mk_sp(lo, item.span().lo()); | |
927 | ||
928 | let has_comments = contains_comment(context.snippet(span)); | |
929 | ||
930 | (Some(mk_sp(lo, item.span().lo())), has_comments) | |
931 | } else { | |
932 | (None, false) | |
933 | }; | |
934 | let prev_has_trailing_comment = match prev_trailing_span { | |
935 | Some(ts) => contains_comment(context.snippet(ts)), | |
936 | _ => false, | |
937 | }; | |
938 | ||
939 | let shape = if need_indent && force_newline { | |
940 | shape | |
941 | .block_indent(context.config.tab_spaces()) | |
942 | .with_max_width(context.config) | |
943 | } else { | |
944 | shape | |
945 | }; | |
946 | let whitespace = if force_newline && (!prev_extendable || !generic_bounds_in_order) { | |
947 | shape | |
948 | .indent | |
949 | .to_string_with_newline(context.config) | |
950 | .to_string() | |
951 | } else { | |
952 | String::from(" ") | |
953 | }; | |
954 | ||
955 | let joiner = match context.config.type_punctuation_density() { | |
956 | TypeDensity::Compressed => String::from("+"), | |
957 | TypeDensity::Wide => whitespace + "+ ", | |
958 | }; | |
959 | let joiner = if has_leading_comment { | |
960 | joiner.trim_end() | |
961 | } else { | |
962 | &joiner | |
963 | }; | |
964 | let joiner = if prev_has_trailing_comment { | |
965 | joiner.trim_start() | |
966 | } else { | |
967 | joiner | |
968 | }; | |
969 | ||
970 | let (extendable, trailing_str) = if i == 0 { | |
971 | let bound_str = item.rewrite(context, shape)?; | |
972 | (is_bound_extendable(&bound_str, item), bound_str) | |
973 | } else { | |
974 | let bound_str = &item.rewrite(context, shape)?; | |
975 | match leading_span { | |
976 | Some(ls) if has_leading_comment => ( | |
977 | is_bound_extendable(bound_str, item), | |
978 | combine_strs_with_missing_comments( | |
979 | context, joiner, bound_str, ls, shape, true, | |
980 | )?, | |
981 | ), | |
982 | _ => ( | |
983 | is_bound_extendable(bound_str, item), | |
984 | String::from(joiner) + bound_str, | |
985 | ), | |
986 | } | |
987 | }; | |
988 | match prev_trailing_span { | |
989 | Some(ts) if prev_has_trailing_comment => combine_strs_with_missing_comments( | |
990 | context, | |
991 | &strs, | |
992 | &trailing_str, | |
993 | ts, | |
994 | shape, | |
995 | true, | |
996 | ) | |
997 | .map(|v| (v, trailing_span, extendable)), | |
998 | _ => Some(( | |
999 | String::from(strs) + &trailing_str, | |
1000 | trailing_span, | |
1001 | extendable, | |
1002 | )), | |
1003 | } | |
1004 | }, | |
1005 | )?; | |
1006 | ||
1007 | if !force_newline | |
1008 | && items.len() > 1 | |
1009 | && (result.0.contains('\n') || result.0.len() > shape.width) | |
1010 | { | |
1011 | join_bounds_inner(context, shape, items, need_indent, true) | |
1012 | } else { | |
1013 | Some(result.0) | |
1014 | } | |
1015 | } | |
1016 | ||
1017 | pub(crate) fn can_be_overflowed_type( | |
1018 | context: &RewriteContext<'_>, | |
1019 | ty: &ast::Ty, | |
1020 | len: usize, | |
1021 | ) -> bool { | |
1022 | match ty.kind { | |
1023 | ast::TyKind::Tup(..) => context.use_block_indent() && len == 1, | |
1024 | ast::TyKind::Rptr(_, ref mutty) | ast::TyKind::Ptr(ref mutty) => { | |
1025 | can_be_overflowed_type(context, &*mutty.ty, len) | |
1026 | } | |
1027 | _ => false, | |
1028 | } | |
1029 | } | |
1030 | ||
1031 | /// Returns `None` if there is no `LifetimeDef` in the given generic parameters. | |
1032 | fn rewrite_lifetime_param( | |
1033 | context: &RewriteContext<'_>, | |
1034 | shape: Shape, | |
1035 | generic_params: &[ast::GenericParam], | |
1036 | ) -> Option<String> { | |
1037 | let result = generic_params | |
1038 | .iter() | |
1039 | .filter(|p| match p.kind { | |
1040 | ast::GenericParamKind::Lifetime => true, | |
1041 | _ => false, | |
1042 | }) | |
1043 | .map(|lt| lt.rewrite(context, shape)) | |
1044 | .collect::<Option<Vec<_>>>()? | |
1045 | .join(", "); | |
1046 | if result.is_empty() { | |
1047 | None | |
1048 | } else { | |
1049 | Some(result) | |
1050 | } | |
1051 | } |