Skip to content

Commit a640315

Browse files
committed
Hide type errors likely caused by incorrect struct literal
When encountering a possible struct literal that is actually interpreted as a block because it could be a value `foo { bar }` or `foo { bar: baz }`, we now collect information on both `bar`'s span and the block's span. If we encounter a type error on `bar`, we skip it.
1 parent 8aaae42 commit a640315

File tree

8 files changed

+45
-20
lines changed

8 files changed

+45
-20
lines changed

src/librustc/session/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,9 @@ pub struct Session {
169169
/// Mapping from ident span to path span for paths that don't exist as written, but that
170170
/// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`.
171171
pub confused_type_with_std_module: Lock<FxHashMap<Span, Span>>,
172+
173+
/// This is a possible struct literal block.
174+
pub possible_struct_literal: Lock<FxHashSet<Span>>,
172175
}
173176

174177
pub struct PerfStats {
@@ -1253,6 +1256,7 @@ fn build_session_(
12531256
driver_lint_caps,
12541257
trait_methods_not_found: Lock::new(Default::default()),
12551258
confused_type_with_std_module: Lock::new(Default::default()),
1259+
possible_struct_literal: Lock::new(Default::default()),
12561260
};
12571261

12581262
validate_commandline_args_with_session_available(&sess);

src/librustc_resolve/diagnostics.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,7 @@ impl<'a> Resolver<'a> {
343343
format!("({})", snippet),
344344
Applicability::MaybeIncorrect,
345345
);
346+
self.session.possible_struct_literal.borrow_mut().insert(sp);
346347
} else {
347348
err.span_label(
348349
span, // Note the parenthesis surrounding the suggestion below

src/librustc_typeck/check/coercion.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1110,6 +1110,21 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E>
11101110
augment_error: Option<&mut dyn FnMut(&mut DiagnosticBuilder<'_>)>,
11111111
label_expression_as_expected: bool)
11121112
{
1113+
if let Some(expr) = &expression {
1114+
if fcx.tcx.sess.parse_sess.missing_ident_could_be_struct_literal
1115+
.borrow().contains(&expr.span)
1116+
{
1117+
for sp in fcx.tcx.sess.possible_struct_literal.borrow().iter() {
1118+
if sp.overlaps(expr.span) {
1119+
// Ignore type errors caused by likely struct literals. An error has
1120+
// already been emitted suggesting surrounding struct literal with `()` in
1121+
// a case like `if x == E::V { field } {}`
1122+
return;
1123+
}
1124+
}
1125+
}
1126+
}
1127+
11131128
// Incorporate whatever type inference information we have
11141129
// until now; in principle we might also want to process
11151130
// pending obligations, but doing so should only improve

src/libsyntax/parse/lexer/mod.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1241,7 +1241,8 @@ impl<'a> StringReader<'a> {
12411241

12421242
return Ok(self.with_str_from(start, |string| {
12431243
// FIXME: perform NFKC normalization here. (Issue #2253)
1244-
let ident = self.mk_ident(string);
1244+
let mut ident = self.mk_ident(string);
1245+
ident.span = self.mk_sp(start, self.pos);
12451246

12461247
if is_raw_ident {
12471248
let span = self.mk_sp(raw_start, self.pos);

src/libsyntax/parse/mod.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,9 @@ pub struct ParseSess {
4747
included_mod_stack: Lock<Vec<PathBuf>>,
4848
source_map: Lrc<SourceMap>,
4949
pub buffered_lints: Lock<Vec<BufferedEarlyLint>>,
50+
/// This ident comes from an abiguous parse that could belong to a struct literal in an invalid
51+
/// context. We use it during type-checking to silence those errors.
52+
pub missing_ident_could_be_struct_literal: Lock<FxHashSet<Span>>,
5053
}
5154

5255
impl ParseSess {
@@ -70,6 +73,7 @@ impl ParseSess {
7073
included_mod_stack: Lock::new(vec![]),
7174
source_map,
7275
buffered_lints: Lock::new(vec![]),
76+
missing_ident_could_be_struct_literal: Lock::new(Default::default()),
7377
}
7478
}
7579

src/libsyntax/parse/parser.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2921,6 +2921,19 @@ impl<'a> Parser<'a> {
29212921
self.look_ahead(3, |t| !t.can_begin_type())
29222922
)
29232923
);
2924+
// could be a block but we won't know until type-checking
2925+
if self.look_ahead(1, |t| t.is_ident()) && (
2926+
// foo { bar }
2927+
self.look_ahead(2, |t| t == &token::CloseDelim(token::Brace)) ||
2928+
// foo { bar: baz }
2929+
self.look_ahead(2, |t| t == &token::Colon)
2930+
) {
2931+
self.look_ahead(1, |t| {
2932+
if let token::Ident(ident, _) = t {
2933+
self.sess.missing_ident_could_be_struct_literal.borrow_mut().insert(ident.span);
2934+
}
2935+
});
2936+
}
29242937

29252938
if struct_allowed || certainly_not_a_block() {
29262939
// This is a struct literal, but we don't can't accept them here

src/test/ui/struct-literal-variant-in-if.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ fn test_E(x: E) {
99
let field = true;
1010
if x == E::V { field } {}
1111
//~^ ERROR expected value, found struct variant `E::V`
12-
//~| ERROR mismatched types
1312
if x == E::I { field1: true, field2: 42 } {}
1413
//~^ ERROR struct literals are not allowed here
1514
if x == E::V { field: false } {}
Lines changed: 6 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: struct literals are not allowed here
2-
--> $DIR/struct-literal-variant-in-if.rs:13:13
2+
--> $DIR/struct-literal-variant-in-if.rs:12:13
33
|
44
LL | if x == E::I { field1: true, field2: 42 } {}
55
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
@@ -9,7 +9,7 @@ LL | if x == (E::I { field1: true, field2: 42 }) {}
99
| ^ ^
1010

1111
error: struct literals are not allowed here
12-
--> $DIR/struct-literal-variant-in-if.rs:15:13
12+
--> $DIR/struct-literal-variant-in-if.rs:14:13
1313
|
1414
LL | if x == E::V { field: false } {}
1515
| ^^^^^^^^^^^^^^^^^^^^^
@@ -19,7 +19,7 @@ LL | if x == (E::V { field: false }) {}
1919
| ^ ^
2020

2121
error: struct literals are not allowed here
22-
--> $DIR/struct-literal-variant-in-if.rs:17:13
22+
--> $DIR/struct-literal-variant-in-if.rs:16:13
2323
|
2424
LL | if x == E::J { field: -42 } {}
2525
| ^^^^^^^^^^^^^^^^^^^
@@ -29,7 +29,7 @@ LL | if x == (E::J { field: -42 }) {}
2929
| ^ ^
3030

3131
error: struct literals are not allowed here
32-
--> $DIR/struct-literal-variant-in-if.rs:19:13
32+
--> $DIR/struct-literal-variant-in-if.rs:18:13
3333
|
3434
LL | if x == E::K { field: "" } {}
3535
| ^^^^^^^^^^^^^^^^^^
@@ -47,27 +47,15 @@ LL | if x == E::V { field } {}
4747
| help: surround the struct literal with parenthesis: `(E::V { field })`
4848

4949
error[E0308]: mismatched types
50-
--> $DIR/struct-literal-variant-in-if.rs:10:20
51-
|
52-
LL | fn test_E(x: E) {
53-
| - help: try adding a return type: `-> bool`
54-
LL | let field = true;
55-
LL | if x == E::V { field } {}
56-
| ^^^^^ expected (), found bool
57-
|
58-
= note: expected type `()`
59-
found type `bool`
60-
61-
error[E0308]: mismatched types
62-
--> $DIR/struct-literal-variant-in-if.rs:21:20
50+
--> $DIR/struct-literal-variant-in-if.rs:20:20
6351
|
6452
LL | let y: usize = ();
6553
| ^^ expected usize, found ()
6654
|
6755
= note: expected type `usize`
6856
found type `()`
6957

70-
error: aborting due to 7 previous errors
58+
error: aborting due to 6 previous errors
7159

7260
Some errors have detailed explanations: E0308, E0423.
7361
For more information about an error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)