Skip to content

Hide type errors likely caused by incorrect struct literal #60118

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions src/librustc/session/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<FxHashMap<Span, Span>>,

/// This is a possible struct literal block.
pub possible_struct_literal: Lock<FxHashSet<Span>>,
}

pub struct PerfStats {
Expand Down Expand Up @@ -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);
Expand Down
1 change: 1 addition & 0 deletions src/librustc_resolve/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
29 changes: 24 additions & 5 deletions src/librustc_resolve/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3222,11 +3222,30 @@ impl<'a> Resolver<'a> {
let is_expected = &|def| source.is_expected(def);

let report_errors = |this: &mut Self, def: Option<Def>| {
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()
};

Expand Down
15 changes: 15 additions & 0 deletions src/librustc_typeck/check/coercion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 3 additions & 1 deletion src/libsyntax/parse/lexer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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()),
}
}

Expand Down
4 changes: 4 additions & 0 deletions src/libsyntax/parse/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@ pub struct ParseSess {
included_mod_stack: Lock<Vec<PathBuf>>,
source_map: Lrc<SourceMap>,
pub buffered_lints: Lock<Vec<BufferedEarlyLint>>,
/// 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<FxHashSet<Span>>,
}

impl ParseSess {
Expand All @@ -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()),
}
}

Expand Down
22 changes: 22 additions & 0 deletions src/libsyntax/parse/parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
3 changes: 2 additions & 1 deletion src/test/ui/struct-literal-variant-in-if.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 } {}
Expand Down
26 changes: 11 additions & 15 deletions src/test/ui/struct-literal-variant-in-if.stderr
Original file line number Diff line number Diff line change
@@ -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 } {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -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 } {}
| ^^^^^^^^^^^^^^^^^^^^^
Expand All @@ -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 } {}
| ^^^^^^^^^^^^^^^^^^^
Expand All @@ -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: "" } {}
| ^^^^^^^^^^^^^^^^^^
Expand All @@ -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 ()
Expand Down