Skip to content

Commit 8aaae42

Browse files
committed
Auto merge of #59981 - estebank:recover-struct-lit, r=petrochenkov
Emit specific error for struct literal in conditions Fix #59962, fix #51311.
2 parents 130dc3e + aa393b0 commit 8aaae42

17 files changed

+341
-282
lines changed

src/librustc_borrowck/borrowck/mod.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -897,8 +897,7 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
897897
self.cannot_borrow_path_as_mutable(error_span, &descr, Origin::Ast)
898898
}
899899
BorrowViolation(euv::ClosureInvocation) => {
900-
span_bug!(err.span,
901-
"err_mutbl with a closure invocation");
900+
span_bug!(err.span, "err_mutbl with a closure invocation");
902901
}
903902
};
904903

@@ -1096,7 +1095,6 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
10961095
BorrowViolation(euv::MatchDiscriminant) => {
10971096
"cannot borrow data mutably"
10981097
}
1099-
11001098
BorrowViolation(euv::ClosureInvocation) => {
11011099
is_closure = true;
11021100
"closure invocation"

src/librustc_resolve/diagnostics.rs

+85-76
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,56 @@ impl<'a> Resolver<'a> {
240240
(err, candidates)
241241
}
242242

243+
fn followed_by_brace(&self, span: Span) -> (bool, Option<(Span, String)>) {
244+
// HACK(estebank): find a better way to figure out that this was a
245+
// parser issue where a struct literal is being used on an expression
246+
// where a brace being opened means a block is being started. Look
247+
// ahead for the next text to see if `span` is followed by a `{`.
248+
let sm = self.session.source_map();
249+
let mut sp = span;
250+
loop {
251+
sp = sm.next_point(sp);
252+
match sm.span_to_snippet(sp) {
253+
Ok(ref snippet) => {
254+
if snippet.chars().any(|c| { !c.is_whitespace() }) {
255+
break;
256+
}
257+
}
258+
_ => break,
259+
}
260+
}
261+
let followed_by_brace = match sm.span_to_snippet(sp) {
262+
Ok(ref snippet) if snippet == "{" => true,
263+
_ => false,
264+
};
265+
// In case this could be a struct literal that needs to be surrounded
266+
// by parenthesis, find the appropriate span.
267+
let mut i = 0;
268+
let mut closing_brace = None;
269+
loop {
270+
sp = sm.next_point(sp);
271+
match sm.span_to_snippet(sp) {
272+
Ok(ref snippet) => {
273+
if snippet == "}" {
274+
let sp = span.to(sp);
275+
if let Ok(snippet) = sm.span_to_snippet(sp) {
276+
closing_brace = Some((sp, snippet));
277+
}
278+
break;
279+
}
280+
}
281+
_ => break,
282+
}
283+
i += 1;
284+
// The bigger the span, the more likely we're incorrect --
285+
// bound it to 100 chars long.
286+
if i > 100 {
287+
break;
288+
}
289+
}
290+
return (followed_by_brace, closing_brace)
291+
}
292+
243293
/// Provides context-dependent help for errors reported by the `smart_resolve_path_fragment`
244294
/// function.
245295
/// Returns `true` if able to provide context-dependent help.
@@ -278,6 +328,39 @@ impl<'a> Resolver<'a> {
278328
_ => false,
279329
};
280330

331+
let mut bad_struct_syntax_suggestion = || {
332+
let (followed_by_brace, closing_brace) = self.followed_by_brace(span);
333+
let mut suggested = false;
334+
match source {
335+
PathSource::Expr(Some(parent)) => {
336+
suggested = path_sep(err, &parent);
337+
}
338+
PathSource::Expr(None) if followed_by_brace == true => {
339+
if let Some((sp, snippet)) = closing_brace {
340+
err.span_suggestion(
341+
sp,
342+
"surround the struct literal with parenthesis",
343+
format!("({})", snippet),
344+
Applicability::MaybeIncorrect,
345+
);
346+
} else {
347+
err.span_label(
348+
span, // Note the parenthesis surrounding the suggestion below
349+
format!("did you mean `({} {{ /* fields */ }})`?", path_str),
350+
);
351+
}
352+
suggested = true;
353+
},
354+
_ => {}
355+
}
356+
if !suggested {
357+
err.span_label(
358+
span,
359+
format!("did you mean `{} {{ /* fields */ }}`?", path_str),
360+
);
361+
}
362+
};
363+
281364
match (def, source) {
282365
(Def::Macro(..), _) => {
283366
err.span_suggestion(
@@ -331,87 +414,13 @@ impl<'a> Resolver<'a> {
331414
);
332415
}
333416
} else {
334-
// HACK(estebank): find a better way to figure out that this was a
335-
// parser issue where a struct literal is being used on an expression
336-
// where a brace being opened means a block is being started. Look
337-
// ahead for the next text to see if `span` is followed by a `{`.
338-
let sm = self.session.source_map();
339-
let mut sp = span;
340-
loop {
341-
sp = sm.next_point(sp);
342-
match sm.span_to_snippet(sp) {
343-
Ok(ref snippet) => {
344-
if snippet.chars().any(|c| { !c.is_whitespace() }) {
345-
break;
346-
}
347-
}
348-
_ => break,
349-
}
350-
}
351-
let followed_by_brace = match sm.span_to_snippet(sp) {
352-
Ok(ref snippet) if snippet == "{" => true,
353-
_ => false,
354-
};
355-
// In case this could be a struct literal that needs to be surrounded
356-
// by parenthesis, find the appropriate span.
357-
let mut i = 0;
358-
let mut closing_brace = None;
359-
loop {
360-
sp = sm.next_point(sp);
361-
match sm.span_to_snippet(sp) {
362-
Ok(ref snippet) => {
363-
if snippet == "}" {
364-
let sp = span.to(sp);
365-
if let Ok(snippet) = sm.span_to_snippet(sp) {
366-
closing_brace = Some((sp, snippet));
367-
}
368-
break;
369-
}
370-
}
371-
_ => break,
372-
}
373-
i += 1;
374-
// The bigger the span, the more likely we're incorrect --
375-
// bound it to 100 chars long.
376-
if i > 100 {
377-
break;
378-
}
379-
}
380-
match source {
381-
PathSource::Expr(Some(parent)) => if !path_sep(err, &parent) {
382-
err.span_label(
383-
span,
384-
format!("did you mean `{} {{ /* fields */ }}`?", path_str),
385-
);
386-
}
387-
PathSource::Expr(None) if followed_by_brace == true => {
388-
if let Some((sp, snippet)) = closing_brace {
389-
err.span_suggestion(
390-
sp,
391-
"surround the struct literal with parenthesis",
392-
format!("({})", snippet),
393-
Applicability::MaybeIncorrect,
394-
);
395-
} else {
396-
err.span_label(
397-
span,
398-
format!("did you mean `({} {{ /* fields */ }})`?", path_str),
399-
);
400-
}
401-
},
402-
_ => {
403-
err.span_label(
404-
span,
405-
format!("did you mean `{} {{ /* fields */ }}`?", path_str),
406-
);
407-
},
408-
}
417+
bad_struct_syntax_suggestion();
409418
}
410419
}
411420
(Def::Union(..), _) |
412421
(Def::Variant(..), _) |
413422
(Def::Ctor(_, _, CtorKind::Fictive), _) if ns == ValueNS => {
414-
err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?", path_str));
423+
bad_struct_syntax_suggestion();
415424
}
416425
(Def::SelfTy(..), _) if ns == ValueNS => {
417426
err.span_label(span, fallback_label);

src/libsyntax/parse/parser.rs

+48-5
Original file line numberDiff line numberDiff line change
@@ -2855,11 +2855,13 @@ impl<'a> Parser<'a> {
28552855
let (delim, tts) = self.expect_delimited_token_tree()?;
28562856
hi = self.prev_span;
28572857
ex = ExprKind::Mac(respan(lo.to(hi), Mac_ { path, tts, delim }));
2858-
} else if self.check(&token::OpenDelim(token::Brace)) &&
2859-
!self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL) {
2860-
// This is a struct literal, unless we're prohibited
2861-
// from parsing struct literals here.
2862-
return self.parse_struct_expr(lo, path, attrs);
2858+
} else if self.check(&token::OpenDelim(token::Brace)) {
2859+
if let Some(expr) = self.maybe_parse_struct_expr(lo, &path, &attrs) {
2860+
return expr;
2861+
} else {
2862+
hi = path.span;
2863+
ex = ExprKind::Path(None, path);
2864+
}
28632865
} else {
28642866
hi = path.span;
28652867
ex = ExprKind::Path(None, path);
@@ -2902,6 +2904,47 @@ impl<'a> Parser<'a> {
29022904
self.maybe_recover_from_bad_qpath(expr, true)
29032905
}
29042906

2907+
fn maybe_parse_struct_expr(
2908+
&mut self,
2909+
lo: Span,
2910+
path: &ast::Path,
2911+
attrs: &ThinVec<Attribute>,
2912+
) -> Option<PResult<'a, P<Expr>>> {
2913+
let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);
2914+
let certainly_not_a_block = || self.look_ahead(1, |t| t.is_ident()) && (
2915+
// `{ ident, ` cannot start a block
2916+
self.look_ahead(2, |t| t == &token::Comma) ||
2917+
self.look_ahead(2, |t| t == &token::Colon) && (
2918+
// `{ ident: token, ` cannot start a block
2919+
self.look_ahead(4, |t| t == &token::Comma) ||
2920+
// `{ ident: ` cannot start a block unless it's a type ascription `ident: Type`
2921+
self.look_ahead(3, |t| !t.can_begin_type())
2922+
)
2923+
);
2924+
2925+
if struct_allowed || certainly_not_a_block() {
2926+
// This is a struct literal, but we don't can't accept them here
2927+
let expr = self.parse_struct_expr(lo, path.clone(), attrs.clone());
2928+
if let (Ok(expr), false) = (&expr, struct_allowed) {
2929+
let mut err = self.diagnostic().struct_span_err(
2930+
expr.span,
2931+
"struct literals are not allowed here",
2932+
);
2933+
err.multipart_suggestion(
2934+
"surround the struct literal with parenthesis",
2935+
vec![
2936+
(lo.shrink_to_lo(), "(".to_string()),
2937+
(expr.span.shrink_to_hi(), ")".to_string()),
2938+
],
2939+
Applicability::MachineApplicable,
2940+
);
2941+
err.emit();
2942+
}
2943+
return Some(expr);
2944+
}
2945+
None
2946+
}
2947+
29052948
fn parse_struct_expr(&mut self, lo: Span, pth: ast::Path, mut attrs: ThinVec<Attribute>)
29062949
-> PResult<'a, P<Expr>> {
29072950
let struct_sp = lo.to(self.prev_span);

src/test/ui/error-codes/E0423.rs

+2-4
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,13 @@ fn bar() {
1010
struct T {}
1111

1212
if let S { x: _x, y: 2 } = S { x: 1, y: 2 } { println!("Ok"); }
13-
//~^ ERROR E0423
14-
//~| expected type, found `1`
13+
//~^ ERROR struct literals are not allowed here
1514
if T {} == T {} { println!("Ok"); }
1615
//~^ ERROR E0423
1716
//~| ERROR expected expression, found `==`
1817
}
1918

2019
fn foo() {
2120
for _ in std::ops::Range { start: 0, end: 10 } {}
22-
//~^ ERROR E0423
23-
//~| ERROR expected type, found `0`
21+
//~^ ERROR struct literals are not allowed here
2422
}

src/test/ui/error-codes/E0423.stderr

+15-39
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,28 @@
1-
error: expected type, found `1`
2-
--> $DIR/E0423.rs:12:39
1+
error: struct literals are not allowed here
2+
--> $DIR/E0423.rs:12:32
33
|
44
LL | if let S { x: _x, y: 2 } = S { x: 1, y: 2 } { println!("Ok"); }
5-
| ^ expecting a type here because of type ascription
6-
|
7-
= note: type ascription is a nightly-only feature that lets you annotate an expression with a type: `<expr>: <type>`
8-
note: this expression expects an ascribed type after the colon
9-
--> $DIR/E0423.rs:12:36
5+
| ^^^^^^^^^^^^^^^^
6+
help: surround the struct literal with parenthesis
107
|
11-
LL | if let S { x: _x, y: 2 } = S { x: 1, y: 2 } { println!("Ok"); }
12-
| ^
13-
= help: this might be indicative of a syntax error elsewhere
8+
LL | if let S { x: _x, y: 2 } = (S { x: 1, y: 2 }) { println!("Ok"); }
9+
| ^ ^
1410

1511
error: expected expression, found `==`
16-
--> $DIR/E0423.rs:15:13
12+
--> $DIR/E0423.rs:14:13
1713
|
1814
LL | if T {} == T {} { println!("Ok"); }
1915
| ^^ expected expression
2016

21-
error: expected type, found `0`
22-
--> $DIR/E0423.rs:21:39
17+
error: struct literals are not allowed here
18+
--> $DIR/E0423.rs:20:14
2319
|
2420
LL | for _ in std::ops::Range { start: 0, end: 10 } {}
25-
| ^ expecting a type here because of type ascription
21+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
22+
help: surround the struct literal with parenthesis
2623
|
27-
= note: type ascription is a nightly-only feature that lets you annotate an expression with a type: `<expr>: <type>`
28-
note: this expression expects an ascribed type after the colon
29-
--> $DIR/E0423.rs:21:32
30-
|
31-
LL | for _ in std::ops::Range { start: 0, end: 10 } {}
32-
| ^^^^^
33-
= help: this might be indicative of a syntax error elsewhere
24+
LL | for _ in (std::ops::Range { start: 0, end: 10 }) {}
25+
| ^ ^
3426

3527
error[E0423]: expected function, found struct `Foo`
3628
--> $DIR/E0423.rs:4:13
@@ -41,30 +33,14 @@ LL | let f = Foo();
4133
| did you mean `Foo { /* fields */ }`?
4234
| help: a function with a similar name exists: `foo`
4335

44-
error[E0423]: expected value, found struct `S`
45-
--> $DIR/E0423.rs:12:32
46-
|
47-
LL | if let S { x: _x, y: 2 } = S { x: 1, y: 2 } { println!("Ok"); }
48-
| ^---------------
49-
| |
50-
| help: surround the struct literal with parenthesis: `(S { x: 1, y: 2 })`
51-
5236
error[E0423]: expected value, found struct `T`
53-
--> $DIR/E0423.rs:15:8
37+
--> $DIR/E0423.rs:14:8
5438
|
5539
LL | if T {} == T {} { println!("Ok"); }
5640
| ^---
5741
| |
5842
| help: surround the struct literal with parenthesis: `(T {})`
5943

60-
error[E0423]: expected value, found struct `std::ops::Range`
61-
--> $DIR/E0423.rs:21:14
62-
|
63-
LL | for _ in std::ops::Range { start: 0, end: 10 } {}
64-
| ^^^^^^^^^^^^^^^----------------------
65-
| |
66-
| help: surround the struct literal with parenthesis: `(std::ops::Range { start: 0, end: 10 })`
67-
68-
error: aborting due to 7 previous errors
44+
error: aborting due to 5 previous errors
6945

7046
For more information about this error, try `rustc --explain E0423`.

src/test/ui/parser/struct-literal-in-for.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ impl Foo {
99
}
1010

1111
fn main() {
12-
for x in Foo { //~ ERROR expected value, found struct `Foo`
13-
x: 3 //~ ERROR expected type, found `3`
14-
}.hi() { //~ ERROR expected one of `.`, `;`, `?`, `}`, or an operator, found `{`
12+
for x in Foo { //~ ERROR struct literals are not allowed here
13+
x: 3 //~^ ERROR `bool` is not an iterator
14+
}.hi() {
1515
println!("yo");
1616
}
1717
}

0 commit comments

Comments
 (0)