Skip to content

Commit 70468af

Browse files
authored
Rollup merge of #106200 - compiler-errors:suggest-impl-trait, r=estebank
Suggest `impl Fn*` and `impl Future` in `-> _` return suggestions Follow-up to #106172, only the last commit is relevant. Can rebase once that PR is landed for easier review. Suggests `impl Future` and `impl Fn{,Mut,Once}` in `-> _` return suggestions. r? `@estebank`
2 parents c361616 + 89086f7 commit 70468af

File tree

9 files changed

+220
-55
lines changed

9 files changed

+220
-55
lines changed

compiler/rustc_hir_analysis/src/collect.rs

+97-50
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@
1717
use crate::astconv::AstConv;
1818
use crate::check::intrinsic::intrinsic_operation_unsafety;
1919
use crate::errors;
20+
use hir::def::DefKind;
2021
use rustc_data_structures::captures::Captures;
2122
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
2223
use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, StashKey};
2324
use rustc_hir as hir;
2425
use rustc_hir::def_id::{DefId, LocalDefId};
2526
use rustc_hir::intravisit::{self, Visitor};
2627
use rustc_hir::{GenericParamKind, Node};
27-
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
2828
use rustc_infer::infer::TyCtxtInferExt;
29+
use rustc_infer::traits::ObligationCause;
2930
use rustc_middle::hir::nested_filter;
3031
use rustc_middle::ty::query::Providers;
3132
use rustc_middle::ty::util::{Discr, IntTypeExt};
@@ -1195,12 +1196,11 @@ fn infer_return_ty_for_fn_sig<'tcx>(
11951196
ty::ReErased => tcx.lifetimes.re_static,
11961197
_ => r,
11971198
});
1198-
let fn_sig = ty::Binder::dummy(fn_sig);
11991199

12001200
let mut visitor = HirPlaceholderCollector::default();
12011201
visitor.visit_ty(ty);
12021202
let mut diag = bad_placeholder(tcx, visitor.0, "return type");
1203-
let ret_ty = fn_sig.skip_binder().output();
1203+
let ret_ty = fn_sig.output();
12041204
if ret_ty.is_suggestable(tcx, false) {
12051205
diag.span_suggestion(
12061206
ty.span,
@@ -1223,26 +1223,26 @@ fn infer_return_ty_for_fn_sig<'tcx>(
12231223
Applicability::MachineApplicable,
12241224
);
12251225
}
1226+
} else if let Some(sugg) = suggest_impl_trait(tcx, ret_ty, ty.span, hir_id, def_id) {
1227+
diag.span_suggestion(
1228+
ty.span,
1229+
"replace with an appropriate return type",
1230+
sugg,
1231+
Applicability::MachineApplicable,
1232+
);
12261233
} else if ret_ty.is_closure() {
1227-
// We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds
1228-
// to prevent the user from getting a papercut while trying to use the unique closure
1229-
// syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`).
12301234
diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
1235+
}
1236+
// Also note how `Fn` traits work just in case!
1237+
if ret_ty.is_closure() {
12311238
diag.note(
12321239
"for more information on `Fn` traits and closure types, see \
12331240
https://doc.rust-lang.org/book/ch13-01-closures.html",
12341241
);
1235-
} else if let Some(i_ty) = suggest_impl_iterator(tcx, ret_ty, ty.span, hir_id, def_id) {
1236-
diag.span_suggestion(
1237-
ty.span,
1238-
"replace with an appropriate return type",
1239-
format!("impl Iterator<Item = {}>", i_ty),
1240-
Applicability::MachineApplicable,
1241-
);
12421242
}
12431243
diag.emit();
12441244

1245-
fn_sig
1245+
ty::Binder::dummy(fn_sig)
12461246
}
12471247
None => <dyn AstConv<'_>>::ty_of_fn(
12481248
icx,
@@ -1256,47 +1256,94 @@ fn infer_return_ty_for_fn_sig<'tcx>(
12561256
}
12571257
}
12581258

1259-
fn suggest_impl_iterator<'tcx>(
1259+
fn suggest_impl_trait<'tcx>(
12601260
tcx: TyCtxt<'tcx>,
12611261
ret_ty: Ty<'tcx>,
12621262
span: Span,
12631263
hir_id: hir::HirId,
12641264
def_id: LocalDefId,
1265-
) -> Option<Ty<'tcx>> {
1266-
let Some(iter_trait) = tcx.get_diagnostic_item(sym::Iterator) else { return None; };
1267-
let Some(iterator_item) = tcx.get_diagnostic_item(sym::IteratorItem) else { return None; };
1268-
if !tcx
1269-
.infer_ctxt()
1270-
.build()
1271-
.type_implements_trait(iter_trait, [ret_ty], tcx.param_env(def_id))
1272-
.must_apply_modulo_regions()
1273-
{
1274-
return None;
1275-
}
1276-
let infcx = tcx.infer_ctxt().build();
1277-
let ocx = ObligationCtxt::new_in_snapshot(&infcx);
1278-
// Find the type of `Iterator::Item`.
1279-
let origin = TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span };
1280-
let ty_var = infcx.next_ty_var(origin);
1281-
let projection = ty::Binder::dummy(ty::PredicateKind::Clause(ty::Clause::Projection(
1282-
ty::ProjectionPredicate {
1283-
projection_ty: tcx.mk_alias_ty(iterator_item, tcx.mk_substs([ret_ty.into()].iter())),
1284-
term: ty_var.into(),
1285-
},
1286-
)));
1287-
// Add `<ret_ty as Iterator>::Item = _` obligation.
1288-
ocx.register_obligation(crate::traits::Obligation::misc(
1289-
tcx,
1290-
span,
1291-
hir_id,
1292-
tcx.param_env(def_id),
1293-
projection,
1294-
));
1295-
if ocx.select_where_possible().is_empty()
1296-
&& let item_ty = infcx.resolve_vars_if_possible(ty_var)
1297-
&& item_ty.is_suggestable(tcx, false)
1298-
{
1299-
return Some(item_ty);
1265+
) -> Option<String> {
1266+
let format_as_assoc: fn(_, _, _, _, _) -> _ =
1267+
|tcx: TyCtxt<'tcx>,
1268+
_: ty::SubstsRef<'tcx>,
1269+
trait_def_id: DefId,
1270+
assoc_item_def_id: DefId,
1271+
item_ty: Ty<'tcx>| {
1272+
let trait_name = tcx.item_name(trait_def_id);
1273+
let assoc_name = tcx.item_name(assoc_item_def_id);
1274+
Some(format!("impl {trait_name}<{assoc_name} = {item_ty}>"))
1275+
};
1276+
let format_as_parenthesized: fn(_, _, _, _, _) -> _ =
1277+
|tcx: TyCtxt<'tcx>,
1278+
substs: ty::SubstsRef<'tcx>,
1279+
trait_def_id: DefId,
1280+
_: DefId,
1281+
item_ty: Ty<'tcx>| {
1282+
let trait_name = tcx.item_name(trait_def_id);
1283+
let args_tuple = substs.type_at(1);
1284+
let ty::Tuple(types) = *args_tuple.kind() else { return None; };
1285+
if !types.is_suggestable(tcx, false) {
1286+
return None;
1287+
}
1288+
let maybe_ret =
1289+
if item_ty.is_unit() { String::new() } else { format!(" -> {item_ty}") };
1290+
Some(format!(
1291+
"impl {trait_name}({}){maybe_ret}",
1292+
types.iter().map(|ty| ty.to_string()).collect::<Vec<_>>().join(", ")
1293+
))
1294+
};
1295+
1296+
for (trait_def_id, assoc_item_def_id, formatter) in [
1297+
(
1298+
tcx.get_diagnostic_item(sym::Iterator),
1299+
tcx.get_diagnostic_item(sym::IteratorItem),
1300+
format_as_assoc,
1301+
),
1302+
(
1303+
tcx.lang_items().future_trait(),
1304+
tcx.get_diagnostic_item(sym::FutureOutput),
1305+
format_as_assoc,
1306+
),
1307+
(tcx.lang_items().fn_trait(), tcx.lang_items().fn_once_output(), format_as_parenthesized),
1308+
(
1309+
tcx.lang_items().fn_mut_trait(),
1310+
tcx.lang_items().fn_once_output(),
1311+
format_as_parenthesized,
1312+
),
1313+
(
1314+
tcx.lang_items().fn_once_trait(),
1315+
tcx.lang_items().fn_once_output(),
1316+
format_as_parenthesized,
1317+
),
1318+
] {
1319+
let Some(trait_def_id) = trait_def_id else { continue; };
1320+
let Some(assoc_item_def_id) = assoc_item_def_id else { continue; };
1321+
if tcx.def_kind(assoc_item_def_id) != DefKind::AssocTy {
1322+
continue;
1323+
}
1324+
let param_env = tcx.param_env(def_id);
1325+
let infcx = tcx.infer_ctxt().build();
1326+
let substs = ty::InternalSubsts::for_item(tcx, trait_def_id, |param, _| {
1327+
if param.index == 0 { ret_ty.into() } else { infcx.var_for_def(span, param) }
1328+
});
1329+
if !infcx.type_implements_trait(trait_def_id, substs, param_env).must_apply_modulo_regions()
1330+
{
1331+
continue;
1332+
}
1333+
let ocx = ObligationCtxt::new_in_snapshot(&infcx);
1334+
let item_ty = ocx.normalize(
1335+
&ObligationCause::misc(span, hir_id),
1336+
param_env,
1337+
tcx.mk_projection(assoc_item_def_id, substs),
1338+
);
1339+
// FIXME(compiler-errors): We may benefit from resolving regions here.
1340+
if ocx.select_where_possible().is_empty()
1341+
&& let item_ty = infcx.resolve_vars_if_possible(item_ty)
1342+
&& item_ty.is_suggestable(tcx, false)
1343+
&& let Some(sugg) = formatter(tcx, infcx.resolve_vars_if_possible(substs), trait_def_id, assoc_item_def_id, item_ty)
1344+
{
1345+
return Some(sugg);
1346+
}
13001347
}
13011348
None
13021349
}

compiler/rustc_span/src/symbol.rs

+1
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,7 @@ symbols! {
193193
FromIterator,
194194
FromResidual,
195195
Future,
196+
FutureOutput,
196197
FxHashMap,
197198
FxHashSet,
198199
GlobalAlloc,

library/core/src/future/future.rs

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ use crate::task::{Context, Poll};
3737
pub trait Future {
3838
/// The type of value produced on completion.
3939
#[stable(feature = "futures_api", since = "1.36.0")]
40+
#[rustc_diagnostic_item = "FutureOutput"]
4041
type Output;
4142

4243
/// Attempt to resolve the future to a final value, registering

src/test/ui/fn/issue-80179.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ fn returns_fn_ptr() -> _ {
1818
fn returns_closure() -> _ {
1919
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types [E0121]
2020
//~| NOTE not allowed in type signatures
21-
//~| HELP consider using an `Fn`, `FnMut`, or `FnOnce` trait bound
22-
//~| NOTE for more information on `Fn` traits and closure types, see
23-
// https://doc.rust-lang.org/book/ch13-01-closures.html
21+
//~| HELP replace with an appropriate return type
22+
//~| SUGGESTION impl Fn() -> i32
23+
//~| NOTE for more information on `Fn` traits and closure types
2424
|| 0
2525
}
2626

src/test/ui/fn/issue-80179.stderr

+4-2
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,11 @@ error[E0121]: the placeholder `_` is not allowed within types on item signatures
1111
--> $DIR/issue-80179.rs:18:25
1212
|
1313
LL | fn returns_closure() -> _ {
14-
| ^ not allowed in type signatures
14+
| ^
15+
| |
16+
| not allowed in type signatures
17+
| help: replace with an appropriate return type: `impl Fn() -> i32`
1518
|
16-
= help: consider using an `Fn`, `FnMut`, or `FnOnce` trait bound
1719
= note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html
1820

1921
error: aborting due to 2 previous errors
+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
fn fn_once() -> _ {
2+
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types [E0121]
3+
//~| NOTE not allowed in type signatures
4+
//~| HELP replace with an appropriate return type
5+
//~| SUGGESTION impl FnOnce()
6+
//~| NOTE for more information on `Fn` traits and closure types
7+
let x = String::new();
8+
|| {
9+
drop(x);
10+
}
11+
}
12+
13+
fn fn_mut() -> _ {
14+
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types [E0121]
15+
//~| NOTE not allowed in type signatures
16+
//~| HELP replace with an appropriate return type
17+
//~| SUGGESTION impl FnMut(char)
18+
//~| NOTE for more information on `Fn` traits and closure types
19+
let x = String::new();
20+
|c| {
21+
x.push(c);
22+
}
23+
}
24+
25+
fn fun() -> _ {
26+
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types [E0121]
27+
//~| NOTE not allowed in type signatures
28+
//~| HELP replace with an appropriate return type
29+
//~| SUGGESTION impl Fn() -> i32
30+
//~| NOTE for more information on `Fn` traits and closure types
31+
|| 1i32
32+
}
33+
34+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
2+
--> $DIR/suggest-return-closure.rs:1:17
3+
|
4+
LL | fn fn_once() -> _ {
5+
| ^
6+
| |
7+
| not allowed in type signatures
8+
| help: replace with an appropriate return type: `impl FnOnce()`
9+
|
10+
= note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html
11+
12+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
13+
--> $DIR/suggest-return-closure.rs:13:16
14+
|
15+
LL | fn fn_mut() -> _ {
16+
| ^
17+
| |
18+
| not allowed in type signatures
19+
| help: replace with an appropriate return type: `impl FnMut(char)`
20+
|
21+
= note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html
22+
23+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
24+
--> $DIR/suggest-return-closure.rs:25:13
25+
|
26+
LL | fn fun() -> _ {
27+
| ^
28+
| |
29+
| not allowed in type signatures
30+
| help: replace with an appropriate return type: `impl Fn() -> i32`
31+
|
32+
= note: for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html
33+
34+
error: aborting due to 3 previous errors
35+
36+
For more information about this error, try `rustc --explain E0121`.
+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// edition: 2021
2+
3+
async fn a() -> i32 {
4+
0
5+
}
6+
7+
fn foo() -> _ {
8+
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types [E0121]
9+
//~| NOTE not allowed in type signatures
10+
//~| HELP replace with an appropriate return type
11+
//~| SUGGESTION impl Future<Output = i32>
12+
a()
13+
}
14+
15+
fn bar() -> _ {
16+
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types [E0121]
17+
//~| NOTE not allowed in type signatures
18+
//~| HELP replace with an appropriate return type
19+
//~| SUGGESTION impl Future<Output = i32>
20+
async { a().await }
21+
}
22+
23+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
2+
--> $DIR/suggest-return-future.rs:7:13
3+
|
4+
LL | fn foo() -> _ {
5+
| ^
6+
| |
7+
| not allowed in type signatures
8+
| help: replace with an appropriate return type: `impl Future<Output = i32>`
9+
10+
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
11+
--> $DIR/suggest-return-future.rs:15:13
12+
|
13+
LL | fn bar() -> _ {
14+
| ^
15+
| |
16+
| not allowed in type signatures
17+
| help: replace with an appropriate return type: `impl Future<Output = i32>`
18+
19+
error: aborting due to 2 previous errors
20+
21+
For more information about this error, try `rustc --explain E0121`.

0 commit comments

Comments
 (0)