From 63be7a24242b262ba9924eec89a5cc6512d8b504 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 9 Oct 2022 21:02:12 +0000 Subject: [PATCH 1/5] Suggest calling ctor when trait is unimplemented --- .../src/traits/error_reporting/suggestions.rs | 19 ++++++++++++++- .../suggestions/call-on-unimplemented-ctor.rs | 17 +++++++++++++ .../call-on-unimplemented-ctor.stderr | 24 +++++++++++++++++++ 3 files changed, 59 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/suggestions/call-on-unimplemented-ctor.rs create mode 100644 src/test/ui/suggestions/call-on-unimplemented-ctor.stderr diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 4431cf9f4436b..0b2fcc9b0d156 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -817,7 +817,14 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let (def_id, output_ty, callable) = match *self_ty.kind() { ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"), - ty::FnDef(def_id, _) => (def_id, self_ty.fn_sig(self.tcx).output(), "function"), + ty::FnDef(def_id, _) => ( + def_id, + self_ty.fn_sig(self.tcx).output(), + match self.tcx.def_kind(def_id) { + DefKind::Ctor(..) => "constructor", + _ => "function", + }, + ), _ => return false, }; let msg = format!("use parentheses to call the {}", callable); @@ -878,6 +885,16 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let sugg = format!("({})", args); (format!("{}{}", ident, sugg), sugg) } + Some(hir::Node::Ctor(data)) => { + let name = self.tcx.def_path_str(def_id); + err.span_label( + self.tcx.def_span(def_id), + format!("consider calling the constructor for `{}`", name), + ); + let args = data.fields().iter().map(|_| "_").collect::>().join(", "); + let sugg = format!("({})", args); + (format!("{name}{sugg}"), sugg) + } _ => return false, }; if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. }) diff --git a/src/test/ui/suggestions/call-on-unimplemented-ctor.rs b/src/test/ui/suggestions/call-on-unimplemented-ctor.rs new file mode 100644 index 0000000000000..28a319382e61a --- /dev/null +++ b/src/test/ui/suggestions/call-on-unimplemented-ctor.rs @@ -0,0 +1,17 @@ +fn main() { + insert_resource(Marker); + insert_resource(Time); + //~^ ERROR the trait bound `fn(u32) -> Time {Time}: Resource` is not satisfied + //~| HELP use parentheses to call the constructor +} + +trait Resource {} + +fn insert_resource(resource: R) {} + +struct Marker; +impl Resource for Marker {} + +struct Time(u32); + +impl Resource for Time {} diff --git a/src/test/ui/suggestions/call-on-unimplemented-ctor.stderr b/src/test/ui/suggestions/call-on-unimplemented-ctor.stderr new file mode 100644 index 0000000000000..ea8aec3098de1 --- /dev/null +++ b/src/test/ui/suggestions/call-on-unimplemented-ctor.stderr @@ -0,0 +1,24 @@ +error[E0277]: the trait bound `fn(u32) -> Time {Time}: Resource` is not satisfied + --> $DIR/call-on-unimplemented-ctor.rs:3:21 + | +LL | insert_resource(Time); + | --------------- ^^^^ the trait `Resource` is not implemented for fn item `fn(u32) -> Time {Time}` + | | + | required by a bound introduced by this call +... +LL | struct Time(u32); + | ----------- consider calling the constructor for `Time` + | +note: required by a bound in `insert_resource` + --> $DIR/call-on-unimplemented-ctor.rs:10:23 + | +LL | fn insert_resource(resource: R) {} + | ^^^^^^^^ required by this bound in `insert_resource` +help: use parentheses to call the constructor + | +LL | insert_resource(Time(_)); + | +++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. From b3edd9f775b734deb215f05269e7af0b6102c50a Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 9 Oct 2022 21:06:59 +0000 Subject: [PATCH 2/5] Use predicate_must_hold_modulo_regions --- .../src/traits/error_reporting/mod.rs | 8 ++++---- .../src/traits/error_reporting/suggestions.rs | 14 ++++---------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 4e8baa2dfab6c..6abff0ac57752 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2,10 +2,10 @@ pub mod on_unimplemented; pub mod suggestions; use super::{ - EvaluationResult, FulfillmentContext, FulfillmentError, FulfillmentErrorCode, - MismatchedProjectionTypes, Obligation, ObligationCause, ObligationCauseCode, - OnUnimplementedDirective, OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, - PredicateObligation, SelectionContext, SelectionError, TraitNotObjectSafe, + FulfillmentContext, FulfillmentError, FulfillmentErrorCode, MismatchedProjectionTypes, + Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedDirective, + OnUnimplementedNote, OutputTypeParameterMismatch, Overflow, PredicateObligation, + SelectionContext, SelectionError, TraitNotObjectSafe, }; use crate::infer::error_reporting::{TyCategory, TypeAnnotationNeeded as ErrorCode}; diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 0b2fcc9b0d156..1faf13cbddf09 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1,6 +1,5 @@ use super::{ - EvaluationResult, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, - SelectionContext, + Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext, }; use crate::autoderef::Autoderef; @@ -839,15 +838,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let new_obligation = self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self); - match self.evaluate_obligation(&new_obligation) { - Ok( - EvaluationResult::EvaluatedToOk - | EvaluationResult::EvaluatedToOkModuloRegions - | EvaluationResult::EvaluatedToOkModuloOpaqueTypes - | EvaluationResult::EvaluatedToAmbig, - ) => {} - _ => return false, + if !self.predicate_must_hold_modulo_regions(&new_obligation) { + return false; } + let hir = self.tcx.hir(); // Get the name of the callable and the arguments to be used in the suggestion. let (snippet, sugg) = match hir.get_if_local(def_id) { From f5336a969ca549ba72f91b14b50b9830cdda49b6 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 9 Oct 2022 21:39:57 +0000 Subject: [PATCH 3/5] Standardize arg suggestions between typeck and trait selection --- .../src/check/fn_ctxt/suggestions.rs | 2 +- .../src/traits/error_reporting/suggestions.rs | 106 +++++++++--------- src/test/ui/binop/issue-77910-1.stderr | 2 +- .../call-on-unimplemented-ctor.stderr | 4 +- 4 files changed, 60 insertions(+), 54 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs index 7a40def177ac9..429f068c91b06 100644 --- a/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs @@ -90,7 +90,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { if ty.is_suggestable(self.tcx, false) { format!("/* {ty} */") } else { - "".to_string() + "/* value */".to_string() } }) .collect::>() diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 1faf13cbddf09..316afe28a0a9d 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -21,6 +21,7 @@ use rustc_hir::lang_items::LangItem; use rustc_hir::{AsyncGeneratorKind, GeneratorKind, Node}; use rustc_infer::infer::error_reporting::TypeErrCtxt; use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; +use rustc_infer::infer::LateBoundRegionConversionTime; use rustc_middle::hir::map; use rustc_middle::ty::{ self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, DefIdTree, @@ -28,7 +29,7 @@ use rustc_middle::ty::{ ToPredicate, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable, }; use rustc_middle::ty::{TypeAndMut, TypeckResults}; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; +use rustc_span::symbol::{sym, Ident, Symbol}; use rustc_span::{BytePos, DesugaringKind, ExpnKind, Span, DUMMY_SP}; use rustc_target::spec::abi; use std::fmt; @@ -814,80 +815,85 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Skipping binder here, remapping below let self_ty = trait_pred.self_ty().skip_binder(); - let (def_id, output_ty, callable) = match *self_ty.kind() { - ty::Closure(def_id, substs) => (def_id, substs.as_closure().sig().output(), "closure"), - ty::FnDef(def_id, _) => ( - def_id, - self_ty.fn_sig(self.tcx).output(), - match self.tcx.def_kind(def_id) { - DefKind::Ctor(..) => "constructor", - _ => "function", - }, - ), + let (def_id, inputs, output, kind) = match *self_ty.kind() { + ty::Closure(def_id, substs) => { + let sig = substs.as_closure().sig(); + (def_id, sig.inputs().map_bound(|inputs| &inputs[1..]), sig.output(), "closure") + } + ty::FnDef(def_id, _) => { + let sig = self_ty.fn_sig(self.tcx); + ( + def_id, + sig.inputs(), + sig.output(), + match self.tcx.def_kind(def_id) { + DefKind::Ctor(..) => "constructor", + _ => "function", + }, + ) + } _ => return false, }; - let msg = format!("use parentheses to call the {}", callable); - - // "We should really create a single list of bound vars from the combined vars - // from the predicate and function, but instead we just liberate the function bound vars" - let output_ty = self.tcx.liberate_late_bound_regions(def_id, output_ty); + let output = self.replace_bound_vars_with_fresh_vars( + obligation.cause.span, + LateBoundRegionConversionTime::FnCall, + output, + ); + let inputs = inputs.skip_binder().iter().map(|ty| { + self.replace_bound_vars_with_fresh_vars( + obligation.cause.span, + LateBoundRegionConversionTime::FnCall, + inputs.rebind(*ty), + ) + }); // Remapping bound vars here - let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output_ty)); + let trait_pred_and_self = trait_pred.map_bound(|trait_pred| (trait_pred, output)); let new_obligation = self.mk_trait_obligation_with_new_self_ty(obligation.param_env, trait_pred_and_self); - if !self.predicate_must_hold_modulo_regions(&new_obligation) { return false; } - let hir = self.tcx.hir(); // Get the name of the callable and the arguments to be used in the suggestion. - let (snippet, sugg) = match hir.get_if_local(def_id) { + let hir = self.tcx.hir(); + + let msg = format!("use parentheses to call the {}", kind); + + let args = inputs + .map(|ty| { + if ty.is_suggestable(self.tcx, false) { + format!("/* {ty} */") + } else { + "/* value */".to_string() + } + }) + .collect::>() + .join(", "); + + let name = match hir.get_if_local(def_id) { Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(hir::Closure { fn_decl, fn_decl_span, .. }), + kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }), .. })) => { err.span_label(*fn_decl_span, "consider calling this closure"); let Some(name) = self.get_closure_name(def_id, err, &msg) else { return false; }; - let args = fn_decl.inputs.iter().map(|_| "_").collect::>().join(", "); - let sugg = format!("({})", args); - (format!("{}{}", name, sugg), sugg) + name.to_string() } - Some(hir::Node::Item(hir::Item { - ident, - kind: hir::ItemKind::Fn(.., body_id), - .. - })) => { + Some(hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(..), .. })) => { err.span_label(ident.span, "consider calling this function"); - let body = hir.body(*body_id); - let args = body - .params - .iter() - .map(|arg| match &arg.pat.kind { - hir::PatKind::Binding(_, _, ident, None) - // FIXME: provide a better suggestion when encountering `SelfLower`, it - // should suggest a method call. - if ident.name != kw::SelfLower => ident.to_string(), - _ => "_".to_string(), - }) - .collect::>() - .join(", "); - let sugg = format!("({})", args); - (format!("{}{}", ident, sugg), sugg) + ident.to_string() } - Some(hir::Node::Ctor(data)) => { + Some(hir::Node::Ctor(..)) => { let name = self.tcx.def_path_str(def_id); err.span_label( self.tcx.def_span(def_id), format!("consider calling the constructor for `{}`", name), ); - let args = data.fields().iter().map(|_| "_").collect::>().join(", "); - let sugg = format!("({})", args); - (format!("{name}{sugg}"), sugg) + name } _ => return false, }; @@ -901,11 +907,11 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err.span_suggestion_verbose( obligation.cause.span.shrink_to_hi(), &msg, - sugg, + format!("({args})"), Applicability::HasPlaceholders, ); } else { - err.help(&format!("{}: `{}`", msg, snippet)); + err.help(&format!("{msg}: `{name}({args})`")); } true } diff --git a/src/test/ui/binop/issue-77910-1.stderr b/src/test/ui/binop/issue-77910-1.stderr index 9c7bf6228be6e..cacea71ac97d5 100644 --- a/src/test/ui/binop/issue-77910-1.stderr +++ b/src/test/ui/binop/issue-77910-1.stderr @@ -19,7 +19,7 @@ LL | assert_eq!(foo, y); | ^^^^^^^^^^^^^^^^^^ `for<'a> fn(&'a i32) -> &'a i32 {foo}` cannot be formatted using `{:?}` because it doesn't implement `Debug` | = help: the trait `Debug` is not implemented for fn item `for<'a> fn(&'a i32) -> &'a i32 {foo}` - = help: use parentheses to call the function: `foo(s)` + = help: use parentheses to call the function: `foo(/* &i32 */)` = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/src/test/ui/suggestions/call-on-unimplemented-ctor.stderr b/src/test/ui/suggestions/call-on-unimplemented-ctor.stderr index ea8aec3098de1..8ffdff2a4a327 100644 --- a/src/test/ui/suggestions/call-on-unimplemented-ctor.stderr +++ b/src/test/ui/suggestions/call-on-unimplemented-ctor.stderr @@ -16,8 +16,8 @@ LL | fn insert_resource(resource: R) {} | ^^^^^^^^ required by this bound in `insert_resource` help: use parentheses to call the constructor | -LL | insert_resource(Time(_)); - | +++ +LL | insert_resource(Time(/* u32 */)); + | +++++++++++ error: aborting due to previous error From 7eb2d4e7d0e070eaf15955fc376498d9e5fa9e78 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sun, 9 Oct 2022 22:25:52 +0000 Subject: [PATCH 4/5] Generalize call suggestion for unsatisfied predicate --- .../rustc_hir_analysis/src/check/callee.rs | 3 +- .../src/check/fn_ctxt/suggestions.rs | 7 +- .../src/traits/error_reporting/mod.rs | 5 + .../src/traits/error_reporting/suggestions.rs | 164 +++++++++++++----- src/test/ui/binop/issue-77910-1.stderr | 2 +- .../ui/closures/closure-bounds-subtype.stderr | 4 + ...70724-add_type_neq_err_label-unwrap.stderr | 2 +- ...rg-where-it-should-have-been-called.stderr | 9 +- .../suggestions/call-on-unimplemented-ctor.rs | 2 +- .../call-on-unimplemented-ctor.stderr | 5 +- .../call-on-unimplemented-fn-ptr.rs | 15 ++ .../call-on-unimplemented-fn-ptr.stderr | 21 +++ ...rg-where-it-should-have-been-called.stderr | 9 +- 13 files changed, 174 insertions(+), 74 deletions(-) create mode 100644 src/test/ui/suggestions/call-on-unimplemented-fn-ptr.rs create mode 100644 src/test/ui/suggestions/call-on-unimplemented-fn-ptr.stderr diff --git a/compiler/rustc_hir_analysis/src/check/callee.rs b/compiler/rustc_hir_analysis/src/check/callee.rs index f0a7c91090611..088de1979babe 100644 --- a/compiler/rustc_hir_analysis/src/check/callee.rs +++ b/compiler/rustc_hir_analysis/src/check/callee.rs @@ -1,6 +1,6 @@ use super::method::probe::{IsSuggestion, Mode, ProbeScope}; use super::method::MethodCallee; -use super::{DefIdOrName, Expectation, FnCtxt, TupleArgumentsFlag}; +use super::{Expectation, FnCtxt, TupleArgumentsFlag}; use crate::type_error_struct; use rustc_ast::util::parser::PREC_POSTFIX; @@ -27,6 +27,7 @@ use rustc_span::Span; use rustc_target::spec::abi; use rustc_trait_selection::autoderef::Autoderef; use rustc_trait_selection::infer::InferCtxtExt as _; +use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; use std::iter; diff --git a/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs index 429f068c91b06..8c4fe75878fbb 100644 --- a/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs @@ -2,7 +2,6 @@ use super::FnCtxt; use crate::astconv::AstConv; use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; -use hir::def_id::DefId; use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX}; use rustc_errors::{Applicability, Diagnostic, MultiSpan}; use rustc_hir as hir; @@ -19,6 +18,7 @@ use rustc_session::errors::ExprParenthesesNeeded; use rustc_span::symbol::sym; use rustc_span::Span; use rustc_trait_selection::infer::InferCtxtExt; +use rustc_trait_selection::traits::error_reporting::DefIdOrName; use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { @@ -1209,8 +1209,3 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } } - -pub enum DefIdOrName { - DefId(DefId), - Name(&'static str), -} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs index 6abff0ac57752..b7e6a564f3953 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs @@ -2796,3 +2796,8 @@ impl<'tcx> ty::TypeVisitor<'tcx> for HasNumericInferVisitor { } } } + +pub enum DefIdOrName { + DefId(DefId), + Name(&'static str), +} diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 316afe28a0a9d..98e32e2ce8595 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -1,11 +1,13 @@ use super::{ - Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, SelectionContext, + DefIdOrName, Obligation, ObligationCause, ObligationCauseCode, PredicateObligation, + SelectionContext, }; use crate::autoderef::Autoderef; use crate::infer::InferCtxt; use crate::traits::normalize_to; +use hir::def::CtorOf; use hir::HirId; use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::stack::ensure_sufficient_stack; @@ -812,28 +814,87 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { err: &mut Diagnostic, trait_pred: ty::PolyTraitPredicate<'tcx>, ) -> bool { - // Skipping binder here, remapping below - let self_ty = trait_pred.self_ty().skip_binder(); + if let ty::PredicateKind::Trait(trait_pred) = obligation.predicate.kind().skip_binder() + && Some(trait_pred.def_id()) == self.tcx.lang_items().sized_trait() + { + // Don't suggest calling to turn an unsized type into a sized type + return false; + } - let (def_id, inputs, output, kind) = match *self_ty.kind() { - ty::Closure(def_id, substs) => { - let sig = substs.as_closure().sig(); - (def_id, sig.inputs().map_bound(|inputs| &inputs[1..]), sig.output(), "closure") + // This is duplicated from `extract_callable_info` in typeck, which + // relies on autoderef, so we can't use it here. + let found = trait_pred.self_ty().skip_binder().peel_refs(); + let Some((def_id_or_name, output, inputs)) = (match *found.kind() + { + ty::FnPtr(fn_sig) => { + Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())) } ty::FnDef(def_id, _) => { - let sig = self_ty.fn_sig(self.tcx); - ( - def_id, - sig.inputs(), - sig.output(), - match self.tcx.def_kind(def_id) { - DefKind::Ctor(..) => "constructor", - _ => "function", - }, - ) + let fn_sig = found.fn_sig(self.tcx); + Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs())) } - _ => return false, - }; + ty::Closure(def_id, substs) => { + let fn_sig = substs.as_closure().sig(); + Some(( + DefIdOrName::DefId(def_id), + fn_sig.output(), + fn_sig.inputs().map_bound(|inputs| &inputs[1..]), + )) + } + ty::Opaque(def_id, substs) => { + self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { + if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() + // args tuple will always be substs[1] + && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() + { + Some(( + DefIdOrName::DefId(def_id), + pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }) + } + ty::Dynamic(data, _, ty::Dyn) => { + data.iter().find_map(|pred| { + if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() + && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output() + // for existential projection, substs are shifted over by 1 + && let ty::Tuple(args) = proj.substs.type_at(0).kind() + { + Some(( + DefIdOrName::Name("trait object"), + pred.rebind(proj.term.ty().unwrap()), + pred.rebind(args.as_slice()), + )) + } else { + None + } + }) + } + ty::Param(_) => { + obligation.param_env.caller_bounds().iter().find_map(|pred| { + if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() + && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() + && proj.projection_ty.self_ty() == found + // args tuple will always be substs[1] + && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() + { + Some(( + DefIdOrName::Name("type parameter"), + pred.kind().rebind(proj.term.ty().unwrap()), + pred.kind().rebind(args.as_slice()), + )) + } else { + None + } + }) + } + _ => None, + }) else { return false; }; let output = self.replace_bound_vars_with_fresh_vars( obligation.cause.span, LateBoundRegionConversionTime::FnCall, @@ -859,7 +920,18 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { // Get the name of the callable and the arguments to be used in the suggestion. let hir = self.tcx.hir(); - let msg = format!("use parentheses to call the {}", kind); + let msg = match def_id_or_name { + DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { + DefKind::Ctor(CtorOf::Struct, _) => { + "use parentheses to instantiate this tuple struct".to_string() + } + DefKind::Ctor(CtorOf::Variant, _) => { + "use parentheses to instantiate this tuple variant".to_string() + } + kind => format!("use parentheses to call this {}", kind.descr(def_id)), + }, + DefIdOrName::Name(name) => format!("use parentheses to call this {name}"), + }; let args = inputs .map(|ty| { @@ -872,31 +944,6 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { .collect::>() .join(", "); - let name = match hir.get_if_local(def_id) { - Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }), - .. - })) => { - err.span_label(*fn_decl_span, "consider calling this closure"); - let Some(name) = self.get_closure_name(def_id, err, &msg) else { - return false; - }; - name.to_string() - } - Some(hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(..), .. })) => { - err.span_label(ident.span, "consider calling this function"); - ident.to_string() - } - Some(hir::Node::Ctor(..)) => { - let name = self.tcx.def_path_str(def_id); - err.span_label( - self.tcx.def_span(def_id), - format!("consider calling the constructor for `{}`", name), - ); - name - } - _ => return false, - }; if matches!(obligation.cause.code(), ObligationCauseCode::FunctionArgumentObligation { .. }) && obligation.cause.span.can_be_used_for_suggestions() { @@ -910,7 +957,32 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { format!("({args})"), Applicability::HasPlaceholders, ); - } else { + } else if let DefIdOrName::DefId(def_id) = def_id_or_name { + let name = match hir.get_if_local(def_id) { + Some(hir::Node::Expr(hir::Expr { + kind: hir::ExprKind::Closure(hir::Closure { fn_decl_span, .. }), + .. + })) => { + err.span_label(*fn_decl_span, "consider calling this closure"); + let Some(name) = self.get_closure_name(def_id, err, &msg) else { + return false; + }; + name.to_string() + } + Some(hir::Node::Item(hir::Item { ident, kind: hir::ItemKind::Fn(..), .. })) => { + err.span_label(ident.span, "consider calling this function"); + ident.to_string() + } + Some(hir::Node::Ctor(..)) => { + let name = self.tcx.def_path_str(def_id); + err.span_label( + self.tcx.def_span(def_id), + format!("consider calling the constructor for `{}`", name), + ); + name + } + _ => return false, + }; err.help(&format!("{msg}: `{name}({args})`")); } true diff --git a/src/test/ui/binop/issue-77910-1.stderr b/src/test/ui/binop/issue-77910-1.stderr index cacea71ac97d5..263a35d982911 100644 --- a/src/test/ui/binop/issue-77910-1.stderr +++ b/src/test/ui/binop/issue-77910-1.stderr @@ -19,7 +19,7 @@ LL | assert_eq!(foo, y); | ^^^^^^^^^^^^^^^^^^ `for<'a> fn(&'a i32) -> &'a i32 {foo}` cannot be formatted using `{:?}` because it doesn't implement `Debug` | = help: the trait `Debug` is not implemented for fn item `for<'a> fn(&'a i32) -> &'a i32 {foo}` - = help: use parentheses to call the function: `foo(/* &i32 */)` + = help: use parentheses to call this function: `foo(/* &i32 */)` = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 2 previous errors diff --git a/src/test/ui/closures/closure-bounds-subtype.stderr b/src/test/ui/closures/closure-bounds-subtype.stderr index 1a40326d986c4..8ad8273fc2b9b 100644 --- a/src/test/ui/closures/closure-bounds-subtype.stderr +++ b/src/test/ui/closures/closure-bounds-subtype.stderr @@ -11,6 +11,10 @@ note: required by a bound in `take_const_owned` | LL | fn take_const_owned(_: F) where F: FnOnce() + Sync + Send { | ^^^^ required by this bound in `take_const_owned` +help: use parentheses to call this type parameter + | +LL | take_const_owned(f()); + | ++ help: consider further restricting this bound | LL | fn give_owned(f: F) where F: FnOnce() + Send + std::marker::Sync { diff --git a/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr b/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr index 7f29709ce5095..b30bcfb776c8c 100644 --- a/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr +++ b/src/test/ui/issues/issue-70724-add_type_neq_err_label-unwrap.stderr @@ -29,7 +29,7 @@ LL | assert_eq!(a, 0); | ^^^^^^^^^^^^^^^^ `fn() -> i32 {a}` cannot be formatted using `{:?}` because it doesn't implement `Debug` | = help: the trait `Debug` is not implemented for fn item `fn() -> i32 {a}` - = help: use parentheses to call the function: `a()` + = help: use parentheses to call this function: `a()` = note: this error originates in the macro `assert_eq` (in Nightly builds, run with -Z macro-backtrace for more info) error: aborting due to 3 previous errors diff --git a/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr b/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr index bfd506c9f6e0c..8ed62f854f096 100644 --- a/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr +++ b/src/test/ui/suggestions/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr @@ -1,9 +1,6 @@ error[E0277]: `fn() -> impl Future {foo}` is not a future --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:10:9 | -LL | async fn foo() {} - | --- consider calling this function -... LL | bar(foo); | --- ^^^ `fn() -> impl Future {foo}` is not a future | | @@ -16,7 +13,7 @@ note: required by a bound in `bar` | LL | fn bar(f: impl Future) {} | ^^^^^^^^^^^^^^^^^ required by this bound in `bar` -help: use parentheses to call the function +help: use parentheses to call this function | LL | bar(foo()); | ++ @@ -24,8 +21,6 @@ LL | bar(foo()); error[E0277]: `[closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:25: 11:33]` is not a future --> $DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:12:9 | -LL | let async_closure = async || (); - | -------- consider calling this closure LL | bar(async_closure); | --- ^^^^^^^^^^^^^ `[closure@$DIR/async-fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:11:25: 11:33]` is not a future | | @@ -38,7 +33,7 @@ note: required by a bound in `bar` | LL | fn bar(f: impl Future) {} | ^^^^^^^^^^^^^^^^^ required by this bound in `bar` -help: use parentheses to call the closure +help: use parentheses to call this closure | LL | bar(async_closure()); | ++ diff --git a/src/test/ui/suggestions/call-on-unimplemented-ctor.rs b/src/test/ui/suggestions/call-on-unimplemented-ctor.rs index 28a319382e61a..0e7412807b465 100644 --- a/src/test/ui/suggestions/call-on-unimplemented-ctor.rs +++ b/src/test/ui/suggestions/call-on-unimplemented-ctor.rs @@ -2,7 +2,7 @@ fn main() { insert_resource(Marker); insert_resource(Time); //~^ ERROR the trait bound `fn(u32) -> Time {Time}: Resource` is not satisfied - //~| HELP use parentheses to call the constructor + //~| HELP use parentheses to instantiate this tuple struct } trait Resource {} diff --git a/src/test/ui/suggestions/call-on-unimplemented-ctor.stderr b/src/test/ui/suggestions/call-on-unimplemented-ctor.stderr index 8ffdff2a4a327..48f3366596f6b 100644 --- a/src/test/ui/suggestions/call-on-unimplemented-ctor.stderr +++ b/src/test/ui/suggestions/call-on-unimplemented-ctor.stderr @@ -5,16 +5,13 @@ LL | insert_resource(Time); | --------------- ^^^^ the trait `Resource` is not implemented for fn item `fn(u32) -> Time {Time}` | | | required by a bound introduced by this call -... -LL | struct Time(u32); - | ----------- consider calling the constructor for `Time` | note: required by a bound in `insert_resource` --> $DIR/call-on-unimplemented-ctor.rs:10:23 | LL | fn insert_resource(resource: R) {} | ^^^^^^^^ required by this bound in `insert_resource` -help: use parentheses to call the constructor +help: use parentheses to instantiate this tuple struct | LL | insert_resource(Time(/* u32 */)); | +++++++++++ diff --git a/src/test/ui/suggestions/call-on-unimplemented-fn-ptr.rs b/src/test/ui/suggestions/call-on-unimplemented-fn-ptr.rs new file mode 100644 index 0000000000000..86490c724e018 --- /dev/null +++ b/src/test/ui/suggestions/call-on-unimplemented-fn-ptr.rs @@ -0,0 +1,15 @@ +struct Foo; + +trait Bar {} + +impl Bar for Foo {} + +fn needs_bar(_: T) {} + +fn blah(f: fn() -> Foo) { + needs_bar(f); + //~^ ERROR the trait bound `fn() -> Foo: Bar` is not satisfied + //~| HELP use parentheses to call this function pointer +} + +fn main() {} diff --git a/src/test/ui/suggestions/call-on-unimplemented-fn-ptr.stderr b/src/test/ui/suggestions/call-on-unimplemented-fn-ptr.stderr new file mode 100644 index 0000000000000..167f7e592a97c --- /dev/null +++ b/src/test/ui/suggestions/call-on-unimplemented-fn-ptr.stderr @@ -0,0 +1,21 @@ +error[E0277]: the trait bound `fn() -> Foo: Bar` is not satisfied + --> $DIR/call-on-unimplemented-fn-ptr.rs:10:15 + | +LL | needs_bar(f); + | --------- ^ the trait `Bar` is not implemented for `fn() -> Foo` + | | + | required by a bound introduced by this call + | +note: required by a bound in `needs_bar` + --> $DIR/call-on-unimplemented-fn-ptr.rs:7:17 + | +LL | fn needs_bar(_: T) {} + | ^^^ required by this bound in `needs_bar` +help: use parentheses to call this function pointer + | +LL | needs_bar(f()); + | ++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr b/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr index fe603b67575d3..955148315baef 100644 --- a/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr +++ b/src/test/ui/suggestions/fn-ctor-passed-as-arg-where-it-should-have-been-called.stderr @@ -1,9 +1,6 @@ error[E0277]: the trait bound `fn() -> impl T {foo}: T` is not satisfied --> $DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:17:9 | -LL | fn foo() -> impl T { S } - | --- consider calling this function -... LL | bar(foo); | --- ^^^ the trait `T` is not implemented for fn item `fn() -> impl T {foo}` | | @@ -14,7 +11,7 @@ note: required by a bound in `bar` | LL | fn bar(f: impl T) {} | ^^^^^^^ required by this bound in `bar` -help: use parentheses to call the function +help: use parentheses to call this function | LL | bar(foo()); | ++ @@ -22,8 +19,6 @@ LL | bar(foo()); error[E0277]: the trait bound `[closure@$DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:18:19: 18:21]: T` is not satisfied --> $DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:19:9 | -LL | let closure = || S; - | -- consider calling this closure LL | bar(closure); | --- ^^^^^^^ the trait `T` is not implemented for closure `[closure@$DIR/fn-ctor-passed-as-arg-where-it-should-have-been-called.rs:18:19: 18:21]` | | @@ -34,7 +29,7 @@ note: required by a bound in `bar` | LL | fn bar(f: impl T) {} | ^^^^^^^ required by this bound in `bar` -help: use parentheses to call the closure +help: use parentheses to call this closure | LL | bar(closure()); | ++ From 35f157073238b9e0e97d9e9d17b4861103d87962 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Wed, 19 Oct 2022 02:53:47 +0000 Subject: [PATCH 5/5] instantiate -> construct --- .../rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs | 6 ++---- .../src/traits/error_reporting/suggestions.rs | 4 ++-- src/test/ui/issues/issue-35241.stderr | 2 +- src/test/ui/resolve/privacy-enum-ctor.stderr | 6 +++--- src/test/ui/suggestions/call-on-unimplemented-ctor.rs | 2 +- src/test/ui/suggestions/call-on-unimplemented-ctor.stderr | 2 +- .../ui/suggestions/fn-or-tuple-struct-without-args.stderr | 6 +++--- src/test/ui/typeck/issue-87181/empty-tuple-method.stderr | 2 +- src/test/ui/typeck/issue-87181/enum-variant.stderr | 2 +- src/test/ui/typeck/issue-87181/tuple-field.stderr | 2 +- 10 files changed, 16 insertions(+), 18 deletions(-) diff --git a/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs index 8c4fe75878fbb..08b21b82faf5a 100644 --- a/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs +++ b/compiler/rustc_hir_analysis/src/check/fn_ctxt/suggestions.rs @@ -102,10 +102,8 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let msg = match def_id_or_name { DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { - DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(), - DefKind::Ctor(CtorOf::Variant, _) => { - "instantiate this tuple variant".to_string() - } + DefKind::Ctor(CtorOf::Struct, _) => "construct this tuple struct".to_string(), + DefKind::Ctor(CtorOf::Variant, _) => "construct this tuple variant".to_string(), kind => format!("call this {}", kind.descr(def_id)), }, DefIdOrName::Name(name) => format!("call this {name}"), diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs index 98e32e2ce8595..8c41d9d240c70 100644 --- a/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs +++ b/compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs @@ -923,10 +923,10 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> { let msg = match def_id_or_name { DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { DefKind::Ctor(CtorOf::Struct, _) => { - "use parentheses to instantiate this tuple struct".to_string() + "use parentheses to construct this tuple struct".to_string() } DefKind::Ctor(CtorOf::Variant, _) => { - "use parentheses to instantiate this tuple variant".to_string() + "use parentheses to construct this tuple variant".to_string() } kind => format!("use parentheses to call this {}", kind.descr(def_id)), }, diff --git a/src/test/ui/issues/issue-35241.stderr b/src/test/ui/issues/issue-35241.stderr index 9ee7654a0885d..42a78ed97e025 100644 --- a/src/test/ui/issues/issue-35241.stderr +++ b/src/test/ui/issues/issue-35241.stderr @@ -11,7 +11,7 @@ LL | fn test() -> Foo { Foo } | = note: expected struct `Foo` found fn item `fn(u32) -> Foo {Foo}` -help: use parentheses to instantiate this tuple struct +help: use parentheses to construct this tuple struct | LL | fn test() -> Foo { Foo(/* u32 */) } | +++++++++++ diff --git a/src/test/ui/resolve/privacy-enum-ctor.stderr b/src/test/ui/resolve/privacy-enum-ctor.stderr index a369dc6db5281..82a4211f08ac3 100644 --- a/src/test/ui/resolve/privacy-enum-ctor.stderr +++ b/src/test/ui/resolve/privacy-enum-ctor.stderr @@ -327,7 +327,7 @@ LL | let _: Z = Z::Fn; | = note: expected enum `Z` found fn item `fn(u8) -> Z {Z::Fn}` -help: use parentheses to instantiate this tuple variant +help: use parentheses to construct this tuple variant | LL | let _: Z = Z::Fn(/* u8 */); | ++++++++++ @@ -362,7 +362,7 @@ LL | let _: E = m::E::Fn; | = note: expected enum `E` found fn item `fn(u8) -> E {E::Fn}` -help: use parentheses to instantiate this tuple variant +help: use parentheses to construct this tuple variant | LL | let _: E = m::E::Fn(/* u8 */); | ++++++++++ @@ -397,7 +397,7 @@ LL | let _: E = E::Fn; | = note: expected enum `E` found fn item `fn(u8) -> E {E::Fn}` -help: use parentheses to instantiate this tuple variant +help: use parentheses to construct this tuple variant | LL | let _: E = E::Fn(/* u8 */); | ++++++++++ diff --git a/src/test/ui/suggestions/call-on-unimplemented-ctor.rs b/src/test/ui/suggestions/call-on-unimplemented-ctor.rs index 0e7412807b465..5f811044eb349 100644 --- a/src/test/ui/suggestions/call-on-unimplemented-ctor.rs +++ b/src/test/ui/suggestions/call-on-unimplemented-ctor.rs @@ -2,7 +2,7 @@ fn main() { insert_resource(Marker); insert_resource(Time); //~^ ERROR the trait bound `fn(u32) -> Time {Time}: Resource` is not satisfied - //~| HELP use parentheses to instantiate this tuple struct + //~| HELP use parentheses to construct this tuple struct } trait Resource {} diff --git a/src/test/ui/suggestions/call-on-unimplemented-ctor.stderr b/src/test/ui/suggestions/call-on-unimplemented-ctor.stderr index 48f3366596f6b..58612cbfb239e 100644 --- a/src/test/ui/suggestions/call-on-unimplemented-ctor.stderr +++ b/src/test/ui/suggestions/call-on-unimplemented-ctor.stderr @@ -11,7 +11,7 @@ note: required by a bound in `insert_resource` | LL | fn insert_resource(resource: R) {} | ^^^^^^^^ required by this bound in `insert_resource` -help: use parentheses to instantiate this tuple struct +help: use parentheses to construct this tuple struct | LL | insert_resource(Time(/* u32 */)); | +++++++++++ diff --git a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr index f05dba1d4ca7e..597dc61c3f7e2 100644 --- a/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr +++ b/src/test/ui/suggestions/fn-or-tuple-struct-without-args.stderr @@ -49,7 +49,7 @@ LL | let _: S = S; | = note: expected struct `S` found fn item `fn(usize, usize) -> S {S}` -help: use parentheses to instantiate this tuple struct +help: use parentheses to construct this tuple struct | LL | let _: S = S(/* usize */, /* usize */); | ++++++++++++++++++++++++++ @@ -85,7 +85,7 @@ LL | let _: V = V; | = note: expected struct `V` found fn item `fn() -> V {V}` -help: use parentheses to instantiate this tuple struct +help: use parentheses to construct this tuple struct | LL | let _: V = V(); | ++ @@ -139,7 +139,7 @@ LL | let _: E = E::A; | = note: expected enum `E` found fn item `fn(usize) -> E {E::A}` -help: use parentheses to instantiate this tuple variant +help: use parentheses to construct this tuple variant | LL | let _: E = E::A(/* usize */); | +++++++++++++ diff --git a/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr b/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr index a18c54a29b52c..23e7b7cc363fe 100644 --- a/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr +++ b/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr @@ -4,7 +4,7 @@ error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo}` in the LL | thing.bar.foo(); | ^^^ method not found in `fn() -> Foo {Foo}` | -help: use parentheses to instantiate this tuple struct +help: use parentheses to construct this tuple struct | LL | (thing.bar)().foo(); | + +++ diff --git a/src/test/ui/typeck/issue-87181/enum-variant.stderr b/src/test/ui/typeck/issue-87181/enum-variant.stderr index 90641410d8e96..2247ea27021f4 100644 --- a/src/test/ui/typeck/issue-87181/enum-variant.stderr +++ b/src/test/ui/typeck/issue-87181/enum-variant.stderr @@ -4,7 +4,7 @@ error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` i LL | thing.bar.foo(); | ^^^ method not found in `fn() -> Foo {Foo::Tup}` | -help: use parentheses to instantiate this tuple variant +help: use parentheses to construct this tuple variant | LL | (thing.bar)().foo(); | + +++ diff --git a/src/test/ui/typeck/issue-87181/tuple-field.stderr b/src/test/ui/typeck/issue-87181/tuple-field.stderr index c1ca26ee9af1d..0a7d30b615a63 100644 --- a/src/test/ui/typeck/issue-87181/tuple-field.stderr +++ b/src/test/ui/typeck/issue-87181/tuple-field.stderr @@ -4,7 +4,7 @@ error[E0609]: no field `0` on type `fn(char, u16) -> Foo {Foo}` LL | thing.bar.0; | ^ | -help: use parentheses to instantiate this tuple struct +help: use parentheses to construct this tuple struct | LL | (thing.bar)(/* char */, /* u16 */).0; | + ++++++++++++++++++++++++