Skip to content

Commit 7699462

Browse files
authored
Rollup merge of #108930 - Ezrashaw:better-error-for-manual-fn-impl, r=petrochenkov
feat: implement better error for manual impl of `Fn*` traits Fixes #39259 cc `@estebank` (you gave me some advice in the linked issue, would you like to review?)
2 parents 4ad3230 + a30c2c2 commit 7699462

File tree

10 files changed

+142
-51
lines changed

10 files changed

+142
-51
lines changed

Diff for: compiler/rustc_hir_analysis/locales/en-US.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@ hir_analysis_assoc_type_binding_not_allowed =
4242
associated type bindings are not allowed here
4343
.label = associated type not allowed here
4444
45+
hir_analysis_parenthesized_fn_trait_expansion =
46+
parenthesized trait syntax expands to `{$expanded_type}`
47+
4548
hir_analysis_typeof_reserved_keyword_used =
4649
`typeof` is a reserved keyword but unimplemented
4750
.suggestion = consider replacing `typeof(...)` with an actual type

Diff for: compiler/rustc_hir_analysis/src/astconv/errors.rs

+72-35
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
use crate::astconv::AstConv;
2-
use crate::errors::{ManualImplementation, MissingTypeParams};
2+
use crate::errors::{
3+
AssocTypeBindingNotAllowed, ManualImplementation, MissingTypeParams,
4+
ParenthesizedFnTraitExpansion,
5+
};
36
use rustc_data_structures::fx::FxHashMap;
47
use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
58
use rustc_hir as hir;
69
use rustc_hir::def_id::DefId;
710
use rustc_infer::traits::FulfillmentError;
11+
use rustc_middle::ty::TyCtxt;
812
use rustc_middle::ty::{self, Ty};
913
use rustc_session::parse::feature_err;
1014
use rustc_span::edit_distance::find_best_match_for_name;
@@ -78,43 +82,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
7882
// Do not suggest the other syntax if we are in trait impl:
7983
// the desugaring would contain an associated type constraint.
8084
if !is_impl {
81-
let args = trait_segment
82-
.args
83-
.as_ref()
84-
.and_then(|args| args.args.get(0))
85-
.and_then(|arg| match arg {
86-
hir::GenericArg::Type(ty) => match ty.kind {
87-
hir::TyKind::Tup(t) => t
88-
.iter()
89-
.map(|e| sess.source_map().span_to_snippet(e.span))
90-
.collect::<Result<Vec<_>, _>>()
91-
.map(|a| a.join(", ")),
92-
_ => sess.source_map().span_to_snippet(ty.span),
93-
}
94-
.map(|s| format!("({})", s))
95-
.ok(),
96-
_ => None,
97-
})
98-
.unwrap_or_else(|| "()".to_string());
99-
let ret = trait_segment
100-
.args()
101-
.bindings
102-
.iter()
103-
.find_map(|b| match (b.ident.name == sym::Output, &b.kind) {
104-
(true, hir::TypeBindingKind::Equality { term }) => {
105-
let span = match term {
106-
hir::Term::Ty(ty) => ty.span,
107-
hir::Term::Const(c) => self.tcx().hir().span(c.hir_id),
108-
};
109-
sess.source_map().span_to_snippet(span).ok()
110-
}
111-
_ => None,
112-
})
113-
.unwrap_or_else(|| "()".to_string());
11485
err.span_suggestion(
11586
span,
11687
"use parenthetical notation instead",
117-
format!("{}{} -> {}", trait_segment.ident, args, ret),
88+
fn_trait_to_string(self.tcx(), trait_segment, true),
11889
Applicability::MaybeIncorrect,
11990
);
12091
}
@@ -629,3 +600,69 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
629600
err.emit();
630601
}
631602
}
603+
604+
/// Emits an error regarding forbidden type binding associations
605+
pub fn prohibit_assoc_ty_binding(
606+
tcx: TyCtxt<'_>,
607+
span: Span,
608+
segment: Option<(&hir::PathSegment<'_>, Span)>,
609+
) {
610+
tcx.sess.emit_err(AssocTypeBindingNotAllowed { span, fn_trait_expansion: if let Some((segment, span)) = segment && segment.args().parenthesized {
611+
Some(ParenthesizedFnTraitExpansion { span, expanded_type: fn_trait_to_string(tcx, segment, false) })
612+
} else {
613+
None
614+
}});
615+
}
616+
617+
pub(crate) fn fn_trait_to_string(
618+
tcx: TyCtxt<'_>,
619+
trait_segment: &hir::PathSegment<'_>,
620+
parenthesized: bool,
621+
) -> String {
622+
let args = trait_segment
623+
.args
624+
.as_ref()
625+
.and_then(|args| args.args.get(0))
626+
.and_then(|arg| match arg {
627+
hir::GenericArg::Type(ty) => match ty.kind {
628+
hir::TyKind::Tup(t) => t
629+
.iter()
630+
.map(|e| tcx.sess.source_map().span_to_snippet(e.span))
631+
.collect::<Result<Vec<_>, _>>()
632+
.map(|a| a.join(", ")),
633+
_ => tcx.sess.source_map().span_to_snippet(ty.span),
634+
}
635+
.map(|s| {
636+
// `s.empty()` checks to see if the type is the unit tuple, if so we don't want a comma
637+
if parenthesized || s.is_empty() { format!("({})", s) } else { format!("({},)", s) }
638+
})
639+
.ok(),
640+
_ => None,
641+
})
642+
.unwrap_or_else(|| "()".to_string());
643+
644+
let ret = trait_segment
645+
.args()
646+
.bindings
647+
.iter()
648+
.find_map(|b| match (b.ident.name == sym::Output, &b.kind) {
649+
(true, hir::TypeBindingKind::Equality { term }) => {
650+
let span = match term {
651+
hir::Term::Ty(ty) => ty.span,
652+
hir::Term::Const(c) => tcx.hir().span(c.hir_id),
653+
};
654+
655+
(span != tcx.hir().span(trait_segment.hir_id))
656+
.then_some(tcx.sess.source_map().span_to_snippet(span).ok())
657+
.flatten()
658+
}
659+
_ => None,
660+
})
661+
.unwrap_or_else(|| "()".to_string());
662+
663+
if parenthesized {
664+
format!("{}{} -> {}", trait_segment.ident, args, ret)
665+
} else {
666+
format!("{}<{}, Output={}>", trait_segment.ident, args, ret)
667+
}
668+
}

Diff for: compiler/rustc_hir_analysis/src/astconv/generics.rs

+3-9
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,8 @@
11
use super::IsMethodCall;
22
use crate::astconv::{
3-
CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
4-
GenericArgCountResult, GenericArgPosition,
3+
errors::prohibit_assoc_ty_binding, CreateSubstsForGenericArgsCtxt, ExplicitLateBound,
4+
GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition,
55
};
6-
use crate::errors::AssocTypeBindingNotAllowed;
76
use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs};
87
use rustc_ast::ast::ParamKindOrd;
98
use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed, MultiSpan};
@@ -433,7 +432,7 @@ pub(crate) fn check_generic_arg_count(
433432
(gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params();
434433

435434
if gen_pos != GenericArgPosition::Type && let Some(b) = gen_args.bindings.first() {
436-
prohibit_assoc_ty_binding(tcx, b.span);
435+
prohibit_assoc_ty_binding(tcx, b.span, None);
437436
}
438437

439438
let explicit_late_bound =
@@ -589,11 +588,6 @@ pub(crate) fn check_generic_arg_count(
589588
}
590589
}
591590

592-
/// Emits an error regarding forbidden type binding associations
593-
pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) {
594-
tcx.sess.emit_err(AssocTypeBindingNotAllowed { span });
595-
}
596-
597591
/// Prohibits explicit lifetime arguments if late-bound lifetime parameters
598592
/// are present. This is used both for datatypes and function calls.
599593
pub(crate) fn prohibit_explicit_late_bound_lifetimes(

Diff for: compiler/rustc_hir_analysis/src/astconv/mod.rs

+6-7
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,8 @@
55
mod errors;
66
pub mod generics;
77

8-
use crate::astconv::generics::{
9-
check_generic_arg_count, create_substs_for_generic_args, prohibit_assoc_ty_binding,
10-
};
8+
use crate::astconv::errors::prohibit_assoc_ty_binding;
9+
use crate::astconv::generics::{check_generic_arg_count, create_substs_for_generic_args};
1110
use crate::bounds::Bounds;
1211
use crate::collect::HirPlaceholderCollector;
1312
use crate::errors::{
@@ -295,7 +294,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
295294
ty::BoundConstness::NotConst,
296295
);
297296
if let Some(b) = item_segment.args().bindings.first() {
298-
prohibit_assoc_ty_binding(self.tcx(), b.span);
297+
prohibit_assoc_ty_binding(self.tcx(), b.span, Some((item_segment, span)));
299298
}
300299

301300
substs
@@ -631,7 +630,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
631630
);
632631

633632
if let Some(b) = item_segment.args().bindings.first() {
634-
prohibit_assoc_ty_binding(self.tcx(), b.span);
633+
prohibit_assoc_ty_binding(self.tcx(), b.span, Some((item_segment, span)));
635634
}
636635

637636
args
@@ -825,7 +824,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
825824
constness,
826825
);
827826
if let Some(b) = trait_segment.args().bindings.first() {
828-
prohibit_assoc_ty_binding(self.tcx(), b.span);
827+
prohibit_assoc_ty_binding(self.tcx(), b.span, Some((trait_segment, span)));
829828
}
830829
self.tcx().mk_trait_ref(trait_def_id, substs)
831830
}
@@ -2596,7 +2595,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
25962595
for segment in segments {
25972596
// Only emit the first error to avoid overloading the user with error messages.
25982597
if let Some(b) = segment.args().bindings.first() {
2599-
prohibit_assoc_ty_binding(self.tcx(), b.span);
2598+
prohibit_assoc_ty_binding(self.tcx(), b.span, None);
26002599
return true;
26012600
}
26022601
}

Diff for: compiler/rustc_hir_analysis/src/errors.rs

+12
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,18 @@ pub struct AssocTypeBindingNotAllowed {
129129
#[primary_span]
130130
#[label]
131131
pub span: Span,
132+
133+
#[subdiagnostic]
134+
pub fn_trait_expansion: Option<ParenthesizedFnTraitExpansion>,
135+
}
136+
137+
#[derive(Subdiagnostic)]
138+
#[help(hir_analysis_parenthesized_fn_trait_expansion)]
139+
pub struct ParenthesizedFnTraitExpansion {
140+
#[primary_span]
141+
pub span: Span,
142+
143+
pub expanded_type: String,
132144
}
133145

134146
#[derive(Diagnostic)]

Diff for: tests/ui/feature-gates/feature-gate-unboxed-closures-manual-impls.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ error[E0229]: associated type bindings are not allowed here
6464
|
6565
LL | impl FnOnce() for Foo1 {
6666
| ^^^^^^^^ associated type not allowed here
67+
|
68+
help: parenthesized trait syntax expands to `FnOnce<(), Output=()>`
69+
--> $DIR/feature-gate-unboxed-closures-manual-impls.rs:16:6
70+
|
71+
LL | impl FnOnce() for Foo1 {
72+
| ^^^^^^^^
6773

6874
error[E0658]: the precise format of `Fn`-family traits' type parameters is subject to change
6975
--> $DIR/feature-gate-unboxed-closures-manual-impls.rs:23:6

Diff for: tests/ui/fn/issue-39259.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![feature(fn_traits)]
2+
#![feature(unboxed_closures)]
3+
4+
struct S;
5+
6+
impl Fn(u32) -> u32 for S {
7+
//~^ ERROR associated type bindings are not allowed here [E0229]
8+
fn call(&self) -> u32 {
9+
5
10+
}
11+
}
12+
13+
fn main() {}

Diff for: tests/ui/fn/issue-39259.stderr

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0229]: associated type bindings are not allowed here
2+
--> $DIR/issue-39259.rs:6:17
3+
|
4+
LL | impl Fn(u32) -> u32 for S {
5+
| ^^^ associated type not allowed here
6+
|
7+
help: parenthesized trait syntax expands to `Fn<(u32,), Output=u32>`
8+
--> $DIR/issue-39259.rs:6:6
9+
|
10+
LL | impl Fn(u32) -> u32 for S {
11+
| ^^^^^^^^^^^^^^
12+
13+
error: aborting due to previous error
14+
15+
For more information about this error, try `rustc --explain E0229`.

Diff for: tests/ui/lifetimes/issue-95023.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,12 @@ error[E0229]: associated type bindings are not allowed here
2525
|
2626
LL | impl Fn(&isize) for Error {
2727
| ^^^^^^^^^^ associated type not allowed here
28+
|
29+
help: parenthesized trait syntax expands to `Fn<(&isize,), Output=()>`
30+
--> $DIR/issue-95023.rs:3:6
31+
|
32+
LL | impl Fn(&isize) for Error {
33+
| ^^^^^^^^^^
2834

2935
error[E0220]: associated type `B` not found for `Self`
3036
--> $DIR/issue-95023.rs:6:44

Diff for: tests/ui/traits/issue-87558.stderr

+6
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,12 @@ error[E0229]: associated type bindings are not allowed here
1717
|
1818
LL | impl Fn(&isize) for Error {
1919
| ^^^^^^^^^^ associated type not allowed here
20+
|
21+
help: parenthesized trait syntax expands to `Fn<(&isize,), Output=()>`
22+
--> $DIR/issue-87558.rs:3:6
23+
|
24+
LL | impl Fn(&isize) for Error {
25+
| ^^^^^^^^^^
2026

2127
error: aborting due to 3 previous errors
2228

0 commit comments

Comments
 (0)