Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion compiler/rustc_hir_typeck/src/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 => {
Expand Down Expand Up @@ -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) =>
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/traits/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -374,7 +374,7 @@ pub enum ObligationCauseCode<'tcx> {

AwaitableExpr(HirId),

ForLoopIterator,
ForLoopIterator(HirId),

QuestionMark,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1119,12 +1119,29 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
.collect::<Vec<_>>()
.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,
};
Expand Down Expand Up @@ -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
Expand Down
73 changes: 73 additions & 0 deletions tests/ui/suggestions/suggest-calling-fn-in-deref-issue-161564.rs
Original file line number Diff line number Diff line change
@@ -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<usize> {
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<usize> {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() {}
Original file line number Diff line number Diff line change
@@ -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<usize> {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`.
Original file line number Diff line number Diff line change
@@ -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<u8>;
}

impl T for S {
fn assoc_in_trait() -> std::vec::IntoIter<u8> {
vec![1u8].into_iter()
}
}

impl S {
fn inherent_assoc() -> impl Iterator<Item = u8> {
[1u8].into_iter()
}
}

fn free_fn() -> impl Iterator<Item = u8> {
[1u8].into_iter()
}

fn main() {
for _ in S::inherent_assoc {} //~ ERROR [E0277]
for _ in <S as T>::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]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
error[E0277]: `fn() -> impl Iterator<Item = u8> {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<Item = u8> {S::inherent_assoc}` is not an iterator
|
= help: the trait `Iterator` is not implemented for fn item `fn() -> impl Iterator<Item = u8> {S::inherent_assoc}`
= note: required for `fn() -> impl Iterator<Item = u8> {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<u8> {<S as T>::assoc_in_trait}` is not an iterator
--> $DIR/suggest-calling-fn-in-for-loop-issue-161564.rs:30:14
|
LL | for _ in <S as T>::assoc_in_trait {}
| ^^^^^^^^^^^^^^^^^^^^^^^^ `fn() -> std::vec::IntoIter<u8> {<S as T>::assoc_in_trait}` is not an iterator
|
= help: the trait `Iterator` is not implemented for fn item `fn() -> std::vec::IntoIter<u8> {<S as T>::assoc_in_trait}`
= note: required for `fn() -> std::vec::IntoIter<u8> {<S as T>::assoc_in_trait}` to implement `IntoIterator`
help: use parentheses to call this associated function
|
LL | for _ in <S as T>::assoc_in_trait() {}
| ++

error[E0277]: `fn() -> impl Iterator<Item = u8> {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<Item = u8> {free_fn}` is not an iterator
|
= help: the trait `Iterator` is not implemented for fn item `fn() -> impl Iterator<Item = u8> {free_fn}`
= note: required for `fn() -> impl Iterator<Item = u8> {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`.
Loading