Skip to content

Commit 3571c5d

Browse files
fix: maintain redundant semis on items in statement pos
1 parent c59b147 commit 3571c5d

File tree

3 files changed

+129
-13
lines changed

3 files changed

+129
-13
lines changed

src/visitor.rs

Lines changed: 44 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
112112
mk_sp(self.last_pos, hi)
113113
}
114114

115-
fn visit_stmt(&mut self, stmt: &Stmt<'_>) {
115+
fn visit_stmt(&mut self, stmt: &Stmt<'_>, include_empty_semi: bool) {
116116
debug!(
117117
"visit_stmt: {}",
118118
self.parse_sess.span_to_debug_info(stmt.span())
@@ -127,22 +127,31 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
127127
.map_or(false, |i| starts_with_newline(&snippet[i..]));
128128
let snippet = snippet.trim();
129129
if !snippet.is_empty() {
130-
if original_starts_with_newline {
131-
self.push_str("\n");
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);
132143
}
133-
self.push_str(&self.block_indent.to_string(self.config));
134-
self.push_str(snippet);
144+
} else if include_empty_semi {
145+
self.push_str(";");
135146
}
136-
137147
self.last_pos = stmt.span().hi();
138148
return;
139149
}
140150

141151
match stmt.as_ast_node().kind {
142152
ast::StmtKind::Item(ref item) => {
143153
self.visit_item(item);
144-
// Handle potential `;` after the item.
145-
self.format_missing(stmt.span().hi());
154+
self.last_pos = stmt.span().hi();
146155
}
147156
ast::StmtKind::Local(..) | ast::StmtKind::Expr(..) | ast::StmtKind::Semi(..) => {
148157
let attrs = get_attrs_from_stmt(stmt.as_ast_node());
@@ -899,7 +908,7 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
899908
self.visit_items_with_reordering(&ptr_vec_to_ref_vec(&m.items));
900909
}
901910

902-
fn walk_stmts(&mut self, stmts: &[Stmt<'_>]) {
911+
fn walk_stmts(&mut self, stmts: &[Stmt<'_>], include_current_empty_semi: bool) {
903912
if stmts.is_empty() {
904913
return;
905914
}
@@ -912,16 +921,38 @@ impl<'b, 'a: 'b> FmtVisitor<'a> {
912921
.collect();
913922

914923
if items.is_empty() {
915-
self.visit_stmt(&stmts[0]);
916-
self.walk_stmts(&stmts[1..]);
924+
self.visit_stmt(&stmts[0], include_current_empty_semi);
925+
926+
// FIXME(calebcartwright 2021-01-03) - This exists strictly to maintain legacy
927+
// formatting where rustfmt would preserve redundant semicolons on Items in a
928+
// statement position.
929+
//
930+
// Starting in rustc-ap-* v692 (~2020-12-01) the rustc parser now parses this as
931+
// two separate statements (Item and Empty kinds), whereas before it was parsed as
932+
// a single statement with the statement's span including the redundant semicolon.
933+
//
934+
// rustfmt typically tosses unnecessary/redundant semicolons, and eventually we
935+
// should toss these as well, but doing so at this time would
936+
// break the Stability Guarantee
937+
// N.B. This could be updated to utilize the version gates.
938+
let include_next_empty = if stmts.len() > 1 {
939+
match (&stmts[0].as_ast_node().kind, &stmts[1].as_ast_node().kind) {
940+
(ast::StmtKind::Item(_), ast::StmtKind::Empty) => true,
941+
_ => false,
942+
}
943+
} else {
944+
false
945+
};
946+
947+
self.walk_stmts(&stmts[1..], include_next_empty);
917948
} else {
918949
self.visit_items_with_reordering(&items);
919-
self.walk_stmts(&stmts[items.len()..]);
950+
self.walk_stmts(&stmts[items.len()..], false);
920951
}
921952
}
922953

923954
fn walk_block_stmts(&mut self, b: &ast::Block) {
924-
self.walk_stmts(&Stmt::from_ast_nodes(b.stmts.iter()))
955+
self.walk_stmts(&Stmt::from_ast_nodes(b.stmts.iter()), false)
925956
}
926957

927958
fn format_mod(

tests/source/statements.rs

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// FIXME(calebcartwright) - Hopefully one day we can
2+
// elide these redundant semis like we do in other contexts.
3+
fn redundant_item_semis() {
4+
impl Foo {
5+
fn get(&self) -> usize {
6+
5
7+
}
8+
};
9+
10+
impl Bar {
11+
fn get(&self) -> usize {
12+
5
13+
}
14+
} /*asdfsf*/;
15+
16+
17+
impl Baz {
18+
fn get(&self) -> usize {
19+
5
20+
}
21+
} /*asdfsf*/
22+
23+
// why would someone do this
24+
;
25+
26+
27+
impl Qux {
28+
fn get(&self) -> usize {
29+
5
30+
}
31+
}
32+
33+
// why
34+
;
35+
36+
impl Lorem {
37+
fn get(&self) -> usize {
38+
5
39+
}
40+
}
41+
// oh why
42+
;
43+
}

tests/target/statements.rs

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// FIXME(calebcartwright) - Hopefully one day we can
2+
// elide these redundant semis like we do in other contexts.
3+
fn redundant_item_semis() {
4+
impl Foo {
5+
fn get(&self) -> usize {
6+
5
7+
}
8+
};
9+
10+
impl Bar {
11+
fn get(&self) -> usize {
12+
5
13+
}
14+
} /*asdfsf*/
15+
;
16+
17+
impl Baz {
18+
fn get(&self) -> usize {
19+
5
20+
}
21+
} /*asdfsf*/
22+
23+
// why would someone do this
24+
;
25+
26+
impl Qux {
27+
fn get(&self) -> usize {
28+
5
29+
}
30+
}
31+
32+
// why
33+
;
34+
35+
impl Lorem {
36+
fn get(&self) -> usize {
37+
5
38+
}
39+
}
40+
// oh why
41+
;
42+
}

0 commit comments

Comments
 (0)