Skip to content

Commit 813d288

Browse files
authored
Rollup merge of #158934 - Rohan-Singla:fix/158492, r=jackh726
diagnostics: fix `let x: vec![]` suggestion pointing into stdlib # Fixes #158492 When a macro call like `vec![]` appears in type position (`let x: vec![];`), the compiler correctly detects the likely typo (`:` instead of `=`), but emits a broken suggestion pointing into the standard library (`library/alloc/src/macros.rs:44`) instead of the user's own code. ## Root cause After macro expansion, `vec![]` becomes `Vec::new()`. The HIR type node's span therefore points to the expanded code inside the macro definition rather than the original `vec![]` call site. The suggestion span was computed as: `stmt.pat.span.between(hir_ty.span)` This creates a span crossing from the user's code into the standard library, causing the suggestion renderer to display the standard library location. ## Fix Compute the span using `find_ancestor_in_same_ctxt` so that the pattern and type are resolved into a common syntax context before calling `between`. For `let x: vec![];`, this walks the type's span up the expansion chain until it reaches the user's file, keeping the suggestion within a single file. This also handles the reverse case, which `source_callsite` alone does not: when the `let` itself comes from a macro body while the pattern is a call-site metavariable, there is no common context. In that case, no suggestion is emitted rather than producing a nonsensical one. ## Before ```text help: use `=` if you meant to assign --> library/alloc/src/macros.rs:44:9 | 44 - $crate::vec::Vec::new() 44 + = ``` ## After ```text help: use `=` if you meant to assign | LL - let x: vec![]; LL + let x = vec![]; ``` ## Tests Two test cases were added to `tests/ui/suggestions/let-binding-init-expr-as-ty.rs`, which already covers the related `let x: Vec::new()` and `let x: S::new(())` cases: 1. The `vec![]` case described above. 2. A `let` inside a `macro_rules!` body where no suggestion should be emitted. ## Note on file layout The new `eq_ctxt_suggestion_span` helper lives in `hir_ty_lowering/errors.rs` rather than next to its callers in `mod.rs`. `mod.rs` was already within a few lines of tidy's 3000-line limit, so keeping the helper there caused the style check to fail. This is pure code motion with no behavior change.
2 parents 81d5d74 + f151cb9 commit 813d288

4 files changed

Lines changed: 79 additions & 25 deletions

File tree

compiler/rustc_hir_analysis/src/hir_ty_lowering/errors.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2055,3 +2055,21 @@ fn assoc_tag_str(assoc_tag: ty::AssocTag) -> &'static str {
20552055
ty::AssocTag::Type => "type",
20562056
}
20572057
}
2058+
2059+
/// Computes the `pat.between(ty)` span for the "use `=`" suggestion on `let pat: ty`.
2060+
/// Returns `None` if `pat` and `ty` are in incompatible macro contexts (e.g. `pat` is a
2061+
/// metavariable from the call site while `ty` lives in the macro body), in which case no
2062+
/// suggestion is emitted.
2063+
pub(crate) fn eq_ctxt_suggestion_span(pat: Span, ty: Span) -> Option<Span> {
2064+
if let Some(ty2) = ty.find_ancestor_in_same_ctxt(pat)
2065+
&& pat.hi() <= ty2.lo()
2066+
{
2067+
return Some(pat.between(ty2));
2068+
}
2069+
if let Some(pat2) = pat.find_ancestor_in_same_ctxt(ty)
2070+
&& pat2.hi() <= ty.lo()
2071+
{
2072+
return Some(pat2.between(ty));
2073+
}
2074+
None
2075+
}

compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,9 @@ use tracing::{debug, instrument};
5656
use crate::check::check_abi;
5757
use crate::check_c_variadic_abi;
5858
use crate::diagnostics::{self, BadReturnTypeNotation, NoFieldOnType, NoVariantNamed};
59-
use crate::hir_ty_lowering::errors::{GenericsArgsErrExtend, prohibit_assoc_item_constraint};
59+
use crate::hir_ty_lowering::errors::{
60+
GenericsArgsErrExtend, eq_ctxt_suggestion_span, prohibit_assoc_item_constraint,
61+
};
6062
use crate::hir_ty_lowering::generics::{check_generic_arg_count, lower_generic_args};
6163
use crate::middle::resolve_bound_vars as rbv;
6264

@@ -3302,18 +3304,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
33023304
.next()
33033305
{
33043306
// `let x: S::new(valid_in_ty_ctxt);` -> `let x = S::new(valid_in_ty_ctxt);`
3305-
let err = tcx
3306-
.dcx()
3307-
.struct_span_err(
3308-
hir_ty.span,
3309-
"expected type, found associated function call",
3310-
)
3311-
.with_span_suggestion_verbose(
3312-
stmt.pat.span.between(hir_ty.span),
3307+
let mut err = tcx.dcx().struct_span_err(
3308+
hir_ty.span,
3309+
"expected type, found associated function call",
3310+
);
3311+
if let Some(between) = eq_ctxt_suggestion_span(stmt.pat.span, hir_ty.span) {
3312+
err.span_suggestion_verbose(
3313+
between,
33133314
"use `=` if you meant to assign",
3314-
" = ".to_string(),
3315+
" = ",
33153316
Applicability::MaybeIncorrect,
33163317
);
3318+
}
33173319
self.dcx().try_steal_replace_and_emit_err(
33183320
hir_ty.span,
33193321
StashKey::ReturnTypeNotation,
@@ -3328,18 +3330,18 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ {
33283330
{
33293331
// `let x: i32::something(valid_in_ty_ctxt);` -> `let x = i32::something(valid_in_ty_ctxt);`
33303332
// FIXME: Check that `something` is a valid function in `i32`.
3331-
let err = tcx
3332-
.dcx()
3333-
.struct_span_err(
3334-
hir_ty.span,
3335-
"expected type, found associated function call",
3336-
)
3337-
.with_span_suggestion_verbose(
3338-
stmt.pat.span.between(hir_ty.span),
3333+
let mut err = tcx.dcx().struct_span_err(
3334+
hir_ty.span,
3335+
"expected type, found associated function call",
3336+
);
3337+
if let Some(between) = eq_ctxt_suggestion_span(stmt.pat.span, hir_ty.span) {
3338+
err.span_suggestion_verbose(
3339+
between,
33393340
"use `=` if you meant to assign",
3340-
" = ".to_string(),
3341+
" = ",
33413342
Applicability::MaybeIncorrect,
33423343
);
3344+
}
33433345
self.dcx().try_steal_replace_and_emit_err(
33443346
hir_ty.span,
33453347
StashKey::ReturnTypeNotation,

tests/ui/suggestions/let-binding-init-expr-as-ty.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,17 @@ fn main() {
2828
//~^ ERROR return type notation is experimental
2929
let x: S::new(()); //~ ERROR expected type, found associated function call
3030

31+
// Macros — suggestion must point at user code, not the macro definition (#158492)
32+
let x: vec![]; //~ ERROR expected type, found associated function call
33+
34+
// When the `let` is inside a macro, no suggestion should be emitted at the call site
35+
macro_rules! make {
36+
($pat:pat) => {
37+
let $pat: Vec::new(); //~ ERROR expected type, found associated function call
38+
};
39+
}
40+
make!(_);
41+
3142
// Literals
3243
let x: 42; //~ ERROR expected type, found `42`
3344
let x: ""; //~ ERROR expected type, found `""`

tests/ui/suggestions/let-binding-init-expr-as-ty.stderr

Lines changed: 29 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error: expected type, found `42`
2-
--> $DIR/let-binding-init-expr-as-ty.rs:32:12
2+
--> $DIR/let-binding-init-expr-as-ty.rs:43:12
33
|
44
LL | let x: 42;
55
| - ^^ expected type
@@ -13,7 +13,7 @@ LL + let x = 42;
1313
|
1414

1515
error: expected type, found `""`
16-
--> $DIR/let-binding-init-expr-as-ty.rs:33:12
16+
--> $DIR/let-binding-init-expr-as-ty.rs:44:12
1717
|
1818
LL | let x: "";
1919
| - ^^ expected type
@@ -40,7 +40,7 @@ LL + let foo = i32::from_be(num);
4040
|
4141

4242
error[E0573]: cannot find type `bar` in this scope
43-
--> $DIR/let-binding-init-expr-as-ty.rs:36:12
43+
--> $DIR/let-binding-init-expr-as-ty.rs:47:12
4444
|
4545
LL | let x: bar();
4646
| ^^^ not found in this scope
@@ -53,15 +53,15 @@ LL + let x = bar();
5353
|
5454

5555
error[E0573]: cannot find type `bar` in this scope
56-
--> $DIR/let-binding-init-expr-as-ty.rs:37:12
56+
--> $DIR/let-binding-init-expr-as-ty.rs:48:12
5757
|
5858
LL | let x: bar;
5959
| ^^^ not found in this scope
6060
|
6161
= note: a function named `bar` exists in another namespace
6262

6363
error[E0573]: cannot find type `x` in this scope
64-
--> $DIR/let-binding-init-expr-as-ty.rs:40:12
64+
--> $DIR/let-binding-init-expr-as-ty.rs:51:12
6565
|
6666
LL | struct K(S::new(()));
6767
| --------------------- similarly named struct `K` defined here
@@ -158,7 +158,30 @@ LL - let x: S::new(());
158158
LL + let x = S::new(());
159159
|
160160

161-
error: aborting due to 13 previous errors
161+
error: expected type, found associated function call
162+
--> $DIR/let-binding-init-expr-as-ty.rs:32:12
163+
|
164+
LL | let x: vec![];
165+
| ^^^^^^
166+
|
167+
help: use `=` if you meant to assign
168+
|
169+
LL - let x: vec![];
170+
LL + let x = vec![];
171+
|
172+
173+
error: expected type, found associated function call
174+
--> $DIR/let-binding-init-expr-as-ty.rs:37:23
175+
|
176+
LL | let $pat: Vec::new();
177+
| ^^^^^^^^^^
178+
...
179+
LL | make!(_);
180+
| -------- in this macro invocation
181+
|
182+
= note: this error originates in the macro `make` (in Nightly builds, run with -Z macro-backtrace for more info)
183+
184+
error: aborting due to 15 previous errors
162185

163186
Some errors have detailed explanations: E0573, E0658.
164187
For more information about an error, try `rustc --explain E0573`.

0 commit comments

Comments
 (0)