diff --git a/compiler/rustc_resolve/src/diagnostics/impls.rs b/compiler/rustc_resolve/src/diagnostics/impls.rs index ccb6139eae652..52016ca5f8d5c 100644 --- a/compiler/rustc_resolve/src/diagnostics/impls.rs +++ b/compiler/rustc_resolve/src/diagnostics/impls.rs @@ -1987,7 +1987,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { is_expected, &|_| true, ); - self.add_typo_suggestion(err, suggestion, ident.span); + self.add_typo_suggestion(err, suggestion, ident.span, None); self.detect_derive_attribute(err, ident, parent_scope, sugg_span); let import_suggestions = @@ -2271,6 +2271,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { err: &mut Diag<'_>, suggestion: Option, span: Span, + prefix: Option, ) { let suggestion = match suggestion { None => return, @@ -2345,7 +2346,10 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { format!("maybe you meant this {}", suggestion.res.descr()) } }; - (span, msg, suggestion.candidate.to_ident_string()) + let candidate_str = suggestion.candidate.to_ident_string(); + let sugg = + if let Some(p) = prefix { format!("{p}: {candidate_str}") } else { candidate_str }; + (span, msg, sugg) }; err.span_suggestion_verbose(span, msg, sugg, Applicability::MaybeIncorrect); } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index e0a3b78fa5e2e..b2061cd9d9d91 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1194,7 +1194,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { } } } - self.r.add_typo_suggestion(err, typo_sugg, ident_span); + self.r.add_typo_suggestion(err, typo_sugg, ident_span, None); return (true, suggested_candidates, candidates); } @@ -1244,7 +1244,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { &base_error.fallback_label, ) { // We do this to avoid losing a secondary span when we override the main error span. - self.r.add_typo_suggestion(err, typo_sugg, ident_span); + self.r.add_typo_suggestion(err, typo_sugg, ident_span, None); return (true, suggested_candidates, candidates); } } @@ -1429,13 +1429,22 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { return false; } + // Preserve the field name for struct field shorthands to avoid suggesting invalid shorthands. + let mut prefix = None; + if let PathSource::Expr(Some(ast::Expr { kind: ExprKind::Struct(expr), .. })) = source + && let Some(ident) = path.last().map(|seg| seg.ident) + && expr.fields.iter().any(|f| f.ident == ident && f.is_shorthand) + { + prefix = Some(ident); + } + let typo_sugg = self.lookup_typo_candidate(path, following_seg, source.namespace(), is_expected); let mut fallback = true; let typo_sugg = typo_sugg .to_opt_suggestion() .filter(|sugg| !suggested_candidates.contains(sugg.candidate.as_str())); - self.r.add_typo_suggestion(err, typo_sugg, ident_span); + self.r.add_typo_suggestion(err, typo_sugg, ident_span, prefix); match self.diag_metadata.current_let_binding { Some((pat_sp, Some(ty_sp), None)) @@ -1453,7 +1462,7 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // If the trait has a single item (which wasn't matched by the algorithm), suggest it let suggestion = self.get_single_associated_item(path, &source, is_expected); - self.r.add_typo_suggestion(err, suggestion, ident_span); + self.r.add_typo_suggestion(err, suggestion, ident_span, prefix); if self.let_binding_suggestion(err, ident_span) { fallback = false; diff --git a/tests/ui/resolve/struct-shorthand-typo.fixed b/tests/ui/resolve/struct-shorthand-typo.fixed new file mode 100644 index 0000000000000..b06bb357d6838 --- /dev/null +++ b/tests/ui/resolve/struct-shorthand-typo.fixed @@ -0,0 +1,44 @@ +//! Regression test for +//! +//! Tests that typo suggestions for unresolved struct shorthands emit valid `field: value` syntax. + +#![allow(unused)] +#![allow(non_snake_case)] + +//@ run-rustfix + +const A: usize = 0; +struct S { a: usize } + +fn test_basic() { + S { a: A }; //~ ERROR cannot find value `a` in this scope +} + +const B: usize = 1; +struct Multi { a: usize, b: usize } +fn test_multi() { + Multi { a: 0, b: B }; //~ ERROR cannot find value `b` in this scope +} + +const C: usize = 2; +struct Fru { c: usize, d: usize } +fn test_fru() { + let base = Fru { c: 0, d: 0 }; + Fru { c: C, ..base }; //~ ERROR cannot find value `c` in this scope +} + +const D: usize = 3; +enum E { + StructVariant { d: usize }, +} +fn test_enum() { + E::StructVariant { d: D }; //~ ERROR cannot find value `d` in this scope +} + +struct Local { e: usize } +fn test_local() { + let E = 4; + Local { e: E }; //~ ERROR cannot find value `e` in this scope +} + +fn main() {} diff --git a/tests/ui/resolve/struct-shorthand-typo.rs b/tests/ui/resolve/struct-shorthand-typo.rs new file mode 100644 index 0000000000000..e8c1765751d73 --- /dev/null +++ b/tests/ui/resolve/struct-shorthand-typo.rs @@ -0,0 +1,44 @@ +//! Regression test for +//! +//! Tests that typo suggestions for unresolved struct shorthands emit valid `field: value` syntax. + +#![allow(unused)] +#![allow(non_snake_case)] + +//@ run-rustfix + +const A: usize = 0; +struct S { a: usize } + +fn test_basic() { + S { a }; //~ ERROR cannot find value `a` in this scope +} + +const B: usize = 1; +struct Multi { a: usize, b: usize } +fn test_multi() { + Multi { a: 0, b }; //~ ERROR cannot find value `b` in this scope +} + +const C: usize = 2; +struct Fru { c: usize, d: usize } +fn test_fru() { + let base = Fru { c: 0, d: 0 }; + Fru { c, ..base }; //~ ERROR cannot find value `c` in this scope +} + +const D: usize = 3; +enum E { + StructVariant { d: usize }, +} +fn test_enum() { + E::StructVariant { d }; //~ ERROR cannot find value `d` in this scope +} + +struct Local { e: usize } +fn test_local() { + let E = 4; + Local { e }; //~ ERROR cannot find value `e` in this scope +} + +fn main() {} diff --git a/tests/ui/resolve/struct-shorthand-typo.stderr b/tests/ui/resolve/struct-shorthand-typo.stderr new file mode 100644 index 0000000000000..620dbbb1eb51c --- /dev/null +++ b/tests/ui/resolve/struct-shorthand-typo.stderr @@ -0,0 +1,78 @@ +error[E0425]: cannot find value `a` in this scope + --> $DIR/struct-shorthand-typo.rs:14:9 + | +LL | S { a }; + | ^ not found in this scope + | +note: similarly named constant `A` defined here + --> $DIR/struct-shorthand-typo.rs:10:1 + | +LL | const A: usize = 0; + | ^^^^^^^^^^^^^^^^^^^ +help: a constant with a similar name exists + | +LL | S { a: A }; + | +++ + +error[E0425]: cannot find value `b` in this scope + --> $DIR/struct-shorthand-typo.rs:20:19 + | +LL | Multi { a: 0, b }; + | ^ not found in this scope + | +note: similarly named constant `B` defined here + --> $DIR/struct-shorthand-typo.rs:17:1 + | +LL | const B: usize = 1; + | ^^^^^^^^^^^^^^^^^^^ +help: a constant with a similar name exists + | +LL | Multi { a: 0, b: B }; + | +++ + +error[E0425]: cannot find value `c` in this scope + --> $DIR/struct-shorthand-typo.rs:27:11 + | +LL | Fru { c, ..base }; + | ^ not found in this scope + | +note: similarly named constant `C` defined here + --> $DIR/struct-shorthand-typo.rs:23:1 + | +LL | const C: usize = 2; + | ^^^^^^^^^^^^^^^^^^^ +help: a constant with a similar name exists + | +LL | Fru { c: C, ..base }; + | +++ + +error[E0425]: cannot find value `d` in this scope + --> $DIR/struct-shorthand-typo.rs:35:24 + | +LL | E::StructVariant { d }; + | ^ not found in this scope + | +note: similarly named constant `D` defined here + --> $DIR/struct-shorthand-typo.rs:30:1 + | +LL | const D: usize = 3; + | ^^^^^^^^^^^^^^^^^^^ +help: a constant with a similar name exists + | +LL | E::StructVariant { d: D }; + | +++ + +error[E0425]: cannot find value `e` in this scope + --> $DIR/struct-shorthand-typo.rs:41:13 + | +LL | Local { e }; + | ^ not found in this scope + | +help: a local variable with a similar name exists + | +LL | Local { e: E }; + | +++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/tests/ui/structs/struct-fields-shorthand-unresolved.stderr b/tests/ui/structs/struct-fields-shorthand-unresolved.stderr index 9b4590c76afb1..d3360885fd523 100644 --- a/tests/ui/structs/struct-fields-shorthand-unresolved.stderr +++ b/tests/ui/structs/struct-fields-shorthand-unresolved.stderr @@ -6,9 +6,8 @@ LL | y | help: a local variable with a similar name exists | -LL - y -LL + x - | +LL | y: x + | +++ error: aborting due to 1 previous error diff --git a/tests/ui/typeck/issue-114423-ice-regression-in-suggestion.stderr b/tests/ui/typeck/issue-114423-ice-regression-in-suggestion.stderr index 9510001193d24..4bf3c6a174410 100644 --- a/tests/ui/typeck/issue-114423-ice-regression-in-suggestion.stderr +++ b/tests/ui/typeck/issue-114423-ice-regression-in-suggestion.stderr @@ -18,9 +18,8 @@ LL | let _ = RGB { r, g, b }; | help: a local variable with a similar name exists | -LL - let _ = RGB { r, g, b }; -LL + let _ = RGB { r, b, b }; - | +LL | let _ = RGB { r, g: b, b }; + | +++ error[E0308]: mismatched types --> $DIR/issue-114423-ice-regression-in-suggestion.rs:7:50