]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use std::cell::{Cell, RefCell}; |
2 | use std::rc::Rc; | |
3 | ||
4 | use rustc_ast::{ast, attr::HasAttrs, token::DelimToken, visit}; | |
5 | use rustc_span::{symbol, BytePos, Pos, Span, DUMMY_SP}; | |
6 | ||
7 | use crate::attr::*; | |
8 | use crate::comment::{contains_comment, rewrite_comment, CodeCharKind, CommentCodeSlices}; | |
9 | use crate::config::Version; | |
10 | use crate::config::{BraceStyle, Config}; | |
11 | use crate::coverage::transform_missing_snippet; | |
12 | use crate::items::{ | |
13 | format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, | |
14 | rewrite_associated_impl_type, rewrite_extern_crate, rewrite_opaque_impl_type, | |
15 | rewrite_opaque_type, rewrite_type_alias, FnBraceStyle, FnSig, StaticParts, StructParts, | |
16 | }; | |
17 | use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition}; | |
18 | use crate::modules::Module; | |
19 | use crate::rewrite::{Rewrite, RewriteContext}; | |
20 | use crate::shape::{Indent, Shape}; | |
21 | use crate::skip::{is_skip_attr, SkipContext}; | |
22 | use crate::source_map::{LineRangeUtils, SpanUtils}; | |
23 | use crate::spanned::Spanned; | |
24 | use crate::stmt::Stmt; | |
25 | use crate::syntux::session::ParseSess; | |
26 | use crate::utils::{ | |
27 | self, contains_skip, count_newlines, depr_skip_annotation, format_unsafety, inner_attributes, | |
28 | last_line_width, mk_sp, ptr_vec_to_ref_vec, rewrite_ident, starts_with_newline, stmt_expr, | |
29 | }; | |
30 | use crate::{ErrorKind, FormatReport, FormattingError}; | |
31 | ||
32 | /// Creates a string slice corresponding to the specified span. | |
33 | pub(crate) struct SnippetProvider { | |
34 | /// A pointer to the content of the file we are formatting. | |
35 | big_snippet: Rc<String>, | |
36 | /// A position of the start of `big_snippet`, used as an offset. | |
37 | start_pos: usize, | |
38 | /// A end position of the file that this snippet lives. | |
39 | end_pos: usize, | |
40 | } | |
41 | ||
42 | impl SnippetProvider { | |
43 | pub(crate) fn span_to_snippet(&self, span: Span) -> Option<&str> { | |
44 | let start_index = span.lo().to_usize().checked_sub(self.start_pos)?; | |
45 | let end_index = span.hi().to_usize().checked_sub(self.start_pos)?; | |
46 | Some(&self.big_snippet[start_index..end_index]) | |
47 | } | |
48 | ||
49 | pub(crate) fn new(start_pos: BytePos, end_pos: BytePos, big_snippet: Rc<String>) -> Self { | |
50 | let start_pos = start_pos.to_usize(); | |
51 | let end_pos = end_pos.to_usize(); | |
52 | SnippetProvider { | |
53 | big_snippet, | |
54 | start_pos, | |
55 | end_pos, | |
56 | } | |
57 | } | |
58 | ||
59 | pub(crate) fn entire_snippet(&self) -> &str { | |
60 | self.big_snippet.as_str() | |
61 | } | |
62 | ||
63 | pub(crate) fn start_pos(&self) -> BytePos { | |
64 | BytePos::from_usize(self.start_pos) | |
65 | } | |
66 | ||
67 | pub(crate) fn end_pos(&self) -> BytePos { | |
68 | BytePos::from_usize(self.end_pos) | |
69 | } | |
70 | } | |
71 | ||
72 | pub(crate) struct FmtVisitor<'a> { | |
73 | parent_context: Option<&'a RewriteContext<'a>>, | |
74 | pub(crate) parse_sess: &'a ParseSess, | |
75 | pub(crate) buffer: String, | |
76 | pub(crate) last_pos: BytePos, | |
77 | // FIXME: use an RAII util or closure for indenting | |
78 | pub(crate) block_indent: Indent, | |
79 | pub(crate) config: &'a Config, | |
80 | pub(crate) is_if_else_block: bool, | |
81 | pub(crate) snippet_provider: &'a SnippetProvider, | |
82 | pub(crate) line_number: usize, | |
83 | /// List of 1-based line ranges which were annotated with skip | |
84 | /// Both bounds are inclusifs. | |
85 | pub(crate) skipped_range: Rc<RefCell<Vec<(usize, usize)>>>, | |
86 | pub(crate) macro_rewrite_failure: bool, | |
87 | pub(crate) report: FormatReport, | |
88 | pub(crate) skip_context: SkipContext, | |
89 | pub(crate) is_macro_def: bool, | |
90 | } | |
91 | ||
92 | impl<'a> Drop for FmtVisitor<'a> { | |
93 | fn drop(&mut self) { | |
94 | if let Some(ctx) = self.parent_context { | |
95 | if self.macro_rewrite_failure { | |
96 | ctx.macro_rewrite_failure.replace(true); | |
97 | } | |
98 | } | |
99 | } | |
100 | } | |
101 | ||
102 | impl<'b, 'a: 'b> FmtVisitor<'a> { | |
103 | fn set_parent_context(&mut self, context: &'a RewriteContext<'_>) { | |
104 | self.parent_context = Some(context); | |
105 | } | |
106 | ||
107 | pub(crate) fn shape(&self) -> Shape { | |
108 | Shape::indented(self.block_indent, self.config) | |
109 | } | |
110 | ||
111 | fn next_span(&self, hi: BytePos) -> Span { | |
112 | mk_sp(self.last_pos, hi) | |
113 | } | |
114 | ||
115 | fn visit_stmt(&mut self, stmt: &Stmt<'_>, include_empty_semi: bool) { | |
116 | debug!( | |
117 | "visit_stmt: {}", | |
118 | self.parse_sess.span_to_debug_info(stmt.span()) | |
119 | ); | |
120 | ||
121 | if stmt.is_empty() { | |
122 | // If the statement is empty, just skip over it. Before that, make sure any comment | |
123 | // snippet preceding the semicolon is picked up. | |
124 | let snippet = self.snippet(mk_sp(self.last_pos, stmt.span().lo())); | |
125 | let original_starts_with_newline = snippet | |
126 | .find(|c| c != ' ') | |
127 | .map_or(false, |i| starts_with_newline(&snippet[i..])); | |
128 | let snippet = snippet.trim(); | |
129 | if !snippet.is_empty() { | |
130 | // FIXME(calebcartwright 2021-01-03) - This exists strictly to maintain legacy | |
131 | // formatting where rustfmt would preserve redundant semicolons on Items in a | |
132 | // statement position. | |
133 | // See comment within `walk_stmts` for more info | |
134 | if include_empty_semi { | |
135 | self.format_missing(stmt.span().hi()); | |
136 | } else { | |
137 | if original_starts_with_newline { | |
138 | self.push_str("\n"); | |
139 | } | |
140 | ||
141 | self.push_str(&self.block_indent.to_string(self.config)); | |
142 | self.push_str(snippet); | |
143 | } | |
144 | } else if include_empty_semi { | |
145 | self.push_str(";"); | |
146 | } | |
147 | self.last_pos = stmt.span().hi(); | |
148 | return; | |
149 | } | |
150 | ||
151 | match stmt.as_ast_node().kind { | |
152 | ast::StmtKind::Item(ref item) => { | |
153 | self.visit_item(item); | |
154 | self.last_pos = stmt.span().hi(); | |
155 | } | |
156 | ast::StmtKind::Local(..) | ast::StmtKind::Expr(..) | ast::StmtKind::Semi(..) => { | |
157 | let attrs = get_attrs_from_stmt(stmt.as_ast_node()); | |
158 | if contains_skip(attrs) { | |
159 | self.push_skipped_with_span( | |
160 | attrs, | |
161 | stmt.span(), | |
162 | get_span_without_attrs(stmt.as_ast_node()), | |
163 | ); | |
164 | } else { | |
165 | let shape = self.shape(); | |
166 | let rewrite = self.with_context(|ctx| stmt.rewrite(&ctx, shape)); | |
167 | self.push_rewrite(stmt.span(), rewrite) | |
168 | } | |
169 | } | |
170 | ast::StmtKind::MacCall(ref mac_stmt) => { | |
171 | if self.visit_attrs(&mac_stmt.attrs, ast::AttrStyle::Outer) { | |
172 | self.push_skipped_with_span( | |
173 | &mac_stmt.attrs, | |
174 | stmt.span(), | |
175 | get_span_without_attrs(stmt.as_ast_node()), | |
176 | ); | |
177 | } else { | |
178 | self.visit_mac(&mac_stmt.mac, None, MacroPosition::Statement); | |
179 | } | |
180 | self.format_missing(stmt.span().hi()); | |
181 | } | |
182 | ast::StmtKind::Empty => (), | |
183 | } | |
184 | } | |
185 | ||
186 | /// Remove spaces between the opening brace and the first statement or the inner attribute | |
187 | /// of the block. | |
188 | fn trim_spaces_after_opening_brace( | |
189 | &mut self, | |
190 | b: &ast::Block, | |
191 | inner_attrs: Option<&[ast::Attribute]>, | |
192 | ) { | |
193 | if let Some(first_stmt) = b.stmts.first() { | |
194 | let hi = inner_attrs | |
195 | .and_then(|attrs| inner_attributes(attrs).first().map(|attr| attr.span.lo())) | |
196 | .unwrap_or_else(|| first_stmt.span().lo()); | |
197 | let missing_span = self.next_span(hi); | |
198 | let snippet = self.snippet(missing_span); | |
199 | let len = CommentCodeSlices::new(snippet) | |
200 | .nth(0) | |
201 | .and_then(|(kind, _, s)| { | |
202 | if kind == CodeCharKind::Normal { | |
203 | s.rfind('\n') | |
204 | } else { | |
205 | None | |
206 | } | |
207 | }); | |
208 | if let Some(len) = len { | |
209 | self.last_pos = self.last_pos + BytePos::from_usize(len); | |
210 | } | |
211 | } | |
212 | } | |
213 | ||
214 | pub(crate) fn visit_block( | |
215 | &mut self, | |
216 | b: &ast::Block, | |
217 | inner_attrs: Option<&[ast::Attribute]>, | |
218 | has_braces: bool, | |
219 | ) { | |
220 | debug!( | |
221 | "visit_block: {}", | |
222 | self.parse_sess.span_to_debug_info(b.span), | |
223 | ); | |
224 | ||
225 | // Check if this block has braces. | |
226 | let brace_compensation = BytePos(if has_braces { 1 } else { 0 }); | |
227 | ||
228 | self.last_pos = self.last_pos + brace_compensation; | |
229 | self.block_indent = self.block_indent.block_indent(self.config); | |
230 | self.push_str("{"); | |
231 | self.trim_spaces_after_opening_brace(b, inner_attrs); | |
232 | ||
233 | // Format inner attributes if available. | |
234 | if let Some(attrs) = inner_attrs { | |
235 | self.visit_attrs(attrs, ast::AttrStyle::Inner); | |
236 | } | |
237 | ||
238 | self.walk_block_stmts(b); | |
239 | ||
240 | if !b.stmts.is_empty() { | |
241 | if let Some(expr) = stmt_expr(&b.stmts[b.stmts.len() - 1]) { | |
242 | if utils::semicolon_for_expr(&self.get_context(), expr) { | |
243 | self.push_str(";"); | |
244 | } | |
245 | } | |
246 | } | |
247 | ||
248 | let rest_span = self.next_span(b.span.hi()); | |
249 | if out_of_file_lines_range!(self, rest_span) { | |
250 | self.push_str(self.snippet(rest_span)); | |
251 | self.block_indent = self.block_indent.block_unindent(self.config); | |
252 | } else { | |
253 | // Ignore the closing brace. | |
254 | let missing_span = self.next_span(b.span.hi() - brace_compensation); | |
255 | self.close_block(missing_span, self.unindent_comment_on_closing_brace(b)); | |
256 | } | |
257 | self.last_pos = source!(self, b.span).hi(); | |
258 | } | |
259 | ||
260 | fn close_block(&mut self, span: Span, unindent_comment: bool) { | |
261 | let config = self.config; | |
262 | ||
263 | let mut last_hi = span.lo(); | |
264 | let mut unindented = false; | |
265 | let mut prev_ends_with_newline = false; | |
266 | let mut extra_newline = false; | |
267 | ||
268 | let skip_normal = |s: &str| { | |
269 | let trimmed = s.trim(); | |
270 | trimmed.is_empty() || trimmed.chars().all(|c| c == ';') | |
271 | }; | |
272 | ||
273 | let comment_snippet = self.snippet(span); | |
274 | ||
275 | let align_to_right = if unindent_comment && contains_comment(&comment_snippet) { | |
276 | let first_lines = comment_snippet.splitn(2, '/').next().unwrap_or(""); | |
277 | last_line_width(first_lines) > last_line_width(&comment_snippet) | |
278 | } else { | |
279 | false | |
280 | }; | |
281 | ||
282 | for (kind, offset, sub_slice) in CommentCodeSlices::new(comment_snippet) { | |
283 | let sub_slice = transform_missing_snippet(config, sub_slice); | |
284 | ||
285 | debug!("close_block: {:?} {:?} {:?}", kind, offset, sub_slice); | |
286 | ||
287 | match kind { | |
288 | CodeCharKind::Comment => { | |
289 | if !unindented && unindent_comment && !align_to_right { | |
290 | unindented = true; | |
291 | self.block_indent = self.block_indent.block_unindent(config); | |
292 | } | |
293 | let span_in_between = mk_sp(last_hi, span.lo() + BytePos::from_usize(offset)); | |
294 | let snippet_in_between = self.snippet(span_in_between); | |
295 | let mut comment_on_same_line = !snippet_in_between.contains("\n"); | |
296 | ||
297 | let mut comment_shape = | |
298 | Shape::indented(self.block_indent, config).comment(config); | |
299 | if self.config.version() == Version::Two && comment_on_same_line { | |
300 | self.push_str(" "); | |
301 | // put the first line of the comment on the same line as the | |
302 | // block's last line | |
303 | match sub_slice.find("\n") { | |
304 | None => { | |
305 | self.push_str(&sub_slice); | |
306 | } | |
307 | Some(offset) if offset + 1 == sub_slice.len() => { | |
308 | self.push_str(&sub_slice[..offset]); | |
309 | } | |
310 | Some(offset) => { | |
311 | let first_line = &sub_slice[..offset]; | |
312 | self.push_str(first_line); | |
313 | self.push_str(&self.block_indent.to_string_with_newline(config)); | |
314 | ||
315 | // put the other lines below it, shaping it as needed | |
316 | let other_lines = &sub_slice[offset + 1..]; | |
317 | let comment_str = | |
318 | rewrite_comment(other_lines, false, comment_shape, config); | |
319 | match comment_str { | |
320 | Some(ref s) => self.push_str(s), | |
321 | None => self.push_str(other_lines), | |
322 | } | |
323 | } | |
324 | } | |
325 | } else { | |
326 | if comment_on_same_line { | |
327 | // 1 = a space before `//` | |
328 | let offset_len = 1 + last_line_width(&self.buffer) | |
329 | .saturating_sub(self.block_indent.width()); | |
330 | match comment_shape | |
331 | .visual_indent(offset_len) | |
332 | .sub_width(offset_len) | |
333 | { | |
334 | Some(shp) => comment_shape = shp, | |
335 | None => comment_on_same_line = false, | |
336 | } | |
337 | }; | |
338 | ||
339 | if comment_on_same_line { | |
340 | self.push_str(" "); | |
341 | } else { | |
342 | if count_newlines(snippet_in_between) >= 2 || extra_newline { | |
343 | self.push_str("\n"); | |
344 | } | |
345 | self.push_str(&self.block_indent.to_string_with_newline(config)); | |
346 | } | |
347 | ||
348 | let comment_str = rewrite_comment(&sub_slice, false, comment_shape, config); | |
349 | match comment_str { | |
350 | Some(ref s) => self.push_str(s), | |
351 | None => self.push_str(&sub_slice), | |
352 | } | |
353 | } | |
354 | } | |
355 | CodeCharKind::Normal if skip_normal(&sub_slice) => { | |
356 | extra_newline = prev_ends_with_newline && sub_slice.contains('\n'); | |
357 | continue; | |
358 | } | |
359 | CodeCharKind::Normal => { | |
360 | self.push_str(&self.block_indent.to_string_with_newline(config)); | |
361 | self.push_str(sub_slice.trim()); | |
362 | } | |
363 | } | |
364 | prev_ends_with_newline = sub_slice.ends_with('\n'); | |
365 | extra_newline = false; | |
366 | last_hi = span.lo() + BytePos::from_usize(offset + sub_slice.len()); | |
367 | } | |
368 | if unindented { | |
369 | self.block_indent = self.block_indent.block_indent(self.config); | |
370 | } | |
371 | self.block_indent = self.block_indent.block_unindent(self.config); | |
372 | self.push_str(&self.block_indent.to_string_with_newline(config)); | |
373 | self.push_str("}"); | |
374 | } | |
375 | ||
376 | fn unindent_comment_on_closing_brace(&self, b: &ast::Block) -> bool { | |
377 | self.is_if_else_block && !b.stmts.is_empty() | |
378 | } | |
379 | ||
380 | // Note that this only gets called for function definitions. Required methods | |
381 | // on traits do not get handled here. | |
382 | pub(crate) fn visit_fn( | |
383 | &mut self, | |
384 | fk: visit::FnKind<'_>, | |
385 | generics: &ast::Generics, | |
386 | fd: &ast::FnDecl, | |
387 | s: Span, | |
388 | defaultness: ast::Defaultness, | |
389 | inner_attrs: Option<&[ast::Attribute]>, | |
390 | ) { | |
391 | let indent = self.block_indent; | |
392 | let block; | |
393 | let rewrite = match fk { | |
394 | visit::FnKind::Fn(_, ident, _, _, Some(ref b)) => { | |
395 | block = b; | |
396 | self.rewrite_fn_before_block( | |
397 | indent, | |
398 | ident, | |
399 | &FnSig::from_fn_kind(&fk, generics, fd, defaultness), | |
400 | mk_sp(s.lo(), b.span.lo()), | |
401 | ) | |
402 | } | |
403 | _ => unreachable!(), | |
404 | }; | |
405 | ||
406 | if let Some((fn_str, fn_brace_style)) = rewrite { | |
407 | self.format_missing_with_indent(source!(self, s).lo()); | |
408 | ||
409 | if let Some(rw) = self.single_line_fn(&fn_str, block, inner_attrs) { | |
410 | self.push_str(&rw); | |
411 | self.last_pos = s.hi(); | |
412 | return; | |
413 | } | |
414 | ||
415 | self.push_str(&fn_str); | |
416 | match fn_brace_style { | |
417 | FnBraceStyle::SameLine => self.push_str(" "), | |
418 | FnBraceStyle::NextLine => { | |
419 | self.push_str(&self.block_indent.to_string_with_newline(self.config)) | |
420 | } | |
421 | _ => unreachable!(), | |
422 | } | |
423 | self.last_pos = source!(self, block.span).lo(); | |
424 | } else { | |
425 | self.format_missing(source!(self, block.span).lo()); | |
426 | } | |
427 | ||
428 | self.visit_block(block, inner_attrs, true) | |
429 | } | |
430 | ||
431 | pub(crate) fn visit_item(&mut self, item: &ast::Item) { | |
432 | skip_out_of_file_lines_range_visitor!(self, item.span); | |
433 | ||
434 | // This is where we bail out if there is a skip attribute. This is only | |
435 | // complex in the module case. It is complex because the module could be | |
436 | // in a separate file and there might be attributes in both files, but | |
437 | // the AST lumps them all together. | |
438 | let filtered_attrs; | |
439 | let mut attrs = &item.attrs; | |
440 | let skip_context_saved = self.skip_context.clone(); | |
441 | self.skip_context.update_with_attrs(&attrs); | |
442 | ||
443 | let should_visit_node_again = match item.kind { | |
444 | // For use/extern crate items, skip rewriting attributes but check for a skip attribute. | |
445 | ast::ItemKind::Use(..) | ast::ItemKind::ExternCrate(_) => { | |
446 | if contains_skip(attrs) { | |
447 | self.push_skipped_with_span(attrs.as_slice(), item.span(), item.span()); | |
448 | false | |
449 | } else { | |
450 | true | |
451 | } | |
452 | } | |
453 | // Module is inline, in this case we treat it like any other item. | |
454 | _ if !is_mod_decl(item) => { | |
455 | if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) { | |
456 | self.push_skipped_with_span(item.attrs.as_slice(), item.span(), item.span()); | |
457 | false | |
458 | } else { | |
459 | true | |
460 | } | |
461 | } | |
462 | // Module is not inline, but should be skipped. | |
463 | ast::ItemKind::Mod(..) if contains_skip(&item.attrs) => false, | |
464 | // Module is not inline and should not be skipped. We want | |
465 | // to process only the attributes in the current file. | |
466 | ast::ItemKind::Mod(..) => { | |
467 | filtered_attrs = filter_inline_attrs(&item.attrs, item.span()); | |
468 | // Assert because if we should skip it should be caught by | |
469 | // the above case. | |
470 | assert!(!self.visit_attrs(&filtered_attrs, ast::AttrStyle::Outer)); | |
471 | attrs = &filtered_attrs; | |
472 | true | |
473 | } | |
474 | _ => { | |
475 | if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) { | |
476 | self.push_skipped_with_span(item.attrs.as_slice(), item.span(), item.span()); | |
477 | false | |
478 | } else { | |
479 | true | |
480 | } | |
481 | } | |
482 | }; | |
483 | ||
484 | // TODO(calebcartwright): consider enabling box_patterns feature gate | |
485 | if should_visit_node_again { | |
486 | match item.kind { | |
487 | ast::ItemKind::Use(ref tree) => self.format_import(item, tree), | |
488 | ast::ItemKind::Impl { .. } => { | |
489 | let block_indent = self.block_indent; | |
490 | let rw = self.with_context(|ctx| format_impl(&ctx, item, block_indent)); | |
491 | self.push_rewrite(item.span, rw); | |
492 | } | |
493 | ast::ItemKind::Trait(..) => { | |
494 | let block_indent = self.block_indent; | |
495 | let rw = self.with_context(|ctx| format_trait(&ctx, item, block_indent)); | |
496 | self.push_rewrite(item.span, rw); | |
497 | } | |
498 | ast::ItemKind::TraitAlias(ref generics, ref generic_bounds) => { | |
499 | let shape = Shape::indented(self.block_indent, self.config); | |
500 | let rw = format_trait_alias( | |
501 | &self.get_context(), | |
502 | item.ident, | |
503 | &item.vis, | |
504 | generics, | |
505 | generic_bounds, | |
506 | shape, | |
507 | ); | |
508 | self.push_rewrite(item.span, rw); | |
509 | } | |
510 | ast::ItemKind::ExternCrate(_) => { | |
511 | let rw = rewrite_extern_crate(&self.get_context(), item, self.shape()); | |
512 | let span = if attrs.is_empty() { | |
513 | item.span | |
514 | } else { | |
515 | mk_sp(attrs[0].span.lo(), item.span.hi()) | |
516 | }; | |
517 | self.push_rewrite(span, rw); | |
518 | } | |
519 | ast::ItemKind::Struct(..) | ast::ItemKind::Union(..) => { | |
520 | self.visit_struct(&StructParts::from_item(item)); | |
521 | } | |
522 | ast::ItemKind::Enum(ref def, ref generics) => { | |
523 | self.format_missing_with_indent(source!(self, item.span).lo()); | |
524 | self.visit_enum(item.ident, &item.vis, def, generics, item.span); | |
525 | self.last_pos = source!(self, item.span).hi(); | |
526 | } | |
527 | ast::ItemKind::Mod(ref module) => { | |
528 | let is_inline = !is_mod_decl(item); | |
529 | self.format_missing_with_indent(source!(self, item.span).lo()); | |
530 | self.format_mod(module, &item.vis, item.span, item.ident, attrs, is_inline); | |
531 | } | |
532 | ast::ItemKind::MacCall(ref mac) => { | |
533 | self.visit_mac(mac, Some(item.ident), MacroPosition::Item); | |
534 | } | |
535 | ast::ItemKind::ForeignMod(ref foreign_mod) => { | |
536 | self.format_missing_with_indent(source!(self, item.span).lo()); | |
537 | self.format_foreign_mod(foreign_mod, item.span); | |
538 | } | |
539 | ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => { | |
540 | self.visit_static(&StaticParts::from_item(item)); | |
541 | } | |
542 | ast::ItemKind::Fn(ref fn_kind) => { | |
543 | let ast::FnKind(defaultness, ref fn_signature, ref generics, ref block) = | |
544 | **fn_kind; | |
545 | if let Some(ref body) = block { | |
546 | let inner_attrs = inner_attributes(&item.attrs); | |
547 | let fn_ctxt = match fn_signature.header.ext { | |
548 | ast::Extern::None => visit::FnCtxt::Free, | |
549 | _ => visit::FnCtxt::Foreign, | |
550 | }; | |
551 | self.visit_fn( | |
552 | visit::FnKind::Fn( | |
553 | fn_ctxt, | |
554 | item.ident, | |
555 | &fn_signature, | |
556 | &item.vis, | |
557 | Some(body), | |
558 | ), | |
559 | generics, | |
560 | &fn_signature.decl, | |
561 | item.span, | |
562 | defaultness, | |
563 | Some(&inner_attrs), | |
564 | ) | |
565 | } else { | |
566 | let indent = self.block_indent; | |
567 | let rewrite = self.rewrite_required_fn( | |
568 | indent, | |
569 | item.ident, | |
570 | &fn_signature, | |
571 | generics, | |
572 | item.span, | |
573 | ); | |
574 | self.push_rewrite(item.span, rewrite); | |
575 | } | |
576 | } | |
577 | ast::ItemKind::TyAlias(ref alias_kind) => { | |
578 | let ast::TyAliasKind(_, ref generics, ref generic_bounds, ref ty) = | |
579 | **alias_kind; | |
580 | match ty { | |
581 | Some(ty) => { | |
582 | let rewrite = rewrite_type_alias( | |
583 | item.ident, | |
584 | Some(&*ty), | |
585 | generics, | |
586 | Some(generic_bounds), | |
587 | &self.get_context(), | |
588 | self.block_indent, | |
589 | &item.vis, | |
590 | item.span, | |
591 | ); | |
592 | self.push_rewrite(item.span, rewrite); | |
593 | } | |
594 | None => { | |
595 | let rewrite = rewrite_opaque_type( | |
596 | &self.get_context(), | |
597 | self.block_indent, | |
598 | item.ident, | |
599 | generic_bounds, | |
600 | generics, | |
601 | &item.vis, | |
602 | item.span, | |
603 | ); | |
604 | self.push_rewrite(item.span, rewrite); | |
605 | } | |
606 | } | |
607 | } | |
608 | ast::ItemKind::GlobalAsm(..) => { | |
609 | let snippet = Some(self.snippet(item.span).to_owned()); | |
610 | self.push_rewrite(item.span, snippet); | |
611 | } | |
612 | ast::ItemKind::MacroDef(ref def) => { | |
613 | let rewrite = rewrite_macro_def( | |
614 | &self.get_context(), | |
615 | self.shape(), | |
616 | self.block_indent, | |
617 | def, | |
618 | item.ident, | |
619 | &item.vis, | |
620 | item.span, | |
621 | ); | |
622 | self.push_rewrite(item.span, rewrite); | |
623 | } | |
624 | }; | |
625 | } | |
626 | self.skip_context = skip_context_saved; | |
627 | } | |
628 | ||
629 | pub(crate) fn visit_trait_item(&mut self, ti: &ast::AssocItem) { | |
630 | skip_out_of_file_lines_range_visitor!(self, ti.span); | |
631 | ||
632 | if self.visit_attrs(&ti.attrs, ast::AttrStyle::Outer) { | |
633 | self.push_skipped_with_span(ti.attrs.as_slice(), ti.span(), ti.span()); | |
634 | return; | |
635 | } | |
636 | ||
637 | // TODO(calebcartwright): consider enabling box_patterns feature gate | |
638 | match ti.kind { | |
639 | ast::AssocItemKind::Const(..) => self.visit_static(&StaticParts::from_trait_item(ti)), | |
640 | ast::AssocItemKind::Fn(ref fn_kind) => { | |
641 | let ast::FnKind(defaultness, ref sig, ref generics, ref block) = **fn_kind; | |
642 | if let Some(ref body) = block { | |
643 | let inner_attrs = inner_attributes(&ti.attrs); | |
644 | let vis = ast::Visibility { | |
645 | kind: ast::VisibilityKind::Inherited, | |
646 | span: DUMMY_SP, | |
647 | tokens: None, | |
648 | }; | |
649 | let fn_ctxt = visit::FnCtxt::Assoc(visit::AssocCtxt::Trait); | |
650 | self.visit_fn( | |
651 | visit::FnKind::Fn(fn_ctxt, ti.ident, sig, &vis, Some(body)), | |
652 | generics, | |
653 | &sig.decl, | |
654 | ti.span, | |
655 | defaultness, | |
656 | Some(&inner_attrs), | |
657 | ); | |
658 | } else { | |
659 | let indent = self.block_indent; | |
660 | let rewrite = | |
661 | self.rewrite_required_fn(indent, ti.ident, sig, generics, ti.span); | |
662 | self.push_rewrite(ti.span, rewrite); | |
663 | } | |
664 | } | |
665 | ast::AssocItemKind::TyAlias(ref ty_alias_kind) => { | |
666 | let ast::TyAliasKind(_, ref generics, ref generic_bounds, ref type_default) = | |
667 | **ty_alias_kind; | |
668 | let rewrite = rewrite_type_alias( | |
669 | ti.ident, | |
670 | type_default.as_ref(), | |
671 | generics, | |
672 | Some(generic_bounds), | |
673 | &self.get_context(), | |
674 | self.block_indent, | |
675 | &ti.vis, | |
676 | ti.span, | |
677 | ); | |
678 | self.push_rewrite(ti.span, rewrite); | |
679 | } | |
680 | ast::AssocItemKind::MacCall(ref mac) => { | |
681 | self.visit_mac(mac, Some(ti.ident), MacroPosition::Item); | |
682 | } | |
683 | } | |
684 | } | |
685 | ||
686 | pub(crate) fn visit_impl_item(&mut self, ii: &ast::AssocItem) { | |
687 | skip_out_of_file_lines_range_visitor!(self, ii.span); | |
688 | ||
689 | if self.visit_attrs(&ii.attrs, ast::AttrStyle::Outer) { | |
690 | self.push_skipped_with_span(ii.attrs.as_slice(), ii.span, ii.span); | |
691 | return; | |
692 | } | |
693 | ||
694 | match ii.kind { | |
695 | ast::AssocItemKind::Fn(ref fn_kind) => { | |
696 | let ast::FnKind(defaultness, ref sig, ref generics, ref block) = **fn_kind; | |
697 | if let Some(ref body) = block { | |
698 | let inner_attrs = inner_attributes(&ii.attrs); | |
699 | let fn_ctxt = visit::FnCtxt::Assoc(visit::AssocCtxt::Impl); | |
700 | self.visit_fn( | |
701 | visit::FnKind::Fn(fn_ctxt, ii.ident, sig, &ii.vis, Some(body)), | |
702 | generics, | |
703 | &sig.decl, | |
704 | ii.span, | |
705 | defaultness, | |
706 | Some(&inner_attrs), | |
707 | ); | |
708 | } else { | |
709 | let indent = self.block_indent; | |
710 | let rewrite = | |
711 | self.rewrite_required_fn(indent, ii.ident, sig, generics, ii.span); | |
712 | self.push_rewrite(ii.span, rewrite); | |
713 | } | |
714 | } | |
715 | ast::AssocItemKind::Const(..) => self.visit_static(&StaticParts::from_impl_item(ii)), | |
716 | ast::AssocItemKind::TyAlias(ref ty_alias_kind) => { | |
717 | let ast::TyAliasKind(defaultness, ref generics, _, ref ty) = **ty_alias_kind; | |
718 | let rewrite_associated = || { | |
719 | rewrite_associated_impl_type( | |
720 | ii.ident, | |
721 | &ii.vis, | |
722 | defaultness, | |
723 | ty.as_ref(), | |
724 | &generics, | |
725 | &self.get_context(), | |
726 | self.block_indent, | |
727 | ii.span, | |
728 | ) | |
729 | }; | |
730 | let rewrite = match ty { | |
731 | None => rewrite_associated(), | |
732 | Some(ty) => match ty.kind { | |
733 | ast::TyKind::ImplTrait(_, ref bounds) => rewrite_opaque_impl_type( | |
734 | &self.get_context(), | |
735 | ii.ident, | |
736 | generics, | |
737 | bounds, | |
738 | self.block_indent, | |
739 | ), | |
740 | _ => rewrite_associated(), | |
741 | }, | |
742 | }; | |
743 | self.push_rewrite(ii.span, rewrite); | |
744 | } | |
745 | ast::AssocItemKind::MacCall(ref mac) => { | |
746 | self.visit_mac(mac, Some(ii.ident), MacroPosition::Item); | |
747 | } | |
748 | } | |
749 | } | |
750 | ||
751 | fn visit_mac(&mut self, mac: &ast::MacCall, ident: Option<symbol::Ident>, pos: MacroPosition) { | |
752 | skip_out_of_file_lines_range_visitor!(self, mac.span()); | |
753 | ||
754 | // 1 = ; | |
755 | let shape = self.shape().saturating_sub_width(1); | |
756 | let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos)); | |
757 | // As of v638 of the rustc-ap-* crates, the associated span no longer includes | |
758 | // the trailing semicolon. This determines the correct span to ensure scenarios | |
759 | // with whitespace between the delimiters and trailing semi (i.e. `foo!(abc) ;`) | |
760 | // are formatted correctly. | |
761 | let (span, rewrite) = match macro_style(mac, &self.get_context()) { | |
762 | DelimToken::Bracket | DelimToken::Paren if MacroPosition::Item == pos => { | |
763 | let search_span = mk_sp(mac.span().hi(), self.snippet_provider.end_pos()); | |
764 | let hi = self.snippet_provider.span_before(search_span, ";"); | |
765 | let target_span = mk_sp(mac.span().lo(), hi + BytePos(1)); | |
766 | let rewrite = rewrite.map(|rw| { | |
767 | if !rw.ends_with(";") { | |
768 | format!("{};", rw) | |
769 | } else { | |
770 | rw | |
771 | } | |
772 | }); | |
773 | (target_span, rewrite) | |
774 | } | |
775 | _ => (mac.span(), rewrite), | |
776 | }; | |
777 | ||
778 | self.push_rewrite(span, rewrite); | |
779 | } | |
780 | ||
781 | pub(crate) fn push_str(&mut self, s: &str) { | |
782 | self.line_number += count_newlines(s); | |
783 | self.buffer.push_str(s); | |
784 | } | |
785 | ||
786 | #[allow(clippy::needless_pass_by_value)] | |
787 | fn push_rewrite_inner(&mut self, span: Span, rewrite: Option<String>) { | |
788 | if let Some(ref s) = rewrite { | |
789 | self.push_str(s); | |
790 | } else { | |
791 | let snippet = self.snippet(span); | |
792 | self.push_str(snippet.trim()); | |
793 | } | |
794 | self.last_pos = source!(self, span).hi(); | |
795 | } | |
796 | ||
797 | pub(crate) fn push_rewrite(&mut self, span: Span, rewrite: Option<String>) { | |
798 | self.format_missing_with_indent(source!(self, span).lo()); | |
799 | self.push_rewrite_inner(span, rewrite); | |
800 | } | |
801 | ||
802 | pub(crate) fn push_skipped_with_span( | |
803 | &mut self, | |
804 | attrs: &[ast::Attribute], | |
805 | item_span: Span, | |
806 | main_span: Span, | |
807 | ) { | |
808 | self.format_missing_with_indent(source!(self, item_span).lo()); | |
809 | // do not take into account the lines with attributes as part of the skipped range | |
810 | let attrs_end = attrs | |
811 | .iter() | |
812 | .map(|attr| self.parse_sess.line_of_byte_pos(attr.span.hi())) | |
813 | .max() | |
814 | .unwrap_or(1); | |
815 | let first_line = self.parse_sess.line_of_byte_pos(main_span.lo()); | |
816 | // Statement can start after some newlines and/or spaces | |
817 | // or it can be on the same line as the last attribute. | |
818 | // So here we need to take a minimum between the two. | |
819 | let lo = std::cmp::min(attrs_end + 1, first_line); | |
820 | self.push_rewrite_inner(item_span, None); | |
821 | let hi = self.line_number + 1; | |
822 | self.skipped_range.borrow_mut().push((lo, hi)); | |
823 | } | |
824 | ||
825 | pub(crate) fn from_context(ctx: &'a RewriteContext<'_>) -> FmtVisitor<'a> { | |
826 | let mut visitor = FmtVisitor::from_parse_sess( | |
827 | ctx.parse_sess, | |
828 | ctx.config, | |
829 | ctx.snippet_provider, | |
830 | ctx.report.clone(), | |
831 | ); | |
832 | visitor.skip_context.update(ctx.skip_context.clone()); | |
833 | visitor.set_parent_context(ctx); | |
834 | visitor | |
835 | } | |
836 | ||
837 | pub(crate) fn from_parse_sess( | |
838 | parse_session: &'a ParseSess, | |
839 | config: &'a Config, | |
840 | snippet_provider: &'a SnippetProvider, | |
841 | report: FormatReport, | |
842 | ) -> FmtVisitor<'a> { | |
843 | FmtVisitor { | |
844 | parent_context: None, | |
845 | parse_sess: parse_session, | |
846 | buffer: String::with_capacity(snippet_provider.big_snippet.len() * 2), | |
847 | last_pos: BytePos(0), | |
848 | block_indent: Indent::empty(), | |
849 | config, | |
850 | is_if_else_block: false, | |
851 | snippet_provider, | |
852 | line_number: 0, | |
853 | skipped_range: Rc::new(RefCell::new(vec![])), | |
854 | is_macro_def: false, | |
855 | macro_rewrite_failure: false, | |
856 | report, | |
857 | skip_context: Default::default(), | |
858 | } | |
859 | } | |
860 | ||
861 | pub(crate) fn opt_snippet(&'b self, span: Span) -> Option<&'a str> { | |
862 | self.snippet_provider.span_to_snippet(span) | |
863 | } | |
864 | ||
865 | pub(crate) fn snippet(&'b self, span: Span) -> &'a str { | |
866 | self.opt_snippet(span).unwrap() | |
867 | } | |
868 | ||
869 | // Returns true if we should skip the following item. | |
870 | pub(crate) fn visit_attrs(&mut self, attrs: &[ast::Attribute], style: ast::AttrStyle) -> bool { | |
871 | for attr in attrs { | |
872 | if attr.has_name(depr_skip_annotation()) { | |
873 | let file_name = self.parse_sess.span_to_filename(attr.span); | |
874 | self.report.append( | |
875 | file_name, | |
876 | vec![FormattingError::from_span( | |
877 | attr.span, | |
878 | self.parse_sess, | |
879 | ErrorKind::DeprecatedAttr, | |
880 | )], | |
881 | ); | |
882 | } else { | |
883 | match &attr.kind { | |
884 | ast::AttrKind::Normal(ref attribute_item, _) | |
885 | if self.is_unknown_rustfmt_attr(&attribute_item.path.segments) => | |
886 | { | |
887 | let file_name = self.parse_sess.span_to_filename(attr.span); | |
888 | self.report.append( | |
889 | file_name, | |
890 | vec![FormattingError::from_span( | |
891 | attr.span, | |
892 | self.parse_sess, | |
893 | ErrorKind::BadAttr, | |
894 | )], | |
895 | ); | |
896 | } | |
897 | _ => (), | |
898 | } | |
899 | } | |
900 | } | |
901 | if contains_skip(attrs) { | |
902 | return true; | |
903 | } | |
904 | ||
905 | let attrs: Vec<_> = attrs.iter().filter(|a| a.style == style).cloned().collect(); | |
906 | if attrs.is_empty() { | |
907 | return false; | |
908 | } | |
909 | ||
910 | let rewrite = attrs.rewrite(&self.get_context(), self.shape()); | |
911 | let span = mk_sp(attrs[0].span.lo(), attrs[attrs.len() - 1].span.hi()); | |
912 | self.push_rewrite(span, rewrite); | |
913 | ||
914 | false | |
915 | } | |
916 | ||
917 | fn is_unknown_rustfmt_attr(&self, segments: &[ast::PathSegment]) -> bool { | |
918 | if segments[0].ident.to_string() != "rustfmt" { | |
919 | return false; | |
920 | } | |
921 | !is_skip_attr(segments) | |
922 | } | |
923 | ||
924 | fn walk_mod_items(&mut self, m: &ast::Mod) { | |
925 | self.visit_items_with_reordering(&ptr_vec_to_ref_vec(&m.items)); | |
926 | } | |
927 | ||
928 | fn walk_stmts(&mut self, stmts: &[Stmt<'_>], include_current_empty_semi: bool) { | |
929 | if stmts.is_empty() { | |
930 | return; | |
931 | } | |
932 | ||
933 | // Extract leading `use ...;`. | |
934 | let items: Vec<_> = stmts | |
935 | .iter() | |
936 | .take_while(|stmt| stmt.to_item().map_or(false, is_use_item)) | |
937 | .filter_map(|stmt| stmt.to_item()) | |
938 | .collect(); | |
939 | ||
940 | if items.is_empty() { | |
941 | self.visit_stmt(&stmts[0], include_current_empty_semi); | |
942 | ||
943 | // FIXME(calebcartwright 2021-01-03) - This exists strictly to maintain legacy | |
944 | // formatting where rustfmt would preserve redundant semicolons on Items in a | |
945 | // statement position. | |
946 | // | |
947 | // Starting in rustc-ap-* v692 (~2020-12-01) the rustc parser now parses this as | |
948 | // two separate statements (Item and Empty kinds), whereas before it was parsed as | |
949 | // a single statement with the statement's span including the redundant semicolon. | |
950 | // | |
951 | // rustfmt typically tosses unnecessary/redundant semicolons, and eventually we | |
952 | // should toss these as well, but doing so at this time would | |
953 | // break the Stability Guarantee | |
954 | // N.B. This could be updated to utilize the version gates. | |
955 | let include_next_empty = if stmts.len() > 1 { | |
956 | match (&stmts[0].as_ast_node().kind, &stmts[1].as_ast_node().kind) { | |
957 | (ast::StmtKind::Item(_), ast::StmtKind::Empty) => true, | |
958 | _ => false, | |
959 | } | |
960 | } else { | |
961 | false | |
962 | }; | |
963 | ||
964 | self.walk_stmts(&stmts[1..], include_next_empty); | |
965 | } else { | |
966 | self.visit_items_with_reordering(&items); | |
967 | self.walk_stmts(&stmts[items.len()..], false); | |
968 | } | |
969 | } | |
970 | ||
971 | fn walk_block_stmts(&mut self, b: &ast::Block) { | |
972 | self.walk_stmts(&Stmt::from_ast_nodes(b.stmts.iter()), false) | |
973 | } | |
974 | ||
975 | fn format_mod( | |
976 | &mut self, | |
977 | m: &ast::Mod, | |
978 | vis: &ast::Visibility, | |
979 | s: Span, | |
980 | ident: symbol::Ident, | |
981 | attrs: &[ast::Attribute], | |
982 | is_internal: bool, | |
983 | ) { | |
984 | let vis_str = utils::format_visibility(&self.get_context(), vis); | |
985 | self.push_str(&*vis_str); | |
986 | self.push_str(format_unsafety(m.unsafety)); | |
987 | self.push_str("mod "); | |
988 | // Calling `to_owned()` to work around borrow checker. | |
989 | let ident_str = rewrite_ident(&self.get_context(), ident).to_owned(); | |
990 | self.push_str(&ident_str); | |
991 | ||
992 | if is_internal { | |
993 | match self.config.brace_style() { | |
994 | BraceStyle::AlwaysNextLine => { | |
995 | let indent_str = self.block_indent.to_string_with_newline(self.config); | |
996 | self.push_str(&indent_str); | |
997 | self.push_str("{"); | |
998 | } | |
999 | _ => self.push_str(" {"), | |
1000 | } | |
1001 | // Hackery to account for the closing }. | |
1002 | let mod_lo = self.snippet_provider.span_after(source!(self, s), "{"); | |
1003 | let body_snippet = | |
1004 | self.snippet(mk_sp(mod_lo, source!(self, m.inner).hi() - BytePos(1))); | |
1005 | let body_snippet = body_snippet.trim(); | |
1006 | if body_snippet.is_empty() { | |
1007 | self.push_str("}"); | |
1008 | } else { | |
1009 | self.last_pos = mod_lo; | |
1010 | self.block_indent = self.block_indent.block_indent(self.config); | |
1011 | self.visit_attrs(attrs, ast::AttrStyle::Inner); | |
1012 | self.walk_mod_items(m); | |
1013 | let missing_span = self.next_span(m.inner.hi() - BytePos(1)); | |
1014 | self.close_block(missing_span, false); | |
1015 | } | |
1016 | self.last_pos = source!(self, m.inner).hi(); | |
1017 | } else { | |
1018 | self.push_str(";"); | |
1019 | self.last_pos = source!(self, s).hi(); | |
1020 | } | |
1021 | } | |
1022 | ||
1023 | pub(crate) fn format_separate_mod(&mut self, m: &Module<'_>, end_pos: BytePos) { | |
1024 | self.block_indent = Indent::empty(); | |
1025 | if self.visit_attrs(m.attrs(), ast::AttrStyle::Inner) { | |
1026 | self.push_skipped_with_span(m.attrs(), m.as_ref().inner, m.as_ref().inner); | |
1027 | } else { | |
1028 | self.walk_mod_items(m.as_ref()); | |
1029 | self.format_missing_with_indent(end_pos); | |
1030 | } | |
1031 | } | |
1032 | ||
1033 | pub(crate) fn skip_empty_lines(&mut self, end_pos: BytePos) { | |
1034 | while let Some(pos) = self | |
1035 | .snippet_provider | |
1036 | .opt_span_after(self.next_span(end_pos), "\n") | |
1037 | { | |
1038 | if let Some(snippet) = self.opt_snippet(self.next_span(pos)) { | |
1039 | if snippet.trim().is_empty() { | |
1040 | self.last_pos = pos; | |
1041 | } else { | |
1042 | return; | |
1043 | } | |
1044 | } | |
1045 | } | |
1046 | } | |
1047 | ||
1048 | pub(crate) fn with_context<F>(&mut self, f: F) -> Option<String> | |
1049 | where | |
1050 | F: Fn(&RewriteContext<'_>) -> Option<String>, | |
1051 | { | |
1052 | let context = self.get_context(); | |
1053 | let result = f(&context); | |
1054 | ||
1055 | self.macro_rewrite_failure |= context.macro_rewrite_failure.get(); | |
1056 | result | |
1057 | } | |
1058 | ||
1059 | pub(crate) fn get_context(&self) -> RewriteContext<'_> { | |
1060 | RewriteContext { | |
1061 | parse_sess: self.parse_sess, | |
1062 | config: self.config, | |
1063 | inside_macro: Rc::new(Cell::new(false)), | |
1064 | use_block: Cell::new(false), | |
1065 | is_if_else_block: Cell::new(false), | |
1066 | force_one_line_chain: Cell::new(false), | |
1067 | snippet_provider: self.snippet_provider, | |
1068 | macro_rewrite_failure: Cell::new(false), | |
1069 | is_macro_def: self.is_macro_def, | |
1070 | report: self.report.clone(), | |
1071 | skip_context: self.skip_context.clone(), | |
1072 | skipped_range: self.skipped_range.clone(), | |
1073 | } | |
1074 | } | |
1075 | } |