Skip to content

Commit

Permalink
Rollup merge of #133382 - mu001999-contrib:diag/fnitem, r=lcnr
Browse files Browse the repository at this point in the history
Suggest considering casting fn item as fn pointer in more cases

Fixes #132648
  • Loading branch information
matthiaskrgr authored Jan 29, 2025
2 parents 61cc3e5 + 4203627 commit f8d103d
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 6 deletions.
2 changes: 2 additions & 0 deletions compiler/rustc_trait_selection/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,8 @@ trait_selection_explicit_lifetime_required_with_param_type = explicit lifetime r
trait_selection_fn_consider_casting = consider casting the fn item to a fn pointer: `{$casting}`
trait_selection_fn_consider_casting_both = consider casting both fn items to fn pointers using `as {$sig}`
trait_selection_fn_uniq_types = different fn items have unique types, even if their signatures are the same
trait_selection_fps_cast = consider casting to a fn pointer
trait_selection_fps_cast_both = consider casting both fn items to fn pointers using `as {$expected_sig}`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1844,7 +1844,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
self.suggest_tuple_pattern(cause, &exp_found, diag);
self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
self.suggest_function_pointers(cause, span, &exp_found, diag);
self.suggest_function_pointers(cause, span, &exp_found, terr, diag);
self.suggest_turning_stmt_into_expr(cause, &exp_found, diag);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ use rustc_middle::traits::{
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
StatementAsExpression,
};
use rustc_middle::ty::error::TypeError;
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt};
use rustc_span::{Span, sym};
Expand All @@ -20,7 +21,7 @@ use tracing::debug;
use crate::error_reporting::TypeErrCtxt;
use crate::error_reporting::infer::hir::Path;
use crate::errors::{
ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
ConsiderAddingAwait, FnConsiderCasting, FnConsiderCastingBoth, FnItemsAreDistinct, FnUniqTypes,
FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding,
SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags,
};
Expand Down Expand Up @@ -381,14 +382,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
}

pub(super) fn suggest_function_pointers(
pub fn suggest_function_pointers_impl(
&self,
cause: &ObligationCause<'tcx>,
span: Span,
span: Option<Span>,
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
diag: &mut Diag<'_>,
) {
debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
let ty::error::ExpectedFound { expected, found } = exp_found;
let expected_inner = expected.peel_refs();
let found_inner = found.peel_refs();
Expand All @@ -411,6 +410,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
return;
}

let Some(span) = span else {
let casting = format!("{fn_name} as {sig}");
diag.subdiagnostic(FnItemsAreDistinct);
diag.subdiagnostic(FnConsiderCasting { casting });
return;
};

let sugg = match (expected.is_ref(), found.is_ref()) {
(true, false) => FunctionPointerSuggestion::UseRef { span, fn_name },
(false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
Expand Down Expand Up @@ -445,6 +451,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}

let fn_name = self.tcx.def_path_str_with_args(*did2, args2);

let Some(span) = span else {
diag.subdiagnostic(FnConsiderCastingBoth { sig: *expected_sig });
return;
};

let sug = if found.is_ref() {
FunctionPointerSuggestion::CastBothRef {
span,
Expand Down Expand Up @@ -488,6 +500,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
};
}

pub(super) fn suggest_function_pointers(
&self,
cause: &ObligationCause<'tcx>,
span: Span,
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
terr: TypeError<'tcx>,
diag: &mut Diag<'_>,
) {
debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);

if exp_found.expected.peel_refs().is_fn() && exp_found.found.peel_refs().is_fn() {
self.suggest_function_pointers_impl(Some(span), exp_found, diag);
} else if let TypeError::Sorts(exp_found) = terr {
self.suggest_function_pointers_impl(None, &exp_found, diag);
}
}

pub fn should_suggest_as_ref_kind(
&self,
expected: Ty<'tcx>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1969,6 +1969,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
StringPart::highlighted(exp_found.found.to_string()),
StringPart::normal("`"),
]);
self.suggest_function_pointers_impl(None, &exp_found, err);
}

true
Expand Down
6 changes: 6 additions & 0 deletions compiler/rustc_trait_selection/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1496,6 +1496,12 @@ pub struct FnConsiderCasting {
pub casting: String,
}

#[derive(Subdiagnostic)]
#[help(trait_selection_fn_consider_casting_both)]
pub struct FnConsiderCastingBoth<'a> {
pub sig: Binder<'a, FnSig<'a>>,
}

#[derive(Subdiagnostic)]
pub enum SuggestAccessingField<'a> {
#[suggestion(
Expand Down
9 changes: 9 additions & 0 deletions tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
//@ edition: 2021

fn foo() {}

fn main() {
let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect(); //~ ERROR
let _: Vec<fn()> = [foo].into_iter().collect(); //~ ERROR
let _: Vec<fn()> = Vec::from([foo]); //~ ERROR
}
59 changes: 59 additions & 0 deletions tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
error[E0277]: a value of type `Vec<(&str, fn())>` cannot be built from an iterator over elements of type `(&str, fn() {foo})`
--> $DIR/casting-fn-item-to-fn-pointer.rs:6:59
|
LL | let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect();
| ^^^^^^^ value of type `Vec<(&str, fn())>` cannot be built from `std::iter::Iterator<Item=(&str, fn() {foo})>`
|
= help: the trait `FromIterator<(&_, fn() {foo})>` is not implemented for `Vec<(&str, fn())>`
but trait `FromIterator<(&_, fn())>` is implemented for it
= help: for that trait implementation, expected `fn()`, found `fn() {foo}`
= note: fn items are distinct from fn pointers
= help: consider casting the fn item to a fn pointer: `foo as fn()`
note: the method call chain might not have had the expected associated types
--> $DIR/casting-fn-item-to-fn-pointer.rs:6:47
|
LL | let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect();
| -------------- ^^^^^^^^^^^ `Iterator::Item` is `(&str, fn() {foo})` here
| |
| this expression has type `[(&str, fn() {foo}); 1]`
note: required by a bound in `collect`
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL

error[E0277]: a value of type `Vec<fn()>` cannot be built from an iterator over elements of type `fn() {foo}`
--> $DIR/casting-fn-item-to-fn-pointer.rs:7:42
|
LL | let _: Vec<fn()> = [foo].into_iter().collect();
| ^^^^^^^ value of type `Vec<fn()>` cannot be built from `std::iter::Iterator<Item=fn() {foo}>`
|
= help: the trait `FromIterator<fn() {foo}>` is not implemented for `Vec<fn()>`
but trait `FromIterator<fn()>` is implemented for it
= help: for that trait implementation, expected `fn()`, found `fn() {foo}`
= note: fn items are distinct from fn pointers
= help: consider casting the fn item to a fn pointer: `foo as fn()`
note: the method call chain might not have had the expected associated types
--> $DIR/casting-fn-item-to-fn-pointer.rs:7:30
|
LL | let _: Vec<fn()> = [foo].into_iter().collect();
| ----- ^^^^^^^^^^^ `Iterator::Item` is `fn() {foo}` here
| |
| this expression has type `[fn() {foo}; 1]`
note: required by a bound in `collect`
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL

error[E0308]: mismatched types
--> $DIR/casting-fn-item-to-fn-pointer.rs:8:24
|
LL | let _: Vec<fn()> = Vec::from([foo]);
| --------- ^^^^^^^^^^^^^^^^ expected `Vec<fn()>`, found `Vec<fn() {foo}>`
| |
| expected due to this
|
= note: expected struct `Vec<fn()>`
found struct `Vec<fn() {foo}>`
= note: fn items are distinct from fn pointers
= help: consider casting the fn item to a fn pointer: `foo as fn()`

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0277, E0308.
For more information about an error, try `rustc --explain E0277`.
2 changes: 2 additions & 0 deletions tests/ui/typeck/issue-107775.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ LL | Self { map }
|
= note: expected struct `HashMap<u16, fn(_) -> Pin<Box<(dyn Future<Output = ()> + Send + 'static)>>>`
found struct `HashMap<{integer}, fn(_) -> Pin<Box<dyn Future<Output = ()> + Send>> {<Struct as Trait>::do_something::<'_>}>`
= note: fn items are distinct from fn pointers
= help: consider casting the fn item to a fn pointer: `<Struct as Trait>::do_something::<'_> as fn(u8) -> Pin<Box<(dyn Future<Output = ()> + Send + 'static)>>`

error: aborting due to 1 previous error

Expand Down

0 comments on commit f8d103d

Please sign in to comment.