diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index 54eb1d815d31a..0be4d5d22d1ce 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -169,6 +169,9 @@ pub struct Session { /// Mapping from ident span to path span for paths that don't exist as written, but that /// exist under `std`. For example, wrote `str::from_utf8` instead of `std::str::from_utf8`. pub confused_type_with_std_module: Lock>, + + /// This is a possible struct literal block. + pub possible_struct_literal: Lock>, } pub struct PerfStats { @@ -1253,6 +1256,7 @@ fn build_session_( driver_lint_caps, trait_methods_not_found: Lock::new(Default::default()), confused_type_with_std_module: Lock::new(Default::default()), + possible_struct_literal: Lock::new(Default::default()), }; validate_commandline_args_with_session_available(&sess); diff --git a/src/librustc_resolve/diagnostics.rs b/src/librustc_resolve/diagnostics.rs index c89c222ad57fc..f29078ce51e25 100644 --- a/src/librustc_resolve/diagnostics.rs +++ b/src/librustc_resolve/diagnostics.rs @@ -343,6 +343,7 @@ impl<'a> Resolver<'a> { format!("({})", snippet), Applicability::MaybeIncorrect, ); + self.session.possible_struct_literal.borrow_mut().insert(sp); } else { err.span_label( span, // Note the parenthesis surrounding the suggestion below diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 8df83120738c1..c2f55d77408f6 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -3222,11 +3222,30 @@ impl<'a> Resolver<'a> { let is_expected = &|def| source.is_expected(def); let report_errors = |this: &mut Self, def: Option| { - let (err, candidates) = this.smart_resolve_report_errors(path, span, source, def); - let def_id = this.current_module.normal_ancestor_id; - let node_id = this.definitions.as_local_node_id(def_id).unwrap(); - let better = def.is_some(); - this.use_injections.push(UseError { err, candidates, node_id, better }); + let mut report = true; + + if let &[segment] = path { + if this.session.parse_sess.missing_ident_could_be_struct_literal + .borrow().contains(&segment.ident.span) + { + for sp in this.session.possible_struct_literal.borrow().iter() { + if sp.overlaps(segment.ident.span) { + // Ignore errors caused by likely struct literals. An error has + // already been emitted suggesting surrounding struct literal with + // `()` in a case like `if x == E::V { field: field } {}` + report = false; + break; + } + } + } + } + if report { + let (err, candidates) = this.smart_resolve_report_errors(path, span, source, def); + let def_id = this.current_module.normal_ancestor_id; + let node_id = this.definitions.as_local_node_id(def_id).unwrap(); + let better = def.is_some(); + this.use_injections.push(UseError { err, candidates, node_id, better }); + } err_path_resolution() }; diff --git a/src/librustc_typeck/check/coercion.rs b/src/librustc_typeck/check/coercion.rs index c470bc09e8cd0..12cc154ae34d3 100644 --- a/src/librustc_typeck/check/coercion.rs +++ b/src/librustc_typeck/check/coercion.rs @@ -1110,6 +1110,21 @@ impl<'gcx, 'tcx, 'exprs, E> CoerceMany<'gcx, 'tcx, 'exprs, E> augment_error: Option<&mut dyn FnMut(&mut DiagnosticBuilder<'_>)>, label_expression_as_expected: bool) { + if let Some(expr) = &expression { + if fcx.tcx.sess.parse_sess.missing_ident_could_be_struct_literal + .borrow().contains(&expr.span) + { + for sp in fcx.tcx.sess.possible_struct_literal.borrow().iter() { + if sp.overlaps(expr.span) { + // Ignore type errors caused by likely struct literals. An error has + // already been emitted suggesting surrounding struct literal with `()` in + // a case like `if x == E::V { field } {}` + return; + } + } + } + } + // Incorporate whatever type inference information we have // until now; in principle we might also want to process // pending obligations, but doing so should only improve diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 3b980fafc2f1c..b9a822d848435 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -1241,7 +1241,8 @@ impl<'a> StringReader<'a> { return Ok(self.with_str_from(start, |string| { // FIXME: perform NFKC normalization here. (Issue #2253) - let ident = self.mk_ident(string); + let mut ident = self.mk_ident(string); + ident.span = self.mk_sp(start, self.pos); if is_raw_ident { let span = self.mk_sp(raw_start, self.pos); @@ -1932,6 +1933,7 @@ mod tests { raw_identifier_spans: Lock::new(Vec::new()), registered_diagnostics: Lock::new(ErrorMap::new()), buffered_lints: Lock::new(vec![]), + missing_ident_could_be_struct_literal: Lock::new(Default::default()), } } diff --git a/src/libsyntax/parse/mod.rs b/src/libsyntax/parse/mod.rs index 371e8fe5cf66f..c9f9b60f33224 100644 --- a/src/libsyntax/parse/mod.rs +++ b/src/libsyntax/parse/mod.rs @@ -47,6 +47,9 @@ pub struct ParseSess { included_mod_stack: Lock>, source_map: Lrc, pub buffered_lints: Lock>, + /// This ident comes from an abiguous parse that could belong to a struct literal in an invalid + /// context. We use it during type-checking to silence those errors. + pub missing_ident_could_be_struct_literal: Lock>, } impl ParseSess { @@ -70,6 +73,7 @@ impl ParseSess { included_mod_stack: Lock::new(vec![]), source_map, buffered_lints: Lock::new(vec![]), + missing_ident_could_be_struct_literal: Lock::new(Default::default()), } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 8feab373e7102..3c04f7b4b2c4c 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2921,6 +2921,28 @@ impl<'a> Parser<'a> { self.look_ahead(3, |t| !t.can_begin_type()) ) ); + // could be a block but we won't know until type-checking + // foo { bar } + if self.look_ahead(1, |t| t.is_ident()) && + self.look_ahead(2, |t| t == &token::CloseDelim(token::Brace)) + { + self.look_ahead(1, |t| { + if let token::Ident(ident, _) = t { + self.sess.missing_ident_could_be_struct_literal.borrow_mut().insert(ident.span); + } + }); + } + // foo { bar: baz } + if self.look_ahead(1, |t| t.is_ident()) && + self.look_ahead(2, |t| t == &token::Colon) && + self.look_ahead(3, |t| t.is_ident()) + { + self.look_ahead(3, |t| { + if let token::Ident(ident, _) = t { + self.sess.missing_ident_could_be_struct_literal.borrow_mut().insert(ident.span); + } + }); + } if struct_allowed || certainly_not_a_block() { // This is a struct literal, but we don't can't accept them here diff --git a/src/test/ui/struct-literal-variant-in-if.rs b/src/test/ui/struct-literal-variant-in-if.rs index 4ef8effaf1f5f..fd9e31815f851 100644 --- a/src/test/ui/struct-literal-variant-in-if.rs +++ b/src/test/ui/struct-literal-variant-in-if.rs @@ -9,7 +9,8 @@ fn test_E(x: E) { let field = true; if x == E::V { field } {} //~^ ERROR expected value, found struct variant `E::V` - //~| ERROR mismatched types + if x == E::V { field: field } {} + //~^ ERROR expected value, found struct variant `E::V` if x == E::I { field1: true, field2: 42 } {} //~^ ERROR struct literals are not allowed here if x == E::V { field: false } {} diff --git a/src/test/ui/struct-literal-variant-in-if.stderr b/src/test/ui/struct-literal-variant-in-if.stderr index 55f23baea7aa8..7eb080eb90279 100644 --- a/src/test/ui/struct-literal-variant-in-if.stderr +++ b/src/test/ui/struct-literal-variant-in-if.stderr @@ -1,5 +1,5 @@ error: struct literals are not allowed here - --> $DIR/struct-literal-variant-in-if.rs:13:13 + --> $DIR/struct-literal-variant-in-if.rs:14:13 | LL | if x == E::I { field1: true, field2: 42 } {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -9,7 +9,7 @@ LL | if x == (E::I { field1: true, field2: 42 }) {} | ^ ^ error: struct literals are not allowed here - --> $DIR/struct-literal-variant-in-if.rs:15:13 + --> $DIR/struct-literal-variant-in-if.rs:16:13 | LL | if x == E::V { field: false } {} | ^^^^^^^^^^^^^^^^^^^^^ @@ -19,7 +19,7 @@ LL | if x == (E::V { field: false }) {} | ^ ^ error: struct literals are not allowed here - --> $DIR/struct-literal-variant-in-if.rs:17:13 + --> $DIR/struct-literal-variant-in-if.rs:18:13 | LL | if x == E::J { field: -42 } {} | ^^^^^^^^^^^^^^^^^^^ @@ -29,7 +29,7 @@ LL | if x == (E::J { field: -42 }) {} | ^ ^ error: struct literals are not allowed here - --> $DIR/struct-literal-variant-in-if.rs:19:13 + --> $DIR/struct-literal-variant-in-if.rs:20:13 | LL | if x == E::K { field: "" } {} | ^^^^^^^^^^^^^^^^^^ @@ -46,20 +46,16 @@ LL | if x == E::V { field } {} | | | help: surround the struct literal with parenthesis: `(E::V { field })` -error[E0308]: mismatched types - --> $DIR/struct-literal-variant-in-if.rs:10:20 - | -LL | fn test_E(x: E) { - | - help: try adding a return type: `-> bool` -LL | let field = true; -LL | if x == E::V { field } {} - | ^^^^^ expected (), found bool +error[E0423]: expected value, found struct variant `E::V` + --> $DIR/struct-literal-variant-in-if.rs:12:13 | - = note: expected type `()` - found type `bool` +LL | if x == E::V { field: field } {} + | ^^^^----------------- + | | + | help: surround the struct literal with parenthesis: `(E::V { field: field })` error[E0308]: mismatched types - --> $DIR/struct-literal-variant-in-if.rs:21:20 + --> $DIR/struct-literal-variant-in-if.rs:22:20 | LL | let y: usize = (); | ^^ expected usize, found ()