1 use std
::cell
::{Cell, RefCell}
;
4 use rustc_ast
::{ast, token::Delimiter, visit}
;
5 use rustc_data_structures
::sync
::Lrc
;
6 use rustc_span
::{symbol, BytePos, Pos, Span}
;
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
;
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
,
17 use crate::macros
::{macro_style, rewrite_macro, rewrite_macro_def, MacroPosition}
;
18 use crate::modules
::Module
;
19 use crate::parse
::session
::ParseSess
;
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
;
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
,
30 use crate::{ErrorKind, FormatReport, FormattingError}
;
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
: Lrc
<String
>,
36 /// A position of the start of `big_snippet`, used as an offset.
38 /// An end position of the file that this snippet lives.
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
])
49 pub(crate) fn new(start_pos
: BytePos
, end_pos
: BytePos
, big_snippet
: Lrc
<String
>) -> Self {
50 let start_pos
= start_pos
.to_usize();
51 let end_pos
= end_pos
.to_usize();
59 pub(crate) fn entire_snippet(&self) -> &str {
60 self.big_snippet
.as_str()
63 pub(crate) fn start_pos(&self) -> BytePos
{
64 BytePos
::from_usize(self.start_pos
)
67 pub(crate) fn end_pos(&self) -> BytePos
{
68 BytePos
::from_usize(self.end_pos
)
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
,
92 impl<'a
> Drop
for FmtVisitor
<'a
> {
94 if let Some(ctx
) = self.parent_context
{
95 if self.macro_rewrite_failure
{
96 ctx
.macro_rewrite_failure
.replace(true);
102 impl<'b
, 'a
: 'b
> FmtVisitor
<'a
> {
103 fn set_parent_context(&mut self, context
: &'a RewriteContext
<'_
>) {
104 self.parent_context
= Some(context
);
107 pub(crate) fn shape(&self) -> Shape
{
108 Shape
::indented(self.block_indent
, self.config
)
111 fn next_span(&self, hi
: BytePos
) -> Span
{
112 mk_sp(self.last_pos
, hi
)
115 fn visit_stmt(&mut self, stmt
: &Stmt
<'_
>, include_empty_semi
: bool
) {
118 self.parse_sess
.span_to_debug_info(stmt
.span())
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
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());
137 if original_starts_with_newline
{
141 self.push_str(&self.block_indent
.to_string(self.config
));
142 self.push_str(snippet
);
144 } else if include_empty_semi
{
147 self.last_pos
= stmt
.span().hi();
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();
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(
162 get_span_without_attrs(stmt
.as_ast_node()),
165 let shape
= self.shape();
166 let rewrite
= self.with_context(|ctx
| stmt
.rewrite(ctx
, shape
));
167 self.push_rewrite(stmt
.span(), rewrite
)
170 ast
::StmtKind
::MacCall(ref mac_stmt
) => {
171 if self.visit_attrs(&mac_stmt
.attrs
, ast
::AttrStyle
::Outer
) {
172 self.push_skipped_with_span(
175 get_span_without_attrs(stmt
.as_ast_node()),
178 self.visit_mac(&mac_stmt
.mac
, None
, MacroPosition
::Statement
);
180 self.format_missing(stmt
.span().hi());
182 ast
::StmtKind
::Empty
=> (),
186 /// Remove spaces between the opening brace and the first statement or the inner attribute
188 fn trim_spaces_after_opening_brace(
191 inner_attrs
: Option
<&[ast
::Attribute
]>,
193 if let Some(first_stmt
) = b
.stmts
.first() {
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
)
201 .and_then(|(kind
, _
, s
)| {
202 if kind
== CodeCharKind
::Normal
{
208 if let Some(len
) = len
{
209 self.last_pos
= self.last_pos
+ BytePos
::from_usize(len
);
214 pub(crate) fn visit_block(
217 inner_attrs
: Option
<&[ast
::Attribute
]>,
222 self.parse_sess
.span_to_debug_info(b
.span
),
225 // Check if this block has braces.
226 let brace_compensation
= BytePos(if has_braces { 1 }
else { 0 }
);
228 self.last_pos
= self.last_pos
+ brace_compensation
;
229 self.block_indent
= self.block_indent
.block_indent(self.config
);
231 self.trim_spaces_after_opening_brace(b
, inner_attrs
);
233 // Format inner attributes if available.
234 if let Some(attrs
) = inner_attrs
{
235 self.visit_attrs(attrs
, ast
::AttrStyle
::Inner
);
238 self.walk_block_stmts(b
);
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
) {
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
);
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
));
257 self.last_pos
= source
!(self, b
.span
).hi();
260 fn close_block(&mut self, span
: Span
, unindent_comment
: bool
) {
261 let config
= self.config
;
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;
268 let skip_normal
= |s
: &str| {
269 let trimmed
= s
.trim();
270 trimmed
.is_empty() || trimmed
.chars().all(|c
| c
== '
;'
)
273 let comment_snippet
= self.snippet(span
);
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
)
282 for (kind
, offset
, sub_slice
) in CommentCodeSlices
::new(comment_snippet
) {
283 let sub_slice
= transform_missing_snippet(config
, sub_slice
);
285 debug
!("close_block: {:?} {:?} {:?}", kind
, offset
, sub_slice
);
288 CodeCharKind
::Comment
=> {
289 if !unindented
&& unindent_comment
&& !align_to_right
{
291 self.block_indent
= self.block_indent
.block_unindent(config
);
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'
);
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
{
301 // put the first line of the comment on the same line as the
303 match sub_slice
.find('
\n'
) {
305 self.push_str(&sub_slice
);
307 Some(offset
) if offset
+ 1 == sub_slice
.len() => {
308 self.push_str(&sub_slice
[..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
));
315 // put the other lines below it, shaping it as needed
316 let other_lines
= &sub_slice
[offset
+ 1..];
318 rewrite_comment(other_lines
, false, comment_shape
, config
);
320 Some(ref s
) => self.push_str(s
),
321 None
=> self.push_str(other_lines
),
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());
331 .visual_indent(offset_len
)
332 .sub_width(offset_len
)
334 Some(shp
) => comment_shape
= shp
,
335 None
=> comment_on_same_line
= false,
339 if comment_on_same_line
{
342 if count_newlines(snippet_in_between
) >= 2 || extra_newline
{
345 self.push_str(&self.block_indent
.to_string_with_newline(config
));
348 let comment_str
= rewrite_comment(&sub_slice
, false, comment_shape
, config
);
350 Some(ref s
) => self.push_str(s
),
351 None
=> self.push_str(&sub_slice
),
355 CodeCharKind
::Normal
if skip_normal(&sub_slice
) => {
356 extra_newline
= prev_ends_with_newline
&& sub_slice
.contains('
\n'
);
359 CodeCharKind
::Normal
=> {
360 self.push_str(&self.block_indent
.to_string_with_newline(config
));
361 self.push_str(sub_slice
.trim());
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());
369 self.block_indent
= self.block_indent
.block_indent(self.config
);
371 self.block_indent
= self.block_indent
.block_unindent(self.config
);
372 self.push_str(&self.block_indent
.to_string_with_newline(config
));
376 fn unindent_comment_on_closing_brace(&self, b
: &ast
::Block
) -> bool
{
377 self.is_if_else_block
&& !b
.stmts
.is_empty()
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(
384 fk
: visit
::FnKind
<'_
>,
387 defaultness
: ast
::Defaultness
,
388 inner_attrs
: Option
<&[ast
::Attribute
]>,
390 let indent
= self.block_indent
;
392 let rewrite
= match fk
{
393 visit
::FnKind
::Fn(_
, ident
, _
, _
, _
, Some(ref b
)) => {
395 self.rewrite_fn_before_block(
398 &FnSig
::from_fn_kind(&fk
, fd
, defaultness
),
399 mk_sp(s
.lo(), b
.span
.lo()),
405 if let Some((fn_str
, fn_brace_style
)) = rewrite
{
406 self.format_missing_with_indent(source
!(self, s
).lo());
408 if let Some(rw
) = self.single_line_fn(&fn_str
, block
, inner_attrs
) {
410 self.last_pos
= s
.hi();
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
))
422 self.last_pos
= source
!(self, block
.span
).lo();
424 self.format_missing(source
!(self, block
.span
).lo());
427 self.visit_block(block
, inner_attrs
, true)
430 pub(crate) fn visit_item(&mut self, item
: &ast
::Item
) {
431 skip_out_of_file_lines_range_visitor
!(self, item
.span
);
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.
438 let mut attrs
= &item
.attrs
;
439 let skip_context_saved
= self.skip_context
.clone();
440 self.skip_context
.update_with_attrs(attrs
);
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());
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());
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
469 assert
!(!self.visit_attrs(&filtered_attrs
, ast
::AttrStyle
::Outer
));
470 attrs
= &filtered_attrs
;
474 if self.visit_attrs(&item
.attrs
, ast
::AttrStyle
::Outer
) {
475 self.push_skipped_with_span(item
.attrs
.as_slice(), item
.span(), item
.span());
483 // TODO(calebcartwright): consider enabling box_patterns feature gate
484 if should_visit_node_again
{
486 ast
::ItemKind
::Use(ref tree
) => self.format_import(item
, tree
),
487 ast
::ItemKind
::Impl(ref iimpl
) => {
488 let block_indent
= self.block_indent
;
489 let rw
= self.with_context(|ctx
| format_impl(ctx
, item
, iimpl
, block_indent
));
490 self.push_rewrite(item
.span
, rw
);
492 ast
::ItemKind
::Trait(..) => {
493 let block_indent
= self.block_indent
;
494 let rw
= self.with_context(|ctx
| format_trait(ctx
, item
, block_indent
));
495 self.push_rewrite(item
.span
, rw
);
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(
507 self.push_rewrite(item
.span
, rw
);
509 ast
::ItemKind
::ExternCrate(_
) => {
510 let rw
= rewrite_extern_crate(&self.get_context(), item
, self.shape());
511 let span
= if attrs
.is_empty() {
514 mk_sp(attrs
[0].span
.lo(), item
.span
.hi())
516 self.push_rewrite(span
, rw
);
518 ast
::ItemKind
::Struct(..) | ast
::ItemKind
::Union(..) => {
519 self.visit_struct(&StructParts
::from_item(item
));
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();
526 ast
::ItemKind
::Mod(unsafety
, ref mod_kind
) => {
527 self.format_missing_with_indent(source
!(self, item
.span
).lo());
528 self.format_mod(mod_kind
, unsafety
, &item
.vis
, item
.span
, item
.ident
, attrs
);
530 ast
::ItemKind
::MacCall(ref mac
) => {
531 self.visit_mac(mac
, Some(item
.ident
), MacroPosition
::Item
);
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
);
537 ast
::ItemKind
::Static(..) | ast
::ItemKind
::Const(..) => {
538 self.visit_static(&StaticParts
::from_item(item
));
540 ast
::ItemKind
::Fn(ref fn_kind
) => {
547 if let Some(ref body
) = body
{
548 let inner_attrs
= inner_attributes(&item
.attrs
);
549 let fn_ctxt
= match sig
.header
.ext
{
550 ast
::Extern
::None
=> visit
::FnCtxt
::Free
,
551 _
=> visit
::FnCtxt
::Foreign
,
568 let indent
= self.block_indent
;
569 let rewrite
= self.rewrite_required_fn(
570 indent
, item
.ident
, sig
, &item
.vis
, generics
, item
.span
,
572 self.push_rewrite(item
.span
, rewrite
);
575 ast
::ItemKind
::TyAlias(ref ty_alias
) => {
576 use ItemVisitorKind
::Item
;
577 self.visit_ty_alias_kind(ty_alias
, &Item(item
), item
.span
);
579 ast
::ItemKind
::GlobalAsm(..) => {
580 let snippet
= Some(self.snippet(item
.span
).to_owned());
581 self.push_rewrite(item
.span
, snippet
);
583 ast
::ItemKind
::MacroDef(ref def
) => {
584 let rewrite
= rewrite_macro_def(
593 self.push_rewrite(item
.span
, rewrite
);
597 self.skip_context
= skip_context_saved
;
600 fn visit_ty_alias_kind(
602 ty_kind
: &ast
::TyAlias
,
603 visitor_kind
: &ItemVisitorKind
<'_
>,
606 let rewrite
= rewrite_type_alias(
613 self.push_rewrite(span
, rewrite
);
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
),
624 skip_out_of_file_lines_range_visitor
!(self, ai
.span
);
626 if self.visit_attrs(&ai
.attrs
, ast
::AttrStyle
::Outer
) {
627 self.push_skipped_with_span(ai
.attrs
.as_slice(), skip_span
, skip_span
);
631 // TODO(calebcartwright): consider enabling box_patterns feature gate
632 match (&ai
.kind
, visitor_kind
) {
633 (ast
::AssocItemKind
::Const(..), AssocTraitItem(_
)) => {
634 self.visit_static(&StaticParts
::from_trait_item(ai
))
636 (ast
::AssocItemKind
::Const(..), AssocImplItem(_
)) => {
637 self.visit_static(&StaticParts
::from_impl_item(ai
))
639 (ast
::AssocItemKind
::Fn(ref 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
);
650 visit
::FnKind
::Fn(fn_ctxt
, ai
.ident
, sig
, &ai
.vis
, generics
, Some(body
)),
657 let indent
= self.block_indent
;
659 self.rewrite_required_fn(indent
, ai
.ident
, sig
, &ai
.vis
, generics
, ai
.span
);
660 self.push_rewrite(ai
.span
, rewrite
);
663 (ast
::AssocItemKind
::TyAlias(ref ty_alias
), _
) => {
664 self.visit_ty_alias_kind(ty_alias
, visitor_kind
, ai
.span
);
666 (ast
::AssocItemKind
::MacCall(ref mac
), _
) => {
667 self.visit_mac(mac
, Some(ai
.ident
), MacroPosition
::Item
);
673 pub(crate) fn visit_trait_item(&mut self, ti
: &ast
::AssocItem
) {
674 self.visit_assoc_item(&ItemVisitorKind
::AssocTraitItem(ti
));
677 pub(crate) fn visit_impl_item(&mut self, ii
: &ast
::AssocItem
) {
678 self.visit_assoc_item(&ItemVisitorKind
::AssocImplItem(ii
));
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());
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()) {
692 Delimiter
::Bracket
| Delimiter
::Parenthesis
if MacroPosition
::Item
== pos
=> {
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
| {
697 if !rw
.ends_with('
;'
) {
703 (target_span
, rewrite
)
705 _
=> (mac
.span(), rewrite
),
708 self.push_rewrite(span
, rewrite
);
711 pub(crate) fn push_str(&mut self, s
: &str) {
712 self.line_number
+= count_newlines(s
);
713 self.buffer
.push_str(s
);
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
{
721 let snippet
= self.snippet(span
);
722 self.push_str(snippet
.trim());
724 self.last_pos
= source
!(self, span
).hi();
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
);
732 pub(crate) fn push_skipped_with_span(
734 attrs
: &[ast
::Attribute
],
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
742 .map(|attr
| self.parse_sess
.line_of_byte_pos(attr
.span
.hi()))
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
));
755 pub(crate) fn from_context(ctx
: &'a RewriteContext
<'_
>) -> FmtVisitor
<'a
> {
756 let mut visitor
= FmtVisitor
::from_parse_sess(
759 ctx
.snippet_provider
,
762 visitor
.skip_context
.update(ctx
.skip_context
.clone());
763 visitor
.set_parent_context(ctx
);
767 pub(crate) fn from_parse_sess(
768 parse_session
: &'a ParseSess
,
770 snippet_provider
: &'a SnippetProvider
,
771 report
: FormatReport
,
772 ) -> FmtVisitor
<'a
> {
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(),
780 is_if_else_block
: false,
783 skipped_range
: Rc
::new(RefCell
::new(vec
![])),
785 macro_rewrite_failure
: false,
787 skip_context
: Default
::default(),
791 pub(crate) fn opt_snippet(&'b
self, span
: Span
) -> Option
<&'a
str> {
792 self.snippet_provider
.span_to_snippet(span
)
795 pub(crate) fn snippet(&'b
self, span
: Span
) -> &'a
str {
796 self.opt_snippet(span
).unwrap()
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
{
802 if attr
.has_name(depr_skip_annotation()) {
803 let file_name
= self.parse_sess
.span_to_filename(attr
.span
);
806 vec
![FormattingError
::from_span(
809 ErrorKind
::DeprecatedAttr
,
814 ast
::AttrKind
::Normal(ref attribute_item
, _
)
815 if self.is_unknown_rustfmt_attr(&attribute_item
.path
.segments
) =>
817 let file_name
= self.parse_sess
.span_to_filename(attr
.span
);
820 vec
![FormattingError
::from_span(
831 if contains_skip(attrs
) {
835 let attrs
: Vec
<_
> = attrs
.iter().filter(|a
| a
.style
== style
).cloned().collect();
836 if attrs
.is_empty() {
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
);
847 fn is_unknown_rustfmt_attr(&self, segments
: &[ast
::PathSegment
]) -> bool
{
848 if segments
[0].ident
.to_string() != "rustfmt" {
851 !is_skip_attr(segments
)
854 fn walk_mod_items(&mut self, items
: &[rustc_ast
::ptr
::P
<ast
::Item
>]) {
855 self.visit_items_with_reordering(&ptr_vec_to_ref_vec(items
));
858 fn walk_stmts(&mut self, stmts
: &[Stmt
<'_
>], include_current_empty_semi
: bool
) {
859 if stmts
.is_empty() {
863 // Extract leading `use ...;`.
864 let items
: Vec
<_
> = stmts
866 .take_while(|stmt
| stmt
.to_item().map_or(false, is_use_item
))
867 .filter_map(|stmt
| stmt
.to_item())
870 if items
.is_empty() {
871 self.visit_stmt(&stmts
[0], include_current_empty_semi
);
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.
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.
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 {
887 (&stmts
[0].as_ast_node().kind
, &stmts
[1].as_ast_node().kind
),
888 (ast
::StmtKind
::Item(_
), ast
::StmtKind
::Empty
)
894 self.walk_stmts(&stmts
[1..], include_next_empty
);
896 self.visit_items_with_reordering(&items
);
897 self.walk_stmts(&stmts
[items
.len()..], false);
901 fn walk_block_stmts(&mut self, b
: &ast
::Block
) {
902 self.walk_stmts(&Stmt
::from_ast_nodes(b
.stmts
.iter()), false)
907 mod_kind
: &ast
::ModKind
,
908 unsafety
: ast
::Unsafe
,
909 vis
: &ast
::Visibility
,
911 ident
: symbol
::Ident
,
912 attrs
: &[ast
::Attribute
],
914 let vis_str
= utils
::format_visibility(&self.get_context(), vis
);
915 self.push_str(&*vis_str
);
916 self.push_str(format_unsafety(unsafety
));
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
);
922 if let ast
::ModKind
::Loaded(ref items
, ast
::Inline
::Yes
, ref spans
) = mod_kind
{
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
);
933 _
=> self.push_str(" {"),
935 // Hackery to account for the closing }.
936 let mod_lo
= self.snippet_provider
.span_after(source
!(self, s
), "{");
938 self.snippet(mk_sp(mod_lo
, source
!(self, inner_span
).hi() - BytePos(1)));
939 let body_snippet
= body_snippet
.trim();
940 if body_snippet
.is_empty() {
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
);
946 self.walk_mod_items(items
);
947 let missing_span
= self.next_span(inner_span
.hi() - BytePos(1));
948 self.close_block(missing_span
, false);
950 self.last_pos
= source
!(self, inner_span
).hi();
953 self.last_pos
= source
!(self, s
).hi();
957 pub(crate) fn format_separate_mod(&mut self, m
: &Module
<'_
>, end_pos
: BytePos
) {
958 self.block_indent
= Indent
::empty();
959 let skipped
= self.visit_attrs(m
.attrs(), ast
::AttrStyle
::Inner
);
962 "Skipping module must be handled before reaching this line."
964 self.walk_mod_items(&m
.items
);
965 self.format_missing_with_indent(end_pos
);
968 pub(crate) fn skip_empty_lines(&mut self, end_pos
: BytePos
) {
969 while let Some(pos
) = self
971 .opt_span_after(self.next_span(end_pos
), "\n")
973 if let Some(snippet
) = self.opt_snippet(self.next_span(pos
)) {
974 if snippet
.trim().is_empty() {
983 pub(crate) fn with_context
<F
>(&mut self, f
: F
) -> Option
<String
>
985 F
: Fn(&RewriteContext
<'_
>) -> Option
<String
>,
987 let context
= self.get_context();
988 let result
= f(&context
);
990 self.macro_rewrite_failure
|= context
.macro_rewrite_failure
.get();
994 pub(crate) fn get_context(&self) -> RewriteContext
<'_
> {
996 parse_sess
: self.parse_sess
,
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(),