diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 6ebf382083f25..548177a150b9a 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -430,6 +430,16 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if let Some(sp) = tcx.sess.psess.ambiguous_block_expr_parse.borrow().get(&sp) { err.subdiagnostic(ExprParenthesesNeeded::surrounding(*sp)); } + // The operand may be an uncalled function, in which case it is its return type + // the user meant to dereference. Only suggest the call when that return type is + // itself dereferenceable, mirroring the checks `lookup_derefing` just failed. + self.suggest_fn_call(&mut err, oprnd, oprnd_t, |output| { + output.builtin_deref(true).is_some() + || self.tcx.lang_items().deref_trait().is_some_and(|deref_trait| { + self.type_implements_trait(deref_trait, [output], self.param_env) + .may_apply() + }) + }); Ty::new_error(tcx, err.emit()) }), hir::UnOp::Not => { @@ -555,7 +565,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { LangItem::IntoIterIntoIter | LangItem::IteratorNext if expr.span.is_desugaring(DesugaringKind::ForLoop) => { - Some(ObligationCauseCode::ForLoopIterator) + Some(ObligationCauseCode::ForLoopIterator(arg.hir_id)) } LangItem::TryTraitFromOutput if expr.span.is_desugaring(DesugaringKind::TryBlock) => diff --git a/compiler/rustc_middle/src/traits/mod.rs b/compiler/rustc_middle/src/traits/mod.rs index 9736aabf29009..4dfc8d7c9705d 100644 --- a/compiler/rustc_middle/src/traits/mod.rs +++ b/compiler/rustc_middle/src/traits/mod.rs @@ -374,7 +374,7 @@ pub enum ObligationCauseCode<'tcx> { AwaitableExpr(HirId), - ForLoopIterator, + ForLoopIterator(HirId), QuestionMark, diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs index 7921250a85a9e..a7f05d756af0f 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/suggestions.rs @@ -1119,12 +1119,29 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { .collect::>() .join(", "); - if let ObligationCauseCode::FunctionArg { arg_hir_id, .. } = obligation.cause.code() - && obligation.cause.span.can_be_used_for_suggestions() - { + let callee_hir_id = match obligation.cause.code() { + ObligationCauseCode::FunctionArg { arg_hir_id, .. } + if obligation.cause.span.can_be_used_for_suggestions() => + { + Some(*arg_hir_id) + } + // The iterator of a `for` loop is passed to `IntoIterator::into_iter`, so the failing + // `Iterator` goal is a derived obligation and `cause.span` carries the loop's + // desugaring context. The expression is still the user's, which its own span attests. + code => match code.peel_derives() { + ObligationCauseCode::ForLoopIterator(iter_hir_id) + if self.tcx.hir_span(*iter_hir_id).can_be_used_for_suggestions() => + { + Some(*iter_hir_id) + } + _ => None, + }, + }; + + if let Some(callee_hir_id) = callee_hir_id { let span = obligation.cause.span; - let arg_expr = match self.tcx.hir_node(*arg_hir_id) { + let arg_expr = match self.tcx.hir_node(callee_hir_id) { hir::Node::Expr(expr) => Some(expr), _ => None, }; @@ -3779,7 +3796,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { | ObligationCauseCode::ReturnValue(_) | ObligationCauseCode::BlockTailExpression(..) | ObligationCauseCode::AwaitableExpr(_) - | ObligationCauseCode::ForLoopIterator + | ObligationCauseCode::ForLoopIterator(_) | ObligationCauseCode::QuestionMark | ObligationCauseCode::CheckAssociatedTypeBounds { .. } | ObligationCauseCode::LetElse diff --git a/tests/ui/suggestions/suggest-calling-fn-in-deref-issue-161564.rs b/tests/ui/suggestions/suggest-calling-fn-in-deref-issue-161564.rs new file mode 100644 index 0000000000000..6f1bf4e4589b8 --- /dev/null +++ b/tests/ui/suggestions/suggest-calling-fn-in-deref-issue-161564.rs @@ -0,0 +1,73 @@ +// Dereferencing an uncalled function item should suggest calling it, rather than +// only complaining that the function's own type cannot be dereferenced. + +pub fn ret_ref() -> &'static usize { + &const { 12 } +} + +pub fn ret_val() -> usize { + 12 +} + +pub fn with_args(_: u8) -> &'static usize { + &const { 12 } +} + +pub fn ret_box() -> Box { + Box::new(12) +} + +struct S; + +impl S { + fn assoc() -> &'static usize { + &const { 12 } + } +} + +pub fn fn_item() { + let _a = *ret_ref; + //~^ ERROR type `fn() -> &'static usize {ret_ref}` cannot be dereferenced + //~| HELP use parentheses to call this function +} + +pub fn takes_args() { + let _a = *with_args; + //~^ ERROR type `fn(u8) -> &'static usize {with_args}` cannot be dereferenced + //~| HELP use parentheses to call this function +} + +pub fn assoc_fn() { + let _a = *S::assoc; + //~^ ERROR type `fn() -> &'static usize {S::assoc}` cannot be dereferenced + //~| HELP use parentheses to call this associated function +} + +pub fn overloaded_deref() { + let _a = *ret_box; + //~^ ERROR type `fn() -> Box {ret_box}` cannot be dereferenced + //~| HELP use parentheses to call this function +} + +pub fn fn_pointer() { + let f: fn() -> &'static usize = ret_ref; + let _a = *f; + //~^ ERROR type `fn() -> &'static usize` cannot be dereferenced + //~| HELP use parentheses to call this function pointer +} + +pub fn closure() { + let c = || &const { 12usize }; + let _a = *c; + //~^ ERROR cannot be dereferenced + //~| HELP use parentheses to call this closure +} + +// Negative case: calling this one still would not produce something dereferenceable, +// so no suggestion should be offered. +pub fn not_derefable_when_called() { + let _a = *ret_val; + //~^ ERROR type `fn() -> usize {ret_val}` cannot be dereferenced +} + +fn main() {} diff --git a/tests/ui/suggestions/suggest-calling-fn-in-deref-issue-161564.stderr b/tests/ui/suggestions/suggest-calling-fn-in-deref-issue-161564.stderr new file mode 100644 index 0000000000000..a119a368450c3 --- /dev/null +++ b/tests/ui/suggestions/suggest-calling-fn-in-deref-issue-161564.stderr @@ -0,0 +1,75 @@ +error[E0614]: type `fn() -> &'static usize {ret_ref}` cannot be dereferenced + --> $DIR/suggest-calling-fn-in-deref-issue-161564.rs:29:14 + | +LL | let _a = *ret_ref; + | ^^^^^^^^ can't be dereferenced + | +help: use parentheses to call this function + | +LL | let _a = *ret_ref(); + | ++ + +error[E0614]: type `fn(u8) -> &'static usize {with_args}` cannot be dereferenced + --> $DIR/suggest-calling-fn-in-deref-issue-161564.rs:35:14 + | +LL | let _a = *with_args; + | ^^^^^^^^^^ can't be dereferenced + | +help: use parentheses to call this function + | +LL | let _a = *with_args(/* u8 */); + | ++++++++++ + +error[E0614]: type `fn() -> &'static usize {S::assoc}` cannot be dereferenced + --> $DIR/suggest-calling-fn-in-deref-issue-161564.rs:41:14 + | +LL | let _a = *S::assoc; + | ^^^^^^^^^ can't be dereferenced + | +help: use parentheses to call this associated function + | +LL | let _a = *S::assoc(); + | ++ + +error[E0614]: type `fn() -> Box {ret_box}` cannot be dereferenced + --> $DIR/suggest-calling-fn-in-deref-issue-161564.rs:47:14 + | +LL | let _a = *ret_box; + | ^^^^^^^^ can't be dereferenced + | +help: use parentheses to call this function + | +LL | let _a = *ret_box(); + | ++ + +error[E0614]: type `fn() -> &'static usize` cannot be dereferenced + --> $DIR/suggest-calling-fn-in-deref-issue-161564.rs:54:14 + | +LL | let _a = *f; + | ^^ can't be dereferenced + | +help: use parentheses to call this function pointer + | +LL | let _a = *f(); + | ++ + +error[E0614]: type `{closure@$DIR/suggest-calling-fn-in-deref-issue-161564.rs:60:13: 60:15}` cannot be dereferenced + --> $DIR/suggest-calling-fn-in-deref-issue-161564.rs:61:14 + | +LL | let _a = *c; + | ^^ can't be dereferenced + | +help: use parentheses to call this closure + | +LL | let _a = *c(); + | ++ + +error[E0614]: type `fn() -> usize {ret_val}` cannot be dereferenced + --> $DIR/suggest-calling-fn-in-deref-issue-161564.rs:69:14 + | +LL | let _a = *ret_val; + | ^^^^^^^^ can't be dereferenced + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0614`. diff --git a/tests/ui/suggestions/suggest-calling-fn-in-for-loop-issue-161564.rs b/tests/ui/suggestions/suggest-calling-fn-in-for-loop-issue-161564.rs new file mode 100644 index 0000000000000..5d50346b5b6fe --- /dev/null +++ b/tests/ui/suggestions/suggest-calling-fn-in-for-loop-issue-161564.rs @@ -0,0 +1,37 @@ +//@ edition: 2021 + +//! Check that a fn item used as the iterator of a `for` loop is suggested to be called, the way +//! it already is when it is passed as a function argument. + +struct S; + +trait T { + fn assoc_in_trait() -> std::vec::IntoIter; +} + +impl T for S { + fn assoc_in_trait() -> std::vec::IntoIter { + vec![1u8].into_iter() + } +} + +impl S { + fn inherent_assoc() -> impl Iterator { + [1u8].into_iter() + } +} + +fn free_fn() -> impl Iterator { + [1u8].into_iter() +} + +fn main() { + for _ in S::inherent_assoc {} //~ ERROR [E0277] + for _ in ::assoc_in_trait {} //~ ERROR [E0277] + for _ in free_fn {} //~ ERROR [E0277] + + let closure = || vec![1u8].into_iter(); + for _ in closure {} //~ ERROR [E0277] + + for _ in || vec![1u8].into_iter() {} //~ ERROR [E0277] +} diff --git a/tests/ui/suggestions/suggest-calling-fn-in-for-loop-issue-161564.stderr b/tests/ui/suggestions/suggest-calling-fn-in-for-loop-issue-161564.stderr new file mode 100644 index 0000000000000..e2edaebb65c9b --- /dev/null +++ b/tests/ui/suggestions/suggest-calling-fn-in-for-loop-issue-161564.stderr @@ -0,0 +1,68 @@ +error[E0277]: `fn() -> impl Iterator {S::inherent_assoc}` is not an iterator + --> $DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:29:14 + | +LL | for _ in S::inherent_assoc {} + | ^^^^^^^^^^^^^^^^^ `fn() -> impl Iterator {S::inherent_assoc}` is not an iterator + | + = help: the trait `Iterator` is not implemented for fn item `fn() -> impl Iterator {S::inherent_assoc}` + = note: required for `fn() -> impl Iterator {S::inherent_assoc}` to implement `IntoIterator` +help: use parentheses to call this associated function + | +LL | for _ in S::inherent_assoc() {} + | ++ + +error[E0277]: `fn() -> std::vec::IntoIter {::assoc_in_trait}` is not an iterator + --> $DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:30:14 + | +LL | for _ in ::assoc_in_trait {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ `fn() -> std::vec::IntoIter {::assoc_in_trait}` is not an iterator + | + = help: the trait `Iterator` is not implemented for fn item `fn() -> std::vec::IntoIter {::assoc_in_trait}` + = note: required for `fn() -> std::vec::IntoIter {::assoc_in_trait}` to implement `IntoIterator` +help: use parentheses to call this associated function + | +LL | for _ in ::assoc_in_trait() {} + | ++ + +error[E0277]: `fn() -> impl Iterator {free_fn}` is not an iterator + --> $DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:31:14 + | +LL | for _ in free_fn {} + | ^^^^^^^ `fn() -> impl Iterator {free_fn}` is not an iterator + | + = help: the trait `Iterator` is not implemented for fn item `fn() -> impl Iterator {free_fn}` + = note: required for `fn() -> impl Iterator {free_fn}` to implement `IntoIterator` +help: use parentheses to call this function + | +LL | for _ in free_fn() {} + | ++ + +error[E0277]: `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:33:19: 33:21}` is not an iterator + --> $DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:34:14 + | +LL | for _ in closure {} + | ^^^^^^^ `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:33:19: 33:21}` is not an iterator + | + = help: the trait `Iterator` is not implemented for closure `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:33:19: 33:21}` + = note: required for `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:33:19: 33:21}` to implement `IntoIterator` +help: use parentheses to call this closure + | +LL | for _ in closure() {} + | ++ + +error[E0277]: `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:36:14: 36:16}` is not an iterator + --> $DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:36:14 + | +LL | for _ in || vec![1u8].into_iter() {} + | ^^^^^^^^^^^^^^^^^^^^^^^^ `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:36:14: 36:16}` is not an iterator + | + = help: the trait `Iterator` is not implemented for closure `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:36:14: 36:16}` + = note: required for `{closure@$DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:36:14: 36:16}` to implement `IntoIterator` +help: use parentheses to call this closure + | +LL | for _ in (|| vec![1u8].into_iter())() {} + | + +++ + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0277`.