diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index 19e2679ae4da7..de95e32de1091 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -33,7 +33,8 @@ use tracing::{debug, instrument}; use super::on_unimplemented::{AppendConstMessage, OnUnimplementedNote}; use super::suggestions::get_explanation_based_on_obligation; use super::{ - ArgKind, CandidateSimilarity, GetSafeTransmuteErrorAndReason, ImplCandidate, UnsatisfiedConst, + ArgKind, CandidateSimilarity, FindExprBySpan, GetSafeTransmuteErrorAndReason, ImplCandidate, + UnsatisfiedConst, }; use crate::error_reporting::TypeErrCtxt; use crate::error_reporting::infer::TyCategory; @@ -378,14 +379,34 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { if let (ty::FnPtr(..), ty::FnDef(..)) = (cand.self_ty().kind(), main_trait_ref.self_ty().skip_binder().kind()) { - err.span_suggestion( - span.shrink_to_hi(), + // Wrap method receivers and `&`-references in parens + let suggestion = if self.tcx.sess.source_map().span_look_ahead(span, ".", Some(50)).is_some() { + vec![ + (span.shrink_to_lo(), format!("(")), + (span.shrink_to_hi(), format!(" as {})", cand.self_ty())), + ] + } else if let Some(body) = self.tcx.hir().maybe_body_owned_by(obligation.cause.body_id) { + let mut expr_finder = FindExprBySpan::new(span, self.tcx); + expr_finder.visit_expr(body.value); + if let Some(expr) = expr_finder.result && + let hir::ExprKind::AddrOf(_, _, expr) = expr.kind { + vec![ + (expr.span.shrink_to_lo(), format!("(")), + (expr.span.shrink_to_hi(), format!(" as {})", cand.self_ty())), + ] + } else { + vec![(span.shrink_to_hi(), format!(" as {}", cand.self_ty()))] + } + } else { + vec![(span.shrink_to_hi(), format!(" as {}", cand.self_ty()))] + }; + err.multipart_suggestion( format!( "the trait `{}` is implemented for fn pointer `{}`, try casting using `as`", cand.print_trait_sugared(), cand.self_ty(), ), - format!(" as {}", cand.self_ty()), + suggestion, Applicability::MaybeIncorrect, ); true diff --git a/src/tools/tidy/src/issues.txt b/src/tools/tidy/src/issues.txt index 6b4c0e9c0b9b9..2071abefbe17e 100644 --- a/src/tools/tidy/src/issues.txt +++ b/src/tools/tidy/src/issues.txt @@ -4076,7 +4076,6 @@ ui/traits/issue-96664.rs ui/traits/issue-96665.rs ui/traits/issue-97576.rs ui/traits/issue-97695-double-trivial-bound.rs -ui/traits/issue-99875.rs ui/traits/next-solver/coherence/issue-102048.rs ui/traits/next-solver/issue-118950-root-region.rs ui/traits/object/issue-33140-traitobject-crate.rs diff --git a/tests/ui/traits/issue-99875.rs b/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.rs similarity index 100% rename from tests/ui/traits/issue-99875.rs rename to tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.rs diff --git a/tests/ui/traits/issue-99875.stderr b/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.stderr similarity index 74% rename from tests/ui/traits/issue-99875.stderr rename to tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.stderr index 29e87571561d8..0666da4c707d4 100644 --- a/tests/ui/traits/issue-99875.stderr +++ b/tests/ui/traits/fn-pointer/bare-fn-no-impl-fn-ptr-99875.stderr @@ -1,5 +1,5 @@ error[E0277]: the trait bound `fn(Argument) -> Return {function}: Trait` is not satisfied - --> $DIR/issue-99875.rs:12:11 + --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:12:11 | LL | takes(function); | ----- ^^^^^^^^ the trait `Trait` is not implemented for fn item `fn(Argument) -> Return {function}` @@ -7,7 +7,7 @@ LL | takes(function); | required by a bound introduced by this call | note: required by a bound in `takes` - --> $DIR/issue-99875.rs:9:18 + --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:9:18 | LL | fn takes(_: impl Trait) {} | ^^^^^ required by this bound in `takes` @@ -16,17 +16,17 @@ help: the trait `Trait` is implemented for fn pointer `fn(Argument) -> Return`, LL | takes(function as fn(Argument) -> Return); | +++++++++++++++++++++++++ -error[E0277]: the trait bound `{closure@$DIR/issue-99875.rs:14:11: 14:34}: Trait` is not satisfied - --> $DIR/issue-99875.rs:14:11 +error[E0277]: the trait bound `{closure@$DIR/bare-fn-no-impl-fn-ptr-99875.rs:14:11: 14:34}: Trait` is not satisfied + --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:14:11 | LL | takes(|_: Argument| -> Return { todo!() }); - | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for closure `{closure@$DIR/issue-99875.rs:14:11: 14:34}` + | ----- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Trait` is not implemented for closure `{closure@$DIR/bare-fn-no-impl-fn-ptr-99875.rs:14:11: 14:34}` | | | required by a bound introduced by this call | = help: the trait `Trait` is implemented for fn pointer `fn(Argument) -> Return` note: required by a bound in `takes` - --> $DIR/issue-99875.rs:9:18 + --> $DIR/bare-fn-no-impl-fn-ptr-99875.rs:9:18 | LL | fn takes(_: impl Trait) {} | ^^^^^ required by this bound in `takes` diff --git a/tests/ui/traits/fn-trait-cast-diagnostic.rs b/tests/ui/traits/fn-pointer/fn-trait-cast-diagnostic.rs similarity index 100% rename from tests/ui/traits/fn-trait-cast-diagnostic.rs rename to tests/ui/traits/fn-pointer/fn-trait-cast-diagnostic.rs diff --git a/tests/ui/traits/fn-trait-cast-diagnostic.stderr b/tests/ui/traits/fn-pointer/fn-trait-cast-diagnostic.stderr similarity index 100% rename from tests/ui/traits/fn-trait-cast-diagnostic.stderr rename to tests/ui/traits/fn-pointer/fn-trait-cast-diagnostic.stderr diff --git a/tests/ui/traits/fn-pointer/suggest-wrap-parens-method.fixed b/tests/ui/traits/fn-pointer/suggest-wrap-parens-method.fixed new file mode 100644 index 0000000000000..e54963d01e83f --- /dev/null +++ b/tests/ui/traits/fn-pointer/suggest-wrap-parens-method.fixed @@ -0,0 +1,15 @@ +//@ run-rustfix + +trait Foo {} + +impl Foo for fn() {} + +trait Bar { + fn do_stuff(&self) where Self: Foo {} +} +impl Bar for T {} + +fn main() { + (main as fn()).do_stuff(); + //~^ ERROR the trait bound +} diff --git a/tests/ui/traits/fn-pointer/suggest-wrap-parens-method.rs b/tests/ui/traits/fn-pointer/suggest-wrap-parens-method.rs new file mode 100644 index 0000000000000..89c1295613cc0 --- /dev/null +++ b/tests/ui/traits/fn-pointer/suggest-wrap-parens-method.rs @@ -0,0 +1,15 @@ +//@ run-rustfix + +trait Foo {} + +impl Foo for fn() {} + +trait Bar { + fn do_stuff(&self) where Self: Foo {} +} +impl Bar for T {} + +fn main() { + main.do_stuff(); + //~^ ERROR the trait bound +} diff --git a/tests/ui/traits/fn-pointer/suggest-wrap-parens-method.stderr b/tests/ui/traits/fn-pointer/suggest-wrap-parens-method.stderr new file mode 100644 index 0000000000000..2fc1523a19373 --- /dev/null +++ b/tests/ui/traits/fn-pointer/suggest-wrap-parens-method.stderr @@ -0,0 +1,19 @@ +error[E0277]: the trait bound `fn() {main}: Foo` is not satisfied + --> $DIR/suggest-wrap-parens-method.rs:13:10 + | +LL | main.do_stuff(); + | ^^^^^^^^ the trait `Foo` is not implemented for fn item `fn() {main}` + | +note: required by a bound in `Bar::do_stuff` + --> $DIR/suggest-wrap-parens-method.rs:8:36 + | +LL | fn do_stuff(&self) where Self: Foo {} + | ^^^ required by this bound in `Bar::do_stuff` +help: the trait `Foo` is implemented for fn pointer `fn()`, try casting using `as` + | +LL | (main as fn()).do_stuff(); + | + ++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/traits/fn-pointer/suggest-wrap-parens.fixed b/tests/ui/traits/fn-pointer/suggest-wrap-parens.fixed new file mode 100644 index 0000000000000..0bc8792b04e06 --- /dev/null +++ b/tests/ui/traits/fn-pointer/suggest-wrap-parens.fixed @@ -0,0 +1,10 @@ +//@ run-rustfix + +trait Foo {} + +impl Foo for fn() {} + +fn main() { + let _x: &dyn Foo = &(main as fn()); + //~^ ERROR the trait bound +} diff --git a/tests/ui/traits/fn-pointer/suggest-wrap-parens.rs b/tests/ui/traits/fn-pointer/suggest-wrap-parens.rs new file mode 100644 index 0000000000000..ffe0826c03506 --- /dev/null +++ b/tests/ui/traits/fn-pointer/suggest-wrap-parens.rs @@ -0,0 +1,10 @@ +//@ run-rustfix + +trait Foo {} + +impl Foo for fn() {} + +fn main() { + let _x: &dyn Foo = &main; + //~^ ERROR the trait bound +} diff --git a/tests/ui/traits/fn-pointer/suggest-wrap-parens.stderr b/tests/ui/traits/fn-pointer/suggest-wrap-parens.stderr new file mode 100644 index 0000000000000..b71debac7156d --- /dev/null +++ b/tests/ui/traits/fn-pointer/suggest-wrap-parens.stderr @@ -0,0 +1,15 @@ +error[E0277]: the trait bound `fn() {main}: Foo` is not satisfied + --> $DIR/suggest-wrap-parens.rs:8:24 + | +LL | let _x: &dyn Foo = &main; + | ^^^^^ the trait `Foo` is not implemented for fn item `fn() {main}` + | + = note: required for the cast from `&fn() {main}` to `&dyn Foo` +help: the trait `Foo` is implemented for fn pointer `fn()`, try casting using `as` + | +LL | let _x: &dyn Foo = &(main as fn()); + | + ++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0277`.