Skip to content

Commit a64de0d

Browse files
authored
Unrolled build for rust-lang#136344
Rollup merge of rust-lang#136344 - zachs18:dot_notation_more_defkinds_3, r=davidtwco Suggest replacing `.` with `::` in more error diagnostics. First commit makes the existing "help: use the path separator to refer to an item" also work when the base is a type alias, not just a trait/module/struct. The existing unconditional `DefKind::Mod | DefKind::Trait` match arm is changed to a conditional `DefKind::Mod | DefKind::Trait | DefKind::TyAlias` arm that only matches if the `path_sep` suggestion-adding closure succeeds, so as not to stop the later `DefKind::TyAlias`-specific suggestions if the path-sep suggestion does not apply. This shouldn't change behavior for `Mod` or `Trait` (due to the default arm's `return false` etc). This commit also updates `tests/ui/resolve/issue-22692.rs` to reflect this, and also renames it to something more meaningful. This commit also makes the `bad_struct_syntax_suggestion` closure take `err` as a parameter instead of capturing it, since otherwise caused borrowing errors due to the change to using `path_sep` in a pattern guard. <details> <summary> Type alias diagnostic example </summary> ```rust type S = String; fn main() { let _ = S.new; } ``` ```diff error[E0423]: expected value, found type alias `S` --> diag7.rs:4:13 | 4 | let _ = S.new; | ^ | - = note: can't use a type alias as a constructor + help: use the path separator to refer to an item + | +4 | let _ = S::new; + | ~~ ``` </details> Second commit adds some cases for `enum`s, where if there is a field/method expression where the field/method has the name of a unit/tuple variant, we assume the user intended to create that variant[^1] and suggest replacing the `.` from the field/method suggestion with a `::` path separator. If no such variant is found (or if the error is not a field/method expression), we give the existing suggestion that suggests adding `::TupleVariant(/* fields */)` after the enum. <details> <summary> Enum diagnostic example </summary> ```rust enum Foo { A(u32), B, C { x: u32 }, } fn main() { let _ = Foo.A(42); // changed let _ = Foo.B; // changed let _ = Foo.D(42); // no change let _ = Foo.D; // no change let _ = Foo(42); // no change } ``` ```diff error[E0423]: expected value, found enum `Foo` --> diag8.rs:8:13 | 8 | let _ = Foo.A(42); // changed | ^^^ | note: the enum is defined here --> diag8.rs:1:1 | 1 | / enum Foo { 2 | | A(u32), 3 | | B, 4 | | C { x: u32 }, 5 | | } | |_^ -help: you might have meant to use the following enum variant - | -8 | let _ = Foo::B.A(42); // changed - | ~~~~~~ -help: alternatively, the following enum variant is available +help: use the path separator to refer to a variant | -8 | let _ = (Foo::A(/* fields */)).A(42); // changed - | ~~~~~~~~~~~~~~~~~~~~~~ +8 | let _ = Foo::A(42); // changed + | ~~ error[E0423]: expected value, found enum `Foo` --> diag8.rs:9:13 | 9 | let _ = Foo.B; // changed | ^^^ | note: the enum is defined here --> diag8.rs:1:1 | 1 | / enum Foo { 2 | | A(u32), 3 | | B, 4 | | C { x: u32 }, 5 | | } | |_^ -help: you might have meant to use the following enum variant - | -9 | let _ = Foo::B.B; // changed - | ~~~~~~ -help: alternatively, the following enum variant is available +help: use the path separator to refer to a variant | -9 | let _ = (Foo::A(/* fields */)).B; // changed - | ~~~~~~~~~~~~~~~~~~~~~~ +9 | let _ = Foo::B; // changed + | ~~ error[E0423]: expected value, found enum `Foo` --> diag8.rs:10:13 | 10 | let _ = Foo.D(42); // no change | ^^^ | note: the enum is defined here --> diag8.rs:1:1 | 1 | / enum Foo { 2 | | A(u32), 3 | | B, 4 | | C { x: u32 }, 5 | | } | |_^ help: you might have meant to use the following enum variant | 10 | let _ = Foo::B.D(42); // no change | ~~~~~~ help: alternatively, the following enum variant is available | 10 | let _ = (Foo::A(/* fields */)).D(42); // no change | ~~~~~~~~~~~~~~~~~~~~~~ error[E0423]: expected value, found enum `Foo` --> diag8.rs:11:13 | 11 | let _ = Foo.D; // no change | ^^^ | note: the enum is defined here --> diag8.rs:1:1 | 1 | / enum Foo { 2 | | A(u32), 3 | | B, 4 | | C { x: u32 }, 5 | | } | |_^ help: you might have meant to use the following enum variant | 11 | let _ = Foo::B.D; // no change | ~~~~~~ help: alternatively, the following enum variant is available | 11 | let _ = (Foo::A(/* fields */)).D; // no change | ~~~~~~~~~~~~~~~~~~~~~~ error[E0423]: expected function, tuple struct or tuple variant, found enum `Foo` --> diag8.rs:12:13 | 12 | let _ = Foo(42); // no change | ^^^ help: try to construct one of the enum's variants: `Foo::A` | = help: you might have meant to construct the enum's non-tuple variant note: the enum is defined here --> diag8.rs:1:1 | 1 | / enum Foo { 2 | | A(u32), 3 | | B, 4 | | C { x: u32 }, 5 | | } | |_^ error: aborting due to 5 previous errors ``` </details> [^1]: or if it's a field expression and a tuple variant, that they meant to refer the variant constructor.
2 parents f280acf + 2c37250 commit a64de0d

8 files changed

+730
-204
lines changed

compiler/rustc_resolve/src/late/diagnostics.rs

+64-24
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_ast::ptr::P;
88
use rustc_ast::visit::{FnCtxt, FnKind, LifetimeCtxt, Visitor, walk_ty};
99
use rustc_ast::{
1010
self as ast, AssocItemKind, DUMMY_NODE_ID, Expr, ExprKind, GenericParam, GenericParamKind,
11-
Item, ItemKind, MethodCall, NodeId, Path, Ty, TyKind,
11+
Item, ItemKind, MethodCall, NodeId, Path, PathSegment, Ty, TyKind,
1212
};
1313
use rustc_ast_pretty::pprust::where_bound_predicate_to_string;
1414
use rustc_data_structures::fx::{FxHashSet, FxIndexSet};
@@ -1529,7 +1529,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
15291529
Applicability::MaybeIncorrect,
15301530
);
15311531
true
1532-
} else if kind == DefKind::Struct
1532+
} else if matches!(kind, DefKind::Struct | DefKind::TyAlias)
15331533
&& let Some(lhs_source_span) = lhs_span.find_ancestor_inside(expr.span)
15341534
&& let Ok(snippet) = this.r.tcx.sess.source_map().span_to_snippet(lhs_source_span)
15351535
{
@@ -1566,7 +1566,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
15661566
}
15671567
};
15681568

1569-
let mut bad_struct_syntax_suggestion = |this: &mut Self, def_id: DefId| {
1569+
let bad_struct_syntax_suggestion = |this: &mut Self, err: &mut Diag<'_>, def_id: DefId| {
15701570
let (followed_by_brace, closing_brace) = this.followed_by_brace(span);
15711571

15721572
match source {
@@ -1740,12 +1740,10 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
17401740
}
17411741
}
17421742
(
1743-
Res::Def(kind @ (DefKind::Mod | DefKind::Trait), _),
1743+
Res::Def(kind @ (DefKind::Mod | DefKind::Trait | DefKind::TyAlias), _),
17441744
PathSource::Expr(Some(parent)),
1745-
) => {
1746-
if !path_sep(self, err, parent, kind) {
1747-
return false;
1748-
}
1745+
) if path_sep(self, err, parent, kind) => {
1746+
return true;
17491747
}
17501748
(
17511749
Res::Def(DefKind::Enum, def_id),
@@ -1777,13 +1775,13 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
17771775
let (ctor_def, ctor_vis, fields) = if let Some(struct_ctor) = struct_ctor {
17781776
if let PathSource::Expr(Some(parent)) = source {
17791777
if let ExprKind::Field(..) | ExprKind::MethodCall(..) = parent.kind {
1780-
bad_struct_syntax_suggestion(self, def_id);
1778+
bad_struct_syntax_suggestion(self, err, def_id);
17811779
return true;
17821780
}
17831781
}
17841782
struct_ctor
17851783
} else {
1786-
bad_struct_syntax_suggestion(self, def_id);
1784+
bad_struct_syntax_suggestion(self, err, def_id);
17871785
return true;
17881786
};
17891787

@@ -1861,7 +1859,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
18611859
err.span_label(span, "constructor is not visible here due to private fields");
18621860
}
18631861
(Res::Def(DefKind::Union | DefKind::Variant, def_id), _) if ns == ValueNS => {
1864-
bad_struct_syntax_suggestion(self, def_id);
1862+
bad_struct_syntax_suggestion(self, err, def_id);
18651863
}
18661864
(Res::Def(DefKind::Ctor(_, CtorKind::Const), def_id), _) if ns == ValueNS => {
18671865
match source {
@@ -2471,31 +2469,73 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
24712469
def_id: DefId,
24722470
span: Span,
24732471
) {
2474-
let Some(variants) = self.collect_enum_ctors(def_id) else {
2472+
let Some(variant_ctors) = self.collect_enum_ctors(def_id) else {
24752473
err.note("you might have meant to use one of the enum's variants");
24762474
return;
24772475
};
24782476

2479-
let suggest_only_tuple_variants =
2480-
matches!(source, PathSource::TupleStruct(..)) || source.is_call();
2481-
if suggest_only_tuple_variants {
2477+
// If the expression is a field-access or method-call, try to find a variant with the field/method name
2478+
// that could have been intended, and suggest replacing the `.` with `::`.
2479+
// Otherwise, suggest adding `::VariantName` after the enum;
2480+
// and if the expression is call-like, only suggest tuple variants.
2481+
let (suggest_path_sep_dot_span, suggest_only_tuple_variants) = match source {
2482+
// `Type(a, b)` in a pattern, only suggest adding a tuple variant after `Type`.
2483+
PathSource::TupleStruct(..) => (None, true),
2484+
PathSource::Expr(Some(expr)) => match &expr.kind {
2485+
// `Type(a, b)`, only suggest adding a tuple variant after `Type`.
2486+
ExprKind::Call(..) => (None, true),
2487+
// `Type.Foo(a, b)`, suggest replacing `.` -> `::` if variant `Foo` exists and is a tuple variant,
2488+
// otherwise suggest adding a variant after `Type`.
2489+
ExprKind::MethodCall(box MethodCall {
2490+
receiver,
2491+
span,
2492+
seg: PathSegment { ident, .. },
2493+
..
2494+
}) => {
2495+
let dot_span = receiver.span.between(*span);
2496+
let found_tuple_variant = variant_ctors.iter().any(|(path, _, ctor_kind)| {
2497+
*ctor_kind == CtorKind::Fn
2498+
&& path.segments.last().is_some_and(|seg| seg.ident == *ident)
2499+
});
2500+
(found_tuple_variant.then_some(dot_span), false)
2501+
}
2502+
// `Type.Foo`, suggest replacing `.` -> `::` if variant `Foo` exists and is a unit or tuple variant,
2503+
// otherwise suggest adding a variant after `Type`.
2504+
ExprKind::Field(base, ident) => {
2505+
let dot_span = base.span.between(ident.span);
2506+
let found_tuple_or_unit_variant = variant_ctors.iter().any(|(path, ..)| {
2507+
path.segments.last().is_some_and(|seg| seg.ident == *ident)
2508+
});
2509+
(found_tuple_or_unit_variant.then_some(dot_span), false)
2510+
}
2511+
_ => (None, false),
2512+
},
2513+
_ => (None, false),
2514+
};
2515+
2516+
if let Some(dot_span) = suggest_path_sep_dot_span {
2517+
err.span_suggestion_verbose(
2518+
dot_span,
2519+
"use the path separator to refer to a variant",
2520+
"::",
2521+
Applicability::MaybeIncorrect,
2522+
);
2523+
} else if suggest_only_tuple_variants {
24822524
// Suggest only tuple variants regardless of whether they have fields and do not
24832525
// suggest path with added parentheses.
2484-
let mut suggestable_variants = variants
2526+
let mut suggestable_variants = variant_ctors
24852527
.iter()
24862528
.filter(|(.., kind)| *kind == CtorKind::Fn)
24872529
.map(|(variant, ..)| path_names_to_string(variant))
24882530
.collect::<Vec<_>>();
24892531
suggestable_variants.sort();
24902532

2491-
let non_suggestable_variant_count = variants.len() - suggestable_variants.len();
2533+
let non_suggestable_variant_count = variant_ctors.len() - suggestable_variants.len();
24922534

2493-
let source_msg = if source.is_call() {
2494-
"to construct"
2495-
} else if matches!(source, PathSource::TupleStruct(..)) {
2535+
let source_msg = if matches!(source, PathSource::TupleStruct(..)) {
24962536
"to match against"
24972537
} else {
2498-
unreachable!()
2538+
"to construct"
24992539
};
25002540

25012541
if !suggestable_variants.is_empty() {
@@ -2514,7 +2554,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
25142554
}
25152555

25162556
// If the enum has no tuple variants..
2517-
if non_suggestable_variant_count == variants.len() {
2557+
if non_suggestable_variant_count == variant_ctors.len() {
25182558
err.help(format!("the enum has no tuple variants {source_msg}"));
25192559
}
25202560

@@ -2537,7 +2577,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
25372577
}
25382578
};
25392579

2540-
let mut suggestable_variants = variants
2580+
let mut suggestable_variants = variant_ctors
25412581
.iter()
25422582
.filter(|(_, def_id, kind)| !needs_placeholder(*def_id, *kind))
25432583
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))
@@ -2564,7 +2604,7 @@ impl<'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> {
25642604
);
25652605
}
25662606

2567-
let mut suggestable_variants_with_placeholders = variants
2607+
let mut suggestable_variants_with_placeholders = variant_ctors
25682608
.iter()
25692609
.filter(|(_, def_id, kind)| needs_placeholder(*def_id, *kind))
25702610
.map(|(variant, _, kind)| (path_names_to_string(variant), kind))

src/tools/tidy/src/issues.txt

-1
Original file line numberDiff line numberDiff line change
@@ -3619,7 +3619,6 @@ ui/resolve/issue-21221-1.rs
36193619
ui/resolve/issue-21221-2.rs
36203620
ui/resolve/issue-21221-3.rs
36213621
ui/resolve/issue-21221-4.rs
3622-
ui/resolve/issue-22692.rs
36233622
ui/resolve/issue-2330.rs
36243623
ui/resolve/issue-23305.rs
36253624
ui/resolve/issue-2356.rs
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// see also https://github.com/rust-lang/rust/issues/22692
2+
3+
type Alias = Vec<u32>;
4+
5+
mod foo {
6+
fn bar() {}
7+
}
8+
9+
fn main() {
10+
let _ = String.new();
11+
//~^ ERROR expected value, found struct `String`
12+
//~| HELP use the path separator
13+
14+
let _ = String.default;
15+
//~^ ERROR expected value, found struct `String`
16+
//~| HELP use the path separator
17+
18+
let _ = Vec::<()>.with_capacity(1);
19+
//~^ ERROR expected value, found struct `Vec`
20+
//~| HELP use the path separator
21+
22+
let _ = Alias.new();
23+
//~^ ERROR expected value, found type alias `Alias`
24+
//~| HELP use the path separator
25+
26+
let _ = Alias.default;
27+
//~^ ERROR expected value, found type alias `Alias`
28+
//~| HELP use the path separator
29+
30+
let _ = foo.bar;
31+
//~^ ERROR expected value, found module `foo`
32+
//~| HELP use the path separator
33+
}
34+
35+
macro_rules! Type {
36+
() => {
37+
::std::cell::Cell
38+
//~^ ERROR expected value, found struct `std::cell::Cell`
39+
//~| ERROR expected value, found struct `std::cell::Cell`
40+
//~| ERROR expected value, found struct `std::cell::Cell`
41+
};
42+
(alias) => {
43+
Alias
44+
//~^ ERROR expected value, found type alias `Alias`
45+
//~| ERROR expected value, found type alias `Alias`
46+
//~| ERROR expected value, found type alias `Alias`
47+
};
48+
}
49+
50+
macro_rules! create {
51+
(type method) => {
52+
Vec.new()
53+
//~^ ERROR expected value, found struct `Vec`
54+
//~| HELP use the path separator
55+
};
56+
(type field) => {
57+
Vec.new
58+
//~^ ERROR expected value, found struct `Vec`
59+
//~| HELP use the path separator
60+
};
61+
(macro method) => {
62+
Type!().new(0)
63+
//~^ HELP use the path separator
64+
};
65+
(macro method alias) => {
66+
Type!(alias).new(0)
67+
//~^ HELP use the path separator
68+
};
69+
}
70+
71+
macro_rules! check_ty {
72+
($Ty:ident) => {
73+
$Ty.foo
74+
//~^ ERROR expected value, found type alias `Alias`
75+
//~| HELP use the path separator
76+
};
77+
}
78+
macro_rules! check_ident {
79+
($Ident:ident) => {
80+
Alias.$Ident
81+
//~^ ERROR expected value, found type alias `Alias`
82+
//~| HELP use the path separator
83+
};
84+
}
85+
macro_rules! check_ty_ident {
86+
($Ty:ident, $Ident:ident) => {
87+
$Ty.$Ident
88+
//~^ ERROR expected value, found type alias `Alias`
89+
//~| HELP use the path separator
90+
};
91+
}
92+
93+
fn interaction_with_macros() {
94+
//
95+
// Verify that we do not only suggest to replace `.` with `::` if the receiver is a
96+
// macro call but that we also correctly suggest to surround it with angle brackets.
97+
//
98+
99+
Type!().get();
100+
//~^ HELP use the path separator
101+
102+
Type! {}.get;
103+
//~^ HELP use the path separator
104+
105+
Type!(alias).get();
106+
//~^ HELP use the path separator
107+
108+
Type! {alias}.get;
109+
//~^ HELP use the path separator
110+
111+
//
112+
// Ensure that the suggestion is shown for expressions inside of macro definitions.
113+
//
114+
115+
let _ = create!(type method);
116+
let _ = create!(type field);
117+
let _ = create!(macro method);
118+
let _ = create!(macro method alias);
119+
120+
let _ = check_ty!(Alias);
121+
let _ = check_ident!(foo);
122+
let _ = check_ty_ident!(Alias, foo);
123+
}

0 commit comments

Comments
 (0)