Skip to content

Commit f1be8d1

Browse files
committed
Identify missing ambiguous case with best effort suggestion
1 parent 2f36b54 commit f1be8d1

File tree

4 files changed

+132
-51
lines changed

4 files changed

+132
-51
lines changed

src/librustc_resolve/diagnostics.rs

Lines changed: 81 additions & 47 deletions
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,8 @@ impl<'a> Resolver<'a> {
278328
_ => false,
279329
};
280330

331+
let (followed_by_brace, closing_brace) = self.followed_by_brace(span);
332+
281333
match (def, source) {
282334
(Def::Macro(..), _) => {
283335
err.span_suggestion(
@@ -331,52 +383,6 @@ impl<'a> Resolver<'a> {
331383
);
332384
}
333385
} 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-
}
380386
match source {
381387
PathSource::Expr(Some(parent)) => if !path_sep(err, &parent) {
382388
err.span_label(
@@ -411,7 +417,35 @@ impl<'a> Resolver<'a> {
411417
(Def::Union(..), _) |
412418
(Def::Variant(..), _) |
413419
(Def::Ctor(_, _, CtorKind::Fictive), _) if ns == ValueNS => {
414-
err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?", path_str));
420+
match source {
421+
PathSource::Expr(Some(parent)) => if !path_sep(err, &parent) {
422+
err.span_label(
423+
span,
424+
format!("did you mean `{} {{ /* fields */ }}`?", path_str),
425+
);
426+
}
427+
PathSource::Expr(None) if followed_by_brace == true => {
428+
if let Some((sp, snippet)) = closing_brace {
429+
err.span_suggestion(
430+
sp,
431+
"surround the struct literal with parenthesis",
432+
format!("({})", snippet),
433+
Applicability::MaybeIncorrect,
434+
);
435+
} else {
436+
err.span_label(
437+
span,
438+
format!("did you mean `({} {{ /* fields */ }})`?", path_str),
439+
);
440+
}
441+
},
442+
_ => {
443+
err.span_label(
444+
span,
445+
format!("did you mean `{} {{ /* fields */ }}`?", path_str),
446+
);
447+
},
448+
}
415449
}
416450
(Def::SelfTy(..), _) if ns == ValueNS => {
417451
err.span_label(span, fallback_label);

src/libsyntax/parse/parser.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2856,7 +2856,7 @@ impl<'a> Parser<'a> {
28562856
hi = self.prev_span;
28572857
ex = ExprKind::Mac(respan(lo.to(hi), Mac_ { path, tts, delim }));
28582858
} else if self.check(&token::OpenDelim(token::Brace)) {
2859-
if let Some(expr) = self.should_parse_struct_expr(lo, path.clone(), attrs.clone()) {
2859+
if let Some(expr) = self.should_parse_struct_expr(lo, &path, &attrs) {
28602860
return expr;
28612861
} else {
28622862
hi = path.span;
@@ -2907,8 +2907,8 @@ impl<'a> Parser<'a> {
29072907
fn should_parse_struct_expr(
29082908
&mut self,
29092909
lo: Span,
2910-
path: ast::Path,
2911-
attrs: ThinVec<Attribute>,
2910+
path: &ast::Path,
2911+
attrs: &ThinVec<Attribute>,
29122912
) -> Option<PResult<'a, P<Expr>>> {
29132913
let could_be_struct = self.look_ahead(1, |t| t.is_ident()) && (
29142914
self.look_ahead(2, |t| *t == token::Colon)
@@ -2924,7 +2924,7 @@ impl<'a> Parser<'a> {
29242924
parse_struct = true;
29252925
}
29262926
if parse_struct {
2927-
match self.parse_struct_expr(lo, path, attrs) {
2927+
match self.parse_struct_expr(lo, path.clone(), attrs.clone()) {
29282928
Err(err) => return Some(Err(err)),
29292929
Ok(expr) => {
29302930
if bad_struct {
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
2+
enum E {
3+
V { field: bool }
4+
}
5+
fn test_E(x: E) {
6+
let field = true;
7+
if x == E::V { field } {}
8+
//~^ ERROR expected value, found struct variant `E::V`
9+
//~| ERROR mismatched types
10+
let y: usize = ();
11+
//~^ ERROR mismatched types
12+
}
13+
14+
fn main() {}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error[E0423]: expected value, found struct variant `E::V`
2+
--> $DIR/struct-literal-variant-in-if.rs:7:13
3+
|
4+
LL | if x == E::V { field } {}
5+
| ^^^^----------
6+
| |
7+
| help: surround the struct literal with parenthesis: `(E::V { field })`
8+
9+
error[E0308]: mismatched types
10+
--> $DIR/struct-literal-variant-in-if.rs:7:20
11+
|
12+
LL | fn test_E(x: E) {
13+
| - help: try adding a return type: `-> bool`
14+
LL | let field = true;
15+
LL | if x == E::V { field } {}
16+
| ^^^^^ expected (), found bool
17+
|
18+
= note: expected type `()`
19+
found type `bool`
20+
21+
error[E0308]: mismatched types
22+
--> $DIR/struct-literal-variant-in-if.rs:10:20
23+
|
24+
LL | let y: usize = ();
25+
| ^^ expected usize, found ()
26+
|
27+
= note: expected type `usize`
28+
found type `()`
29+
30+
error: aborting due to 3 previous errors
31+
32+
Some errors occurred: E0308, E0423.
33+
For more information about an error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)