Skip to content

Commit b22261e

Browse files
estebankpetrochenkov
authored andcommitted
Identify missing ambiguous case with best effort suggestion
1 parent 2d37ce1 commit b22261e

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
@@ -238,6 +238,56 @@ impl<'a> Resolver<'a> {
238238
(err, candidates)
239239
}
240240

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

329+
let (followed_by_brace, closing_brace) = self.followed_by_brace(span);
330+
279331
match (def, source) {
280332
(Def::Macro(..), _) => {
281333
err.span_suggestion(
@@ -329,52 +381,6 @@ impl<'a> Resolver<'a> {
329381
);
330382
}
331383
} else {
332-
// HACK(estebank): find a better way to figure out that this was a
333-
// parser issue where a struct literal is being used on an expression
334-
// where a brace being opened means a block is being started. Look
335-
// ahead for the next text to see if `span` is followed by a `{`.
336-
let sm = self.session.source_map();
337-
let mut sp = span;
338-
loop {
339-
sp = sm.next_point(sp);
340-
match sm.span_to_snippet(sp) {
341-
Ok(ref snippet) => {
342-
if snippet.chars().any(|c| { !c.is_whitespace() }) {
343-
break;
344-
}
345-
}
346-
_ => break,
347-
}
348-
}
349-
let followed_by_brace = match sm.span_to_snippet(sp) {
350-
Ok(ref snippet) if snippet == "{" => true,
351-
_ => false,
352-
};
353-
// In case this could be a struct literal that needs to be surrounded
354-
// by parenthesis, find the appropriate span.
355-
let mut i = 0;
356-
let mut closing_brace = None;
357-
loop {
358-
sp = sm.next_point(sp);
359-
match sm.span_to_snippet(sp) {
360-
Ok(ref snippet) => {
361-
if snippet == "}" {
362-
let sp = span.to(sp);
363-
if let Ok(snippet) = sm.span_to_snippet(sp) {
364-
closing_brace = Some((sp, snippet));
365-
}
366-
break;
367-
}
368-
}
369-
_ => break,
370-
}
371-
i += 1;
372-
// The bigger the span, the more likely we're incorrect --
373-
// bound it to 100 chars long.
374-
if i > 100 {
375-
break;
376-
}
377-
}
378384
match source {
379385
PathSource::Expr(Some(parent)) => if !path_sep(err, &parent) {
380386
err.span_label(
@@ -409,7 +415,35 @@ impl<'a> Resolver<'a> {
409415
(Def::Union(..), _) |
410416
(Def::Variant(..), _) |
411417
(Def::Ctor(_, _, CtorKind::Fictive), _) if ns == ValueNS => {
412-
err.span_label(span, format!("did you mean `{} {{ /* fields */ }}`?", path_str));
418+
match source {
419+
PathSource::Expr(Some(parent)) => if !path_sep(err, &parent) {
420+
err.span_label(
421+
span,
422+
format!("did you mean `{} {{ /* fields */ }}`?", path_str),
423+
);
424+
}
425+
PathSource::Expr(None) if followed_by_brace == true => {
426+
if let Some((sp, snippet)) = closing_brace {
427+
err.span_suggestion(
428+
sp,
429+
"surround the struct literal with parenthesis",
430+
format!("({})", snippet),
431+
Applicability::MaybeIncorrect,
432+
);
433+
} else {
434+
err.span_label(
435+
span,
436+
format!("did you mean `({} {{ /* fields */ }})`?", path_str),
437+
);
438+
}
439+
},
440+
_ => {
441+
err.span_label(
442+
span,
443+
format!("did you mean `{} {{ /* fields */ }}`?", path_str),
444+
);
445+
},
446+
}
413447
}
414448
(Def::SelfTy(..), _) if ns == ValueNS => {
415449
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)