]>
Commit | Line | Data |
---|---|---|
f20569fa XL |
1 | use std::cell::{Cell, RefCell}; |
2 | use std::rc::Rc; | |
3 | ||
04454e1e | 4 | use rustc_ast::{ast, token::Delimiter, visit}; |
cdc7bbd5 | 5 | use rustc_data_structures::sync::Lrc; |
3c0e092e | 6 | use rustc_span::{symbol, BytePos, Pos, Span}; |
f20569fa XL |
7 | |
8 | use crate::attr::*; | |
9 | use crate::comment::{contains_comment, rewrite_comment, CodeCharKind, CommentCodeSlices}; | |
10 | use crate::config::Version; | |
11 | use crate::config::{BraceStyle, Config}; | |
12 | use crate::coverage::transform_missing_snippet; | |
13 | use crate::items::{ | |
3c0e092e XL |
14 | format_impl, format_trait, format_trait_alias, is_mod_decl, is_use_item, rewrite_extern_crate, |
15 | rewrite_type_alias, FnBraceStyle, FnSig, ItemVisitorKind, StaticParts, StructParts, | |
f20569fa XL |
16 | }; |
17 | use crate::macros::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition}; | |
18 | use crate::modules::Module; | |
a2a8927a | 19 | use crate::parse::session::ParseSess; |
f20569fa XL |
20 | use crate::rewrite::{Rewrite, RewriteContext}; |
21 | use crate::shape::{Indent, Shape}; | |
22 | use crate::skip::{is_skip_attr, SkipContext}; | |
23 | use crate::source_map::{LineRangeUtils, SpanUtils}; | |
24 | use crate::spanned::Spanned; | |
25 | use crate::stmt::Stmt; | |
f20569fa XL |
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. | |
cdc7bbd5 | 35 | big_snippet: Lrc<String>, |
f20569fa XL |
36 | /// A position of the start of `big_snippet`, used as an offset. |
37 | start_pos: usize, | |
94222f64 | 38 | /// An end position of the file that this snippet lives. |
f20569fa XL |
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 | ||
cdc7bbd5 | 49 | pub(crate) fn new(start_pos: BytePos, end_pos: BytePos, big_snippet: Lrc<String>) -> Self { |
f20569fa XL |
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(); | |
3c0e092e | 166 | let rewrite = self.with_context(|ctx| stmt.rewrite(ctx, shape)); |
f20569fa XL |
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) | |
94222f64 | 200 | .next() |
f20569fa XL |
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 | ||
3c0e092e | 275 | let align_to_right = if unindent_comment && contains_comment(comment_snippet) { |
f20569fa | 276 | let first_lines = comment_snippet.splitn(2, '/').next().unwrap_or(""); |
3c0e092e | 277 | last_line_width(first_lines) > last_line_width(comment_snippet) |
f20569fa XL |
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); | |
94222f64 | 295 | let mut comment_on_same_line = !snippet_in_between.contains('\n'); |
f20569fa XL |
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 | |
94222f64 | 303 | match sub_slice.find('\n') { |
f20569fa XL |
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<'_>, | |
f20569fa XL |
385 | fd: &ast::FnDecl, |
386 | s: Span, | |
387 | defaultness: ast::Defaultness, | |
388 | inner_attrs: Option<&[ast::Attribute]>, | |
389 | ) { | |
390 | let indent = self.block_indent; | |
391 | let block; | |
392 | let rewrite = match fk { | |
04454e1e | 393 | visit::FnKind::Fn(_, ident, _, _, _, Some(ref b)) => { |
f20569fa XL |
394 | block = b; |
395 | self.rewrite_fn_before_block( | |
396 | indent, | |
397 | ident, | |
04454e1e | 398 | &FnSig::from_fn_kind(&fk, fd, defaultness), |
f20569fa XL |
399 | mk_sp(s.lo(), b.span.lo()), |
400 | ) | |
401 | } | |
402 | _ => unreachable!(), | |
403 | }; | |
404 | ||
405 | if let Some((fn_str, fn_brace_style)) = rewrite { | |
406 | self.format_missing_with_indent(source!(self, s).lo()); | |
407 | ||
408 | if let Some(rw) = self.single_line_fn(&fn_str, block, inner_attrs) { | |
409 | self.push_str(&rw); | |
410 | self.last_pos = s.hi(); | |
411 | return; | |
412 | } | |
413 | ||
414 | self.push_str(&fn_str); | |
415 | match fn_brace_style { | |
416 | FnBraceStyle::SameLine => self.push_str(" "), | |
417 | FnBraceStyle::NextLine => { | |
418 | self.push_str(&self.block_indent.to_string_with_newline(self.config)) | |
419 | } | |
420 | _ => unreachable!(), | |
421 | } | |
422 | self.last_pos = source!(self, block.span).lo(); | |
423 | } else { | |
424 | self.format_missing(source!(self, block.span).lo()); | |
425 | } | |
426 | ||
427 | self.visit_block(block, inner_attrs, true) | |
428 | } | |
429 | ||
430 | pub(crate) fn visit_item(&mut self, item: &ast::Item) { | |
431 | skip_out_of_file_lines_range_visitor!(self, item.span); | |
432 | ||
433 | // This is where we bail out if there is a skip attribute. This is only | |
434 | // complex in the module case. It is complex because the module could be | |
435 | // in a separate file and there might be attributes in both files, but | |
436 | // the AST lumps them all together. | |
437 | let filtered_attrs; | |
438 | let mut attrs = &item.attrs; | |
439 | let skip_context_saved = self.skip_context.clone(); | |
3c0e092e | 440 | self.skip_context.update_with_attrs(attrs); |
f20569fa XL |
441 | |
442 | let should_visit_node_again = match item.kind { | |
443 | // For use/extern crate items, skip rewriting attributes but check for a skip attribute. | |
444 | ast::ItemKind::Use(..) | ast::ItemKind::ExternCrate(_) => { | |
445 | if contains_skip(attrs) { | |
446 | self.push_skipped_with_span(attrs.as_slice(), item.span(), item.span()); | |
447 | false | |
448 | } else { | |
449 | true | |
450 | } | |
451 | } | |
452 | // Module is inline, in this case we treat it like any other item. | |
453 | _ if !is_mod_decl(item) => { | |
454 | if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) { | |
455 | self.push_skipped_with_span(item.attrs.as_slice(), item.span(), item.span()); | |
456 | false | |
457 | } else { | |
458 | true | |
459 | } | |
460 | } | |
461 | // Module is not inline, but should be skipped. | |
462 | ast::ItemKind::Mod(..) if contains_skip(&item.attrs) => false, | |
463 | // Module is not inline and should not be skipped. We want | |
464 | // to process only the attributes in the current file. | |
465 | ast::ItemKind::Mod(..) => { | |
466 | filtered_attrs = filter_inline_attrs(&item.attrs, item.span()); | |
467 | // Assert because if we should skip it should be caught by | |
468 | // the above case. | |
469 | assert!(!self.visit_attrs(&filtered_attrs, ast::AttrStyle::Outer)); | |
470 | attrs = &filtered_attrs; | |
471 | true | |
472 | } | |
473 | _ => { | |
474 | if self.visit_attrs(&item.attrs, ast::AttrStyle::Outer) { | |
475 | self.push_skipped_with_span(item.attrs.as_slice(), item.span(), item.span()); | |
476 | false | |
477 | } else { | |
478 | true | |
479 | } | |
480 | } | |
481 | }; | |
482 | ||
483 | // TODO(calebcartwright): consider enabling box_patterns feature gate | |
484 | if should_visit_node_again { | |
485 | match item.kind { | |
486 | ast::ItemKind::Use(ref tree) => self.format_import(item, tree), | |
a2a8927a | 487 | ast::ItemKind::Impl(ref iimpl) => { |
f20569fa | 488 | let block_indent = self.block_indent; |
a2a8927a | 489 | let rw = self.with_context(|ctx| format_impl(ctx, item, iimpl, block_indent)); |
f20569fa XL |
490 | self.push_rewrite(item.span, rw); |
491 | } | |
492 | ast::ItemKind::Trait(..) => { | |
493 | let block_indent = self.block_indent; | |
3c0e092e | 494 | let rw = self.with_context(|ctx| format_trait(ctx, item, block_indent)); |
f20569fa XL |
495 | self.push_rewrite(item.span, rw); |
496 | } | |
497 | ast::ItemKind::TraitAlias(ref generics, ref generic_bounds) => { | |
498 | let shape = Shape::indented(self.block_indent, self.config); | |
499 | let rw = format_trait_alias( | |
500 | &self.get_context(), | |
501 | item.ident, | |
502 | &item.vis, | |
503 | generics, | |
504 | generic_bounds, | |
505 | shape, | |
506 | ); | |
507 | self.push_rewrite(item.span, rw); | |
508 | } | |
509 | ast::ItemKind::ExternCrate(_) => { | |
510 | let rw = rewrite_extern_crate(&self.get_context(), item, self.shape()); | |
511 | let span = if attrs.is_empty() { | |
512 | item.span | |
513 | } else { | |
514 | mk_sp(attrs[0].span.lo(), item.span.hi()) | |
515 | }; | |
516 | self.push_rewrite(span, rw); | |
517 | } | |
518 | ast::ItemKind::Struct(..) | ast::ItemKind::Union(..) => { | |
519 | self.visit_struct(&StructParts::from_item(item)); | |
520 | } | |
521 | ast::ItemKind::Enum(ref def, ref generics) => { | |
522 | self.format_missing_with_indent(source!(self, item.span).lo()); | |
523 | self.visit_enum(item.ident, &item.vis, def, generics, item.span); | |
524 | self.last_pos = source!(self, item.span).hi(); | |
525 | } | |
cdc7bbd5 | 526 | ast::ItemKind::Mod(unsafety, ref mod_kind) => { |
f20569fa | 527 | self.format_missing_with_indent(source!(self, item.span).lo()); |
cdc7bbd5 | 528 | self.format_mod(mod_kind, unsafety, &item.vis, item.span, item.ident, attrs); |
f20569fa XL |
529 | } |
530 | ast::ItemKind::MacCall(ref mac) => { | |
531 | self.visit_mac(mac, Some(item.ident), MacroPosition::Item); | |
532 | } | |
533 | ast::ItemKind::ForeignMod(ref foreign_mod) => { | |
534 | self.format_missing_with_indent(source!(self, item.span).lo()); | |
535 | self.format_foreign_mod(foreign_mod, item.span); | |
536 | } | |
537 | ast::ItemKind::Static(..) | ast::ItemKind::Const(..) => { | |
538 | self.visit_static(&StaticParts::from_item(item)); | |
539 | } | |
540 | ast::ItemKind::Fn(ref fn_kind) => { | |
3c0e092e XL |
541 | let ast::Fn { |
542 | defaultness, | |
543 | ref sig, | |
544 | ref generics, | |
545 | ref body, | |
546 | } = **fn_kind; | |
547 | if let Some(ref body) = body { | |
f20569fa | 548 | let inner_attrs = inner_attributes(&item.attrs); |
3c0e092e | 549 | let fn_ctxt = match sig.header.ext { |
f20569fa XL |
550 | ast::Extern::None => visit::FnCtxt::Free, |
551 | _ => visit::FnCtxt::Foreign, | |
552 | }; | |
553 | self.visit_fn( | |
04454e1e FG |
554 | visit::FnKind::Fn( |
555 | fn_ctxt, | |
556 | item.ident, | |
557 | sig, | |
558 | &item.vis, | |
559 | generics, | |
560 | Some(body), | |
561 | ), | |
3c0e092e | 562 | &sig.decl, |
f20569fa XL |
563 | item.span, |
564 | defaultness, | |
565 | Some(&inner_attrs), | |
566 | ) | |
567 | } else { | |
568 | let indent = self.block_indent; | |
569 | let rewrite = self.rewrite_required_fn( | |
a2a8927a | 570 | indent, item.ident, sig, &item.vis, generics, item.span, |
f20569fa XL |
571 | ); |
572 | self.push_rewrite(item.span, rewrite); | |
573 | } | |
574 | } | |
3c0e092e XL |
575 | ast::ItemKind::TyAlias(ref ty_alias) => { |
576 | use ItemVisitorKind::Item; | |
a2a8927a | 577 | self.visit_ty_alias_kind(ty_alias, &Item(item), item.span); |
f20569fa XL |
578 | } |
579 | ast::ItemKind::GlobalAsm(..) => { | |
580 | let snippet = Some(self.snippet(item.span).to_owned()); | |
581 | self.push_rewrite(item.span, snippet); | |
582 | } | |
583 | ast::ItemKind::MacroDef(ref def) => { | |
584 | let rewrite = rewrite_macro_def( | |
585 | &self.get_context(), | |
586 | self.shape(), | |
587 | self.block_indent, | |
588 | def, | |
589 | item.ident, | |
590 | &item.vis, | |
591 | item.span, | |
592 | ); | |
593 | self.push_rewrite(item.span, rewrite); | |
594 | } | |
595 | }; | |
596 | } | |
597 | self.skip_context = skip_context_saved; | |
598 | } | |
599 | ||
3c0e092e XL |
600 | fn visit_ty_alias_kind( |
601 | &mut self, | |
602 | ty_kind: &ast::TyAlias, | |
603 | visitor_kind: &ItemVisitorKind<'_>, | |
604 | span: Span, | |
605 | ) { | |
606 | let rewrite = rewrite_type_alias( | |
607 | ty_kind, | |
608 | &self.get_context(), | |
609 | self.block_indent, | |
610 | visitor_kind, | |
611 | span, | |
612 | ); | |
613 | self.push_rewrite(span, rewrite); | |
614 | } | |
f20569fa | 615 | |
3c0e092e XL |
616 | fn visit_assoc_item(&mut self, visitor_kind: &ItemVisitorKind<'_>) { |
617 | use ItemVisitorKind::*; | |
618 | // TODO(calebcartwright): Not sure the skip spans are correct | |
619 | let (ai, skip_span, assoc_ctxt) = match visitor_kind { | |
620 | AssocTraitItem(ai) => (*ai, ai.span(), visit::AssocCtxt::Trait), | |
621 | AssocImplItem(ai) => (*ai, ai.span, visit::AssocCtxt::Impl), | |
622 | _ => unreachable!(), | |
623 | }; | |
624 | skip_out_of_file_lines_range_visitor!(self, ai.span); | |
625 | ||
626 | if self.visit_attrs(&ai.attrs, ast::AttrStyle::Outer) { | |
a2a8927a | 627 | self.push_skipped_with_span(ai.attrs.as_slice(), skip_span, skip_span); |
f20569fa XL |
628 | return; |
629 | } | |
630 | ||
631 | // TODO(calebcartwright): consider enabling box_patterns feature gate | |
3c0e092e XL |
632 | match (&ai.kind, visitor_kind) { |
633 | (ast::AssocItemKind::Const(..), AssocTraitItem(_)) => { | |
a2a8927a | 634 | self.visit_static(&StaticParts::from_trait_item(ai)) |
3c0e092e XL |
635 | } |
636 | (ast::AssocItemKind::Const(..), AssocImplItem(_)) => { | |
a2a8927a | 637 | self.visit_static(&StaticParts::from_impl_item(ai)) |
3c0e092e XL |
638 | } |
639 | (ast::AssocItemKind::Fn(ref fn_kind), _) => { | |
640 | let ast::Fn { | |
641 | defaultness, | |
642 | ref sig, | |
643 | ref generics, | |
644 | ref body, | |
645 | } = **fn_kind; | |
646 | if let Some(ref body) = body { | |
647 | let inner_attrs = inner_attributes(&ai.attrs); | |
648 | let fn_ctxt = visit::FnCtxt::Assoc(assoc_ctxt); | |
f20569fa | 649 | self.visit_fn( |
04454e1e | 650 | visit::FnKind::Fn(fn_ctxt, ai.ident, sig, &ai.vis, generics, Some(body)), |
f20569fa | 651 | &sig.decl, |
3c0e092e | 652 | ai.span, |
f20569fa XL |
653 | defaultness, |
654 | Some(&inner_attrs), | |
655 | ); | |
656 | } else { | |
657 | let indent = self.block_indent; | |
658 | let rewrite = | |
3c0e092e XL |
659 | self.rewrite_required_fn(indent, ai.ident, sig, &ai.vis, generics, ai.span); |
660 | self.push_rewrite(ai.span, rewrite); | |
f20569fa XL |
661 | } |
662 | } | |
3c0e092e XL |
663 | (ast::AssocItemKind::TyAlias(ref ty_alias), _) => { |
664 | self.visit_ty_alias_kind(ty_alias, visitor_kind, ai.span); | |
f20569fa | 665 | } |
3c0e092e XL |
666 | (ast::AssocItemKind::MacCall(ref mac), _) => { |
667 | self.visit_mac(mac, Some(ai.ident), MacroPosition::Item); | |
f20569fa | 668 | } |
3c0e092e | 669 | _ => unreachable!(), |
f20569fa XL |
670 | } |
671 | } | |
672 | ||
3c0e092e XL |
673 | pub(crate) fn visit_trait_item(&mut self, ti: &ast::AssocItem) { |
674 | self.visit_assoc_item(&ItemVisitorKind::AssocTraitItem(ti)); | |
675 | } | |
f20569fa | 676 | |
3c0e092e XL |
677 | pub(crate) fn visit_impl_item(&mut self, ii: &ast::AssocItem) { |
678 | self.visit_assoc_item(&ItemVisitorKind::AssocImplItem(ii)); | |
f20569fa XL |
679 | } |
680 | ||
681 | fn visit_mac(&mut self, mac: &ast::MacCall, ident: Option<symbol::Ident>, pos: MacroPosition) { | |
682 | skip_out_of_file_lines_range_visitor!(self, mac.span()); | |
683 | ||
684 | // 1 = ; | |
685 | let shape = self.shape().saturating_sub_width(1); | |
686 | let rewrite = self.with_context(|ctx| rewrite_macro(mac, ident, ctx, shape, pos)); | |
687 | // As of v638 of the rustc-ap-* crates, the associated span no longer includes | |
688 | // the trailing semicolon. This determines the correct span to ensure scenarios | |
689 | // with whitespace between the delimiters and trailing semi (i.e. `foo!(abc) ;`) | |
690 | // are formatted correctly. | |
691 | let (span, rewrite) = match macro_style(mac, &self.get_context()) { | |
04454e1e | 692 | Delimiter::Bracket | Delimiter::Parenthesis if MacroPosition::Item == pos => { |
f20569fa XL |
693 | let search_span = mk_sp(mac.span().hi(), self.snippet_provider.end_pos()); |
694 | let hi = self.snippet_provider.span_before(search_span, ";"); | |
695 | let target_span = mk_sp(mac.span().lo(), hi + BytePos(1)); | |
696 | let rewrite = rewrite.map(|rw| { | |
94222f64 | 697 | if !rw.ends_with(';') { |
f20569fa XL |
698 | format!("{};", rw) |
699 | } else { | |
700 | rw | |
701 | } | |
702 | }); | |
703 | (target_span, rewrite) | |
704 | } | |
705 | _ => (mac.span(), rewrite), | |
706 | }; | |
707 | ||
708 | self.push_rewrite(span, rewrite); | |
709 | } | |
710 | ||
711 | pub(crate) fn push_str(&mut self, s: &str) { | |
712 | self.line_number += count_newlines(s); | |
713 | self.buffer.push_str(s); | |
714 | } | |
715 | ||
716 | #[allow(clippy::needless_pass_by_value)] | |
717 | fn push_rewrite_inner(&mut self, span: Span, rewrite: Option<String>) { | |
718 | if let Some(ref s) = rewrite { | |
719 | self.push_str(s); | |
720 | } else { | |
721 | let snippet = self.snippet(span); | |
722 | self.push_str(snippet.trim()); | |
723 | } | |
724 | self.last_pos = source!(self, span).hi(); | |
725 | } | |
726 | ||
727 | pub(crate) fn push_rewrite(&mut self, span: Span, rewrite: Option<String>) { | |
728 | self.format_missing_with_indent(source!(self, span).lo()); | |
729 | self.push_rewrite_inner(span, rewrite); | |
730 | } | |
731 | ||
732 | pub(crate) fn push_skipped_with_span( | |
733 | &mut self, | |
734 | attrs: &[ast::Attribute], | |
735 | item_span: Span, | |
736 | main_span: Span, | |
737 | ) { | |
738 | self.format_missing_with_indent(source!(self, item_span).lo()); | |
739 | // do not take into account the lines with attributes as part of the skipped range | |
740 | let attrs_end = attrs | |
741 | .iter() | |
742 | .map(|attr| self.parse_sess.line_of_byte_pos(attr.span.hi())) | |
743 | .max() | |
744 | .unwrap_or(1); | |
745 | let first_line = self.parse_sess.line_of_byte_pos(main_span.lo()); | |
746 | // Statement can start after some newlines and/or spaces | |
747 | // or it can be on the same line as the last attribute. | |
748 | // So here we need to take a minimum between the two. | |
749 | let lo = std::cmp::min(attrs_end + 1, first_line); | |
750 | self.push_rewrite_inner(item_span, None); | |
751 | let hi = self.line_number + 1; | |
752 | self.skipped_range.borrow_mut().push((lo, hi)); | |
753 | } | |
754 | ||
755 | pub(crate) fn from_context(ctx: &'a RewriteContext<'_>) -> FmtVisitor<'a> { | |
756 | let mut visitor = FmtVisitor::from_parse_sess( | |
757 | ctx.parse_sess, | |
758 | ctx.config, | |
759 | ctx.snippet_provider, | |
760 | ctx.report.clone(), | |
761 | ); | |
762 | visitor.skip_context.update(ctx.skip_context.clone()); | |
763 | visitor.set_parent_context(ctx); | |
764 | visitor | |
765 | } | |
766 | ||
767 | pub(crate) fn from_parse_sess( | |
768 | parse_session: &'a ParseSess, | |
769 | config: &'a Config, | |
770 | snippet_provider: &'a SnippetProvider, | |
771 | report: FormatReport, | |
772 | ) -> FmtVisitor<'a> { | |
773 | FmtVisitor { | |
774 | parent_context: None, | |
775 | parse_sess: parse_session, | |
776 | buffer: String::with_capacity(snippet_provider.big_snippet.len() * 2), | |
777 | last_pos: BytePos(0), | |
778 | block_indent: Indent::empty(), | |
779 | config, | |
780 | is_if_else_block: false, | |
781 | snippet_provider, | |
782 | line_number: 0, | |
783 | skipped_range: Rc::new(RefCell::new(vec![])), | |
784 | is_macro_def: false, | |
785 | macro_rewrite_failure: false, | |
786 | report, | |
787 | skip_context: Default::default(), | |
788 | } | |
789 | } | |
790 | ||
791 | pub(crate) fn opt_snippet(&'b self, span: Span) -> Option<&'a str> { | |
792 | self.snippet_provider.span_to_snippet(span) | |
793 | } | |
794 | ||
795 | pub(crate) fn snippet(&'b self, span: Span) -> &'a str { | |
796 | self.opt_snippet(span).unwrap() | |
797 | } | |
798 | ||
799 | // Returns true if we should skip the following item. | |
800 | pub(crate) fn visit_attrs(&mut self, attrs: &[ast::Attribute], style: ast::AttrStyle) -> bool { | |
801 | for attr in attrs { | |
802 | if attr.has_name(depr_skip_annotation()) { | |
803 | let file_name = self.parse_sess.span_to_filename(attr.span); | |
804 | self.report.append( | |
805 | file_name, | |
806 | vec![FormattingError::from_span( | |
807 | attr.span, | |
808 | self.parse_sess, | |
809 | ErrorKind::DeprecatedAttr, | |
810 | )], | |
811 | ); | |
812 | } else { | |
813 | match &attr.kind { | |
814 | ast::AttrKind::Normal(ref attribute_item, _) | |
815 | if self.is_unknown_rustfmt_attr(&attribute_item.path.segments) => | |
816 | { | |
817 | let file_name = self.parse_sess.span_to_filename(attr.span); | |
818 | self.report.append( | |
819 | file_name, | |
820 | vec![FormattingError::from_span( | |
821 | attr.span, | |
822 | self.parse_sess, | |
823 | ErrorKind::BadAttr, | |
824 | )], | |
825 | ); | |
826 | } | |
827 | _ => (), | |
828 | } | |
829 | } | |
830 | } | |
831 | if contains_skip(attrs) { | |
832 | return true; | |
833 | } | |
834 | ||
835 | let attrs: Vec<_> = attrs.iter().filter(|a| a.style == style).cloned().collect(); | |
836 | if attrs.is_empty() { | |
837 | return false; | |
838 | } | |
839 | ||
840 | let rewrite = attrs.rewrite(&self.get_context(), self.shape()); | |
841 | let span = mk_sp(attrs[0].span.lo(), attrs[attrs.len() - 1].span.hi()); | |
842 | self.push_rewrite(span, rewrite); | |
843 | ||
844 | false | |
845 | } | |
846 | ||
847 | fn is_unknown_rustfmt_attr(&self, segments: &[ast::PathSegment]) -> bool { | |
848 | if segments[0].ident.to_string() != "rustfmt" { | |
849 | return false; | |
850 | } | |
851 | !is_skip_attr(segments) | |
852 | } | |
853 | ||
94222f64 | 854 | fn walk_mod_items(&mut self, items: &[rustc_ast::ptr::P<ast::Item>]) { |
3c0e092e | 855 | self.visit_items_with_reordering(&ptr_vec_to_ref_vec(items)); |
f20569fa XL |
856 | } |
857 | ||
858 | fn walk_stmts(&mut self, stmts: &[Stmt<'_>], include_current_empty_semi: bool) { | |
859 | if stmts.is_empty() { | |
860 | return; | |
861 | } | |
862 | ||
863 | // Extract leading `use ...;`. | |
864 | let items: Vec<_> = stmts | |
865 | .iter() | |
866 | .take_while(|stmt| stmt.to_item().map_or(false, is_use_item)) | |
867 | .filter_map(|stmt| stmt.to_item()) | |
868 | .collect(); | |
869 | ||
870 | if items.is_empty() { | |
871 | self.visit_stmt(&stmts[0], include_current_empty_semi); | |
872 | ||
873 | // FIXME(calebcartwright 2021-01-03) - This exists strictly to maintain legacy | |
874 | // formatting where rustfmt would preserve redundant semicolons on Items in a | |
875 | // statement position. | |
876 | // | |
877 | // Starting in rustc-ap-* v692 (~2020-12-01) the rustc parser now parses this as | |
878 | // two separate statements (Item and Empty kinds), whereas before it was parsed as | |
879 | // a single statement with the statement's span including the redundant semicolon. | |
880 | // | |
881 | // rustfmt typically tosses unnecessary/redundant semicolons, and eventually we | |
882 | // should toss these as well, but doing so at this time would | |
883 | // break the Stability Guarantee | |
884 | // N.B. This could be updated to utilize the version gates. | |
885 | let include_next_empty = if stmts.len() > 1 { | |
94222f64 XL |
886 | matches!( |
887 | (&stmts[0].as_ast_node().kind, &stmts[1].as_ast_node().kind), | |
888 | (ast::StmtKind::Item(_), ast::StmtKind::Empty) | |
889 | ) | |
f20569fa XL |
890 | } else { |
891 | false | |
892 | }; | |
893 | ||
894 | self.walk_stmts(&stmts[1..], include_next_empty); | |
895 | } else { | |
896 | self.visit_items_with_reordering(&items); | |
897 | self.walk_stmts(&stmts[items.len()..], false); | |
898 | } | |
899 | } | |
900 | ||
901 | fn walk_block_stmts(&mut self, b: &ast::Block) { | |
902 | self.walk_stmts(&Stmt::from_ast_nodes(b.stmts.iter()), false) | |
903 | } | |
904 | ||
905 | fn format_mod( | |
906 | &mut self, | |
cdc7bbd5 XL |
907 | mod_kind: &ast::ModKind, |
908 | unsafety: ast::Unsafe, | |
f20569fa XL |
909 | vis: &ast::Visibility, |
910 | s: Span, | |
911 | ident: symbol::Ident, | |
912 | attrs: &[ast::Attribute], | |
f20569fa XL |
913 | ) { |
914 | let vis_str = utils::format_visibility(&self.get_context(), vis); | |
915 | self.push_str(&*vis_str); | |
cdc7bbd5 | 916 | self.push_str(format_unsafety(unsafety)); |
f20569fa XL |
917 | self.push_str("mod "); |
918 | // Calling `to_owned()` to work around borrow checker. | |
919 | let ident_str = rewrite_ident(&self.get_context(), ident).to_owned(); | |
920 | self.push_str(&ident_str); | |
921 | ||
5e7ed085 FG |
922 | if let ast::ModKind::Loaded(ref items, ast::Inline::Yes, ref spans) = mod_kind { |
923 | let ast::ModSpans { | |
924 | inner_span, | |
925 | inject_use_span: _, | |
926 | } = *spans; | |
f20569fa XL |
927 | match self.config.brace_style() { |
928 | BraceStyle::AlwaysNextLine => { | |
929 | let indent_str = self.block_indent.to_string_with_newline(self.config); | |
930 | self.push_str(&indent_str); | |
931 | self.push_str("{"); | |
932 | } | |
933 | _ => self.push_str(" {"), | |
934 | } | |
935 | // Hackery to account for the closing }. | |
936 | let mod_lo = self.snippet_provider.span_after(source!(self, s), "{"); | |
937 | let body_snippet = | |
cdc7bbd5 | 938 | self.snippet(mk_sp(mod_lo, source!(self, inner_span).hi() - BytePos(1))); |
f20569fa XL |
939 | let body_snippet = body_snippet.trim(); |
940 | if body_snippet.is_empty() { | |
941 | self.push_str("}"); | |
942 | } else { | |
943 | self.last_pos = mod_lo; | |
944 | self.block_indent = self.block_indent.block_indent(self.config); | |
945 | self.visit_attrs(attrs, ast::AttrStyle::Inner); | |
cdc7bbd5 XL |
946 | self.walk_mod_items(items); |
947 | let missing_span = self.next_span(inner_span.hi() - BytePos(1)); | |
f20569fa XL |
948 | self.close_block(missing_span, false); |
949 | } | |
cdc7bbd5 | 950 | self.last_pos = source!(self, inner_span).hi(); |
f20569fa XL |
951 | } else { |
952 | self.push_str(";"); | |
953 | self.last_pos = source!(self, s).hi(); | |
954 | } | |
955 | } | |
956 | ||
957 | pub(crate) fn format_separate_mod(&mut self, m: &Module<'_>, end_pos: BytePos) { | |
958 | self.block_indent = Indent::empty(); | |
a2a8927a XL |
959 | let skipped = self.visit_attrs(m.attrs(), ast::AttrStyle::Inner); |
960 | assert!( | |
961 | !skipped, | |
962 | "Skipping module must be handled before reaching this line." | |
963 | ); | |
964 | self.walk_mod_items(&m.items); | |
965 | self.format_missing_with_indent(end_pos); | |
f20569fa XL |
966 | } |
967 | ||
968 | pub(crate) fn skip_empty_lines(&mut self, end_pos: BytePos) { | |
969 | while let Some(pos) = self | |
970 | .snippet_provider | |
971 | .opt_span_after(self.next_span(end_pos), "\n") | |
972 | { | |
973 | if let Some(snippet) = self.opt_snippet(self.next_span(pos)) { | |
974 | if snippet.trim().is_empty() { | |
975 | self.last_pos = pos; | |
976 | } else { | |
977 | return; | |
978 | } | |
979 | } | |
980 | } | |
981 | } | |
982 | ||
983 | pub(crate) fn with_context<F>(&mut self, f: F) -> Option<String> | |
984 | where | |
985 | F: Fn(&RewriteContext<'_>) -> Option<String>, | |
986 | { | |
987 | let context = self.get_context(); | |
988 | let result = f(&context); | |
989 | ||
990 | self.macro_rewrite_failure |= context.macro_rewrite_failure.get(); | |
991 | result | |
992 | } | |
993 | ||
994 | pub(crate) fn get_context(&self) -> RewriteContext<'_> { | |
995 | RewriteContext { | |
996 | parse_sess: self.parse_sess, | |
997 | config: self.config, | |
998 | inside_macro: Rc::new(Cell::new(false)), | |
999 | use_block: Cell::new(false), | |
1000 | is_if_else_block: Cell::new(false), | |
1001 | force_one_line_chain: Cell::new(false), | |
1002 | snippet_provider: self.snippet_provider, | |
1003 | macro_rewrite_failure: Cell::new(false), | |
1004 | is_macro_def: self.is_macro_def, | |
1005 | report: self.report.clone(), | |
1006 | skip_context: self.skip_context.clone(), | |
1007 | skipped_range: self.skipped_range.clone(), | |
1008 | } | |
1009 | } | |
1010 | } |