diff --git a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs index fd78b483b75..b5ee4a5edce 100644 --- a/compiler/rustc_borrowck/src/diagnostics/region_errors.rs +++ b/compiler/rustc_borrowck/src/diagnostics/region_errors.rs @@ -1,17 +1,17 @@ //! Error reporting machinery for lifetime errors. -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; +use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; use rustc_infer::infer::{ - error_reporting::nice_region_error::NiceRegionError, - error_reporting::unexpected_hidden_region_diagnostic, NllRegionVariableOrigin, - RelateParamBound, + error_reporting::nice_region_error::{self, find_param_with_region, NiceRegionError}, + error_reporting::unexpected_hidden_region_diagnostic, + NllRegionVariableOrigin, RelateParamBound, }; use rustc_middle::hir::place::PlaceBase; use rustc_middle::mir::{ConstraintCategory, ReturnConstraint}; -use rustc_middle::ty::subst::{InternalSubsts, Subst}; +use rustc_middle::ty::subst::InternalSubsts; use rustc_middle::ty::{self, RegionVid, Ty}; -use rustc_span::symbol::{kw, sym}; -use rustc_span::{BytePos, Span}; +use rustc_span::symbol::sym; +use rustc_span::Span; use crate::borrowck_errors; @@ -651,82 +651,47 @@ fn add_static_impl_trait_suggestion( fr_name: RegionName, outlived_fr: RegionVid, ) { - if let (Some(f), Some(ty::ReStatic)) = - (self.to_error_region(fr), self.to_error_region(outlived_fr).as_deref()) + if let (Some(f), Some(outlived_f)) = + (self.to_error_region(fr), self.to_error_region(outlived_fr)) { - if let Some(&ty::Opaque(did, substs)) = self + if *outlived_f != ty::ReStatic { + return; + } + + let fn_returns = self .infcx .tcx .is_suitable_region(f) - .map(|r| r.def_id) - .and_then(|id| self.infcx.tcx.return_type_impl_trait(id)) - .map(|(ty, _)| ty.kind()) - { - // Check whether or not the impl trait return type is intended to capture - // data with the static lifetime. - // - // eg. check for `impl Trait + 'static` instead of `impl Trait`. - let has_static_predicate = { - let bounds = self.infcx.tcx.explicit_item_bounds(did); + .map(|r| self.infcx.tcx.return_type_impl_or_dyn_traits(r.def_id)) + .unwrap_or_default(); - let mut found = false; - for (bound, _) in bounds { - if let ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_, r)) = - bound.kind().skip_binder() - { - let r = r.subst(self.infcx.tcx, substs); - if r.is_static() { - found = true; - break; - } else { - // If there's already a lifetime bound, don't - // suggest anything. - return; - } - } - } - - found - }; - - debug!( - "add_static_impl_trait_suggestion: has_static_predicate={:?}", - has_static_predicate - ); - let static_str = kw::StaticLifetime; - // If there is a static predicate, then the only sensible suggestion is to replace - // fr with `'static`. - if has_static_predicate { - diag.help(&format!("consider replacing `{fr_name}` with `{static_str}`")); - } else { - // Otherwise, we should suggest adding a constraint on the return type. - let span = self.infcx.tcx.def_span(did); - if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) { - let suggestable_fr_name = if fr_name.was_named() { - fr_name.to_string() - } else { - "'_".to_string() - }; - let span = if snippet.ends_with(';') { - // `type X = impl Trait;` - span.with_hi(span.hi() - BytePos(1)) - } else { - span - }; - let suggestion = format!(" + {suggestable_fr_name}"); - let span = span.shrink_to_hi(); - diag.span_suggestion( - span, - &format!( - "to allow this `impl Trait` to capture borrowed data with lifetime \ - `{fr_name}`, add `{suggestable_fr_name}` as a bound", - ), - suggestion, - Applicability::MachineApplicable, - ); - } - } + if fn_returns.is_empty() { + return; } + + let param = if let Some(param) = find_param_with_region(self.infcx.tcx, f, outlived_f) { + param + } else { + return; + }; + + let lifetime = if f.has_name() { fr_name.to_string() } else { "'_".to_string() }; + + let arg = match param.param.pat.simple_ident() { + Some(simple_ident) => format!("argument `{}`", simple_ident), + None => "the argument".to_string(), + }; + let captures = format!("captures data from {}", arg); + + return nice_region_error::suggest_new_region_bound( + self.infcx.tcx, + diag, + fn_returns, + lifetime, + Some(arg), + captures, + Some((param.param_ty_span, param.param_ty.to_string())), + ); } } } diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs index ef56093bed8..2d01673b61d 100644 --- a/compiler/rustc_hir/src/hir.rs +++ b/compiler/rustc_hir/src/hir.rs @@ -3314,6 +3314,12 @@ pub fn fn_kind(self) -> Option> { _ => None, } } + + /// Get the fields for the tuple-constructor, + /// if this node is a tuple constructor, otherwise None + pub fn tuple_fields(&self) -> Option<&'hir [FieldDef<'hir>]> { + if let Node::Ctor(&VariantData::Tuple(fields, _)) = self { Some(fields) } else { None } + } } // Some nodes are used a lot. Make sure they don't unintentionally get bigger. diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs index df81aea6ef9..79f852d8a95 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/mod.rs @@ -15,6 +15,7 @@ mod util; pub use static_impl_trait::suggest_new_region_bound; +pub use util::find_param_with_region; impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> { pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool { diff --git a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs index 719f6b37a43..7d3ed2ed38a 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/nice_region_error/util.rs @@ -2,6 +2,7 @@ //! anonymous regions. use crate::infer::error_reporting::nice_region_error::NiceRegionError; +use crate::infer::TyCtxt; use rustc_hir as hir; use rustc_hir::def_id::LocalDefId; use rustc_middle::ty::{self, Binder, DefIdTree, Region, Ty, TypeFoldable}; @@ -9,7 +10,7 @@ /// Information about the anonymous region we are searching for. #[derive(Debug)] -pub(super) struct AnonymousParamInfo<'tcx> { +pub struct AnonymousParamInfo<'tcx> { /// The parameter corresponding to the anonymous region. pub param: &'tcx hir::Param<'tcx>, /// The type corresponding to the anonymous region parameter. @@ -22,76 +23,83 @@ pub(super) struct AnonymousParamInfo<'tcx> { pub is_first: bool, } +// This method walks the Type of the function body parameters using +// `fold_regions()` function and returns the +// &hir::Param of the function parameter corresponding to the anonymous +// region and the Ty corresponding to the named region. +// Currently only the case where the function declaration consists of +// one named region and one anonymous region is handled. +// Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32` +// Here, we would return the hir::Param for y, we return the type &'a +// i32, which is the type of y but with the anonymous region replaced +// with 'a, the corresponding bound region and is_first which is true if +// the hir::Param is the first parameter in the function declaration. +pub fn find_param_with_region<'tcx>( + tcx: TyCtxt<'tcx>, + anon_region: Region<'tcx>, + replace_region: Region<'tcx>, +) -> Option> { + let (id, bound_region) = match *anon_region { + ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region), + ty::ReEarlyBound(ebr) => { + (tcx.parent(ebr.def_id).unwrap(), ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name)) + } + _ => return None, // not a free region + }; + + let hir = &tcx.hir(); + let hir_id = hir.local_def_id_to_hir_id(id.as_local()?); + let body_id = hir.maybe_body_owned_by(hir_id)?; + let body = hir.body(body_id); + let owner_id = hir.body_owner(body_id); + let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap(); + let poly_fn_sig = tcx.fn_sig(id); + let fn_sig = tcx.liberate_late_bound_regions(id, poly_fn_sig); + body.params + .iter() + .take(if fn_sig.c_variadic { + fn_sig.inputs().len() + } else { + assert_eq!(fn_sig.inputs().len(), body.params.len()); + body.params.len() + }) + .enumerate() + .find_map(|(index, param)| { + // May return None; sometimes the tables are not yet populated. + let ty = fn_sig.inputs()[index]; + let mut found_anon_region = false; + let new_param_ty = tcx.fold_regions(ty, &mut false, |r, _| { + if r == anon_region { + found_anon_region = true; + replace_region + } else { + r + } + }); + if found_anon_region { + let ty_hir_id = fn_decl.inputs[index].hir_id; + let param_ty_span = hir.span(ty_hir_id); + let is_first = index == 0; + Some(AnonymousParamInfo { + param, + param_ty: new_param_ty, + param_ty_span, + bound_region, + is_first, + }) + } else { + None + } + }) +} + impl<'a, 'tcx> NiceRegionError<'a, 'tcx> { - // This method walks the Type of the function body parameters using - // `fold_regions()` function and returns the - // &hir::Param of the function parameter corresponding to the anonymous - // region and the Ty corresponding to the named region. - // Currently only the case where the function declaration consists of - // one named region and one anonymous region is handled. - // Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32` - // Here, we would return the hir::Param for y, we return the type &'a - // i32, which is the type of y but with the anonymous region replaced - // with 'a, the corresponding bound region and is_first which is true if - // the hir::Param is the first parameter in the function declaration. pub(super) fn find_param_with_region( &self, anon_region: Region<'tcx>, replace_region: Region<'tcx>, ) -> Option> { - let (id, bound_region) = match *anon_region { - ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region), - ty::ReEarlyBound(ebr) => ( - self.tcx().parent(ebr.def_id).unwrap(), - ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name), - ), - _ => return None, // not a free region - }; - - let hir = &self.tcx().hir(); - let hir_id = hir.local_def_id_to_hir_id(id.as_local()?); - let body_id = hir.maybe_body_owned_by(hir_id)?; - let body = hir.body(body_id); - let owner_id = hir.body_owner(body_id); - let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap(); - let poly_fn_sig = self.tcx().fn_sig(id); - let fn_sig = self.tcx().liberate_late_bound_regions(id, poly_fn_sig); - body.params - .iter() - .take(if fn_sig.c_variadic { - fn_sig.inputs().len() - } else { - assert_eq!(fn_sig.inputs().len(), body.params.len()); - body.params.len() - }) - .enumerate() - .find_map(|(index, param)| { - // May return None; sometimes the tables are not yet populated. - let ty = fn_sig.inputs()[index]; - let mut found_anon_region = false; - let new_param_ty = self.tcx().fold_regions(ty, &mut false, |r, _| { - if r == anon_region { - found_anon_region = true; - replace_region - } else { - r - } - }); - if found_anon_region { - let ty_hir_id = fn_decl.inputs[index].hir_id; - let param_ty_span = hir.span(ty_hir_id); - let is_first = index == 0; - Some(AnonymousParamInfo { - param, - param_ty: new_param_ty, - param_ty_span, - bound_region, - is_first, - }) - } else { - None - } - }) + find_param_with_region(self.tcx(), anon_region, replace_region) } // Here, we check for the case where the anonymous region diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 1cd0ace8adb..1caf93e5fe0 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -2681,21 +2681,21 @@ fn suggest_trait_fn_ty_for_impl_fn_infer( let trait_ref = self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty)); - let x: &ty::AssocItem = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind( + let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind( tcx, *ident, ty::AssocKind::Fn, trait_ref.def_id, )?; - let fn_sig = tcx.fn_sig(x.def_id).subst( + let fn_sig = tcx.fn_sig(assoc.def_id).subst( tcx, - trait_ref.substs.extend_to(tcx, x.def_id, |param, _| tcx.mk_param_from_def(param)), + trait_ref.substs.extend_to(tcx, assoc.def_id, |param, _| tcx.mk_param_from_def(param)), ); let ty = if let Some(arg_idx) = arg_idx { fn_sig.input(arg_idx) } else { fn_sig.output() }; - Some(tcx.erase_late_bound_regions(ty)) + Some(tcx.liberate_late_bound_regions(fn_hir_id.expect_owner().to_def_id(), ty)) } fn validate_late_bound_regions( diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs index 480a5512249..47cb1ea48cb 100644 --- a/compiler/rustc_typeck/src/check/expr.rs +++ b/compiler/rustc_typeck/src/check/expr.rs @@ -21,11 +21,13 @@ }; use crate::type_error_struct; +use super::suggest_call_constructor; use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; use rustc_ast as ast; use rustc_data_structures::fx::FxHashMap; use rustc_data_structures::stack::ensure_sufficient_stack; use rustc_errors::Diagnostic; +use rustc_errors::EmissionGuarantee; use rustc_errors::ErrorGuaranteed; use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId}; use rustc_hir as hir; @@ -1986,6 +1988,26 @@ fn check_field( self.tcx().ty_error() } + fn check_call_constructor( + &self, + err: &mut DiagnosticBuilder<'_, G>, + base: &'tcx hir::Expr<'tcx>, + def_id: DefId, + ) { + let local_id = def_id.expect_local(); + let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id); + let node = self.tcx.hir().get(hir_id); + + if let Some(fields) = node.tuple_fields() { + let kind = match self.tcx.opt_def_kind(local_id) { + Some(DefKind::Ctor(of, _)) => of, + _ => return, + }; + + suggest_call_constructor(base.span, kind, fields.len(), err); + } + } + fn suggest_await_on_field_access( &self, err: &mut Diagnostic, @@ -2055,6 +2077,9 @@ fn ban_nonexisting_field( ty::Opaque(_, _) => { self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs()); } + ty::FnDef(def_id, _) => { + self.check_call_constructor(&mut err, base, def_id); + } _ => {} } diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs index 88e0a4bada8..80c31355fe7 100644 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ b/compiler/rustc_typeck/src/check/method/suggest.rs @@ -8,6 +8,7 @@ MultiSpan, }; use rustc_hir as hir; +use rustc_hir::def::DefKind; use rustc_hir::def_id::DefId; use rustc_hir::lang_items::LangItem; use rustc_hir::{ExprKind, Node, QPath}; @@ -29,7 +30,7 @@ use std::iter; use super::probe::{Mode, ProbeScope}; -use super::{CandidateSource, MethodError, NoMatchData}; +use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData}; impl<'a, 'tcx> FnCtxt<'a, 'tcx> { fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { @@ -271,207 +272,84 @@ pub fn report_method_error( (None, true) => "variant", } }; - // FIXME(eddyb) this indentation is probably unnecessary. - let mut err = { - // Suggest clamping down the type if the method that is being attempted to - // be used exists at all, and the type is an ambiguous numeric type - // ({integer}/{float}). - let mut candidates = all_traits(self.tcx) - .into_iter() - .filter_map(|info| self.associated_value(info.def_id, item_name)); - // There are methods that are defined on the primitive types and won't be - // found when exploring `all_traits`, but we also need them to be accurate on - // our suggestions (#47759). - let found_assoc = |ty: Ty<'tcx>| { - simplify_type(tcx, ty, TreatParams::AsPlaceholders) - .and_then(|simp| { - tcx.incoherent_impls(simp) + + if self.suggest_constraining_numerical_ty( + tcx, actual, source, span, item_kind, item_name, &ty_str, + ) { + return None; + } + + span = item_name.span; + + // Don't show generic arguments when the method can't be found in any implementation (#81576). + let mut ty_str_reported = ty_str.clone(); + if let ty::Adt(_, generics) = actual.kind() { + if generics.len() > 0 { + let mut autoderef = self.autoderef(span, actual); + let candidate_found = autoderef.any(|(ty, _)| { + if let ty::Adt(adt_deref, _) = ty.kind() { + self.tcx + .inherent_impls(adt_deref.did()) .iter() - .find_map(|&id| self.associated_value(id, item_name)) - }) - .is_some() - }; - let found_candidate = candidates.next().is_some() - || found_assoc(tcx.types.i8) - || found_assoc(tcx.types.i16) - || found_assoc(tcx.types.i32) - || found_assoc(tcx.types.i64) - || found_assoc(tcx.types.i128) - || found_assoc(tcx.types.u8) - || found_assoc(tcx.types.u16) - || found_assoc(tcx.types.u32) - || found_assoc(tcx.types.u64) - || found_assoc(tcx.types.u128) - || found_assoc(tcx.types.f32) - || found_assoc(tcx.types.f32); - if let (true, false, SelfSource::MethodCall(expr), true) = ( - actual.is_numeric(), - actual.has_concrete_skeleton(), - source, - found_candidate, - ) { - let mut err = struct_span_err!( - tcx.sess, - span, - E0689, - "can't call {} `{}` on ambiguous numeric type `{}`", - item_kind, - item_name, - ty_str - ); - let concrete_type = if actual.is_integral() { "i32" } else { "f32" }; - match expr.kind { - ExprKind::Lit(ref lit) => { - // numeric literal - let snippet = tcx - .sess - .source_map() - .span_to_snippet(lit.span) - .unwrap_or_else(|_| "".to_owned()); - - // If this is a floating point literal that ends with '.', - // get rid of it to stop this from becoming a member access. - let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); - - err.span_suggestion( - lit.span, - &format!( - "you must specify a concrete type for this numeric value, \ - like `{}`", - concrete_type - ), - format!("{snippet}_{concrete_type}"), - Applicability::MaybeIncorrect, - ); + .filter_map(|def_id| self.associated_value(*def_id, item_name)) + .count() + >= 1 + } else { + false } - ExprKind::Path(QPath::Resolved(_, path)) => { - // local binding - if let hir::def::Res::Local(hir_id) = path.res { - let span = tcx.hir().span(hir_id); - let snippet = tcx.sess.source_map().span_to_snippet(span); - let filename = tcx.sess.source_map().span_to_filename(span); - - let parent_node = - self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)); - let msg = format!( - "you must specify a type for this binding, like `{}`", - concrete_type, - ); - - match (filename, parent_node, snippet) { - ( - FileName::Real(_), - Node::Local(hir::Local { - source: hir::LocalSource::Normal, - ty, - .. - }), - Ok(ref snippet), - ) => { - err.span_suggestion( - // account for `let x: _ = 42;` - // ^^^^ - span.to(ty - .as_ref() - .map(|ty| ty.span) - .unwrap_or(span)), - &msg, - format!("{}: {}", snippet, concrete_type), - Applicability::MaybeIncorrect, - ); - } - _ => { - err.span_label(span, msg); - } - } - } - } - _ => {} - } - err.emit(); - return None; - } else { - span = item_name.span; - - // Don't show generic arguments when the method can't be found in any implementation (#81576). - let mut ty_str_reported = ty_str.clone(); - if let ty::Adt(_, generics) = actual.kind() { - if generics.len() > 0 { - let mut autoderef = self.autoderef(span, actual); - let candidate_found = autoderef.any(|(ty, _)| { - if let ty::Adt(adt_deref, _) = ty.kind() { - self.tcx - .inherent_impls(adt_deref.did()) - .iter() - .filter_map(|def_id| { - self.associated_value(*def_id, item_name) - }) - .count() - >= 1 - } else { - false - } - }); - let has_deref = autoderef.step_count() > 0; - if !candidate_found - && !has_deref - && unsatisfied_predicates.is_empty() - { - if let Some((path_string, _)) = ty_str.split_once('<') { - ty_str_reported = path_string.to_string(); - } - } + }); + let has_deref = autoderef.step_count() > 0; + if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() { + if let Some((path_string, _)) = ty_str.split_once('<') { + ty_str_reported = path_string.to_string(); } } - - let mut err = struct_span_err!( - tcx.sess, - span, - E0599, - "no {} named `{}` found for {} `{}` in the current scope", - item_kind, - item_name, - actual.prefix_string(self.tcx), - ty_str_reported, - ); - if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { - self.suggest_await_before_method( - &mut err, item_name, actual, cal, span, - ); - } - if let Some(span) = - tcx.resolutions(()).confused_type_with_std_module.get(&span) - { - if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) { - err.span_suggestion( - *span, - "you are looking for the module in `std`, \ - not the primitive type", - format!("std::{}", snippet), - Applicability::MachineApplicable, - ); - } - } - if let ty::RawPtr(_) = &actual.kind() { - err.note( - "try using `<*const T>::as_ref()` to get a reference to the \ - type behind the pointer: https://doc.rust-lang.org/std/\ - primitive.pointer.html#method.as_ref", - ); - err.note( - "using `<*const T>::as_ref()` on a pointer \ - which is unaligned or points to invalid \ - or uninitialized memory is undefined behavior", - ); - } - err } - }; + } + let mut err = struct_span_err!( + tcx.sess, + span, + E0599, + "no {} named `{}` found for {} `{}` in the current scope", + item_kind, + item_name, + actual.prefix_string(self.tcx), + ty_str_reported, + ); if actual.references_error() { err.downgrade_to_delayed_bug(); } + if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { + self.suggest_await_before_method( + &mut err, item_name, actual, cal, span, + ); + } + if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) { + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) { + err.span_suggestion( + *span, + "you are looking for the module in `std`, \ + not the primitive type", + format!("std::{}", snippet), + Applicability::MachineApplicable, + ); + } + } + if let ty::RawPtr(_) = &actual.kind() { + err.note( + "try using `<*const T>::as_ref()` to get a reference to the \ + type behind the pointer: https://doc.rust-lang.org/std/\ + primitive.pointer.html#method.as_ref", + ); + err.note( + "using `<*const T>::as_ref()` on a pointer \ + which is unaligned or points to invalid \ + or uninitialized memory is undefined behavior", + ); + } + if let Some(def) = actual.ty_adt_def() { if let Some(full_sp) = tcx.hir().span_if_local(def.did()) { let def_sp = tcx.sess.source_map().guess_head_span(full_sp); @@ -488,19 +366,32 @@ pub fn report_method_error( } if self.is_fn_ty(rcvr_ty, span) { - fn report_function(err: &mut Diagnostic, name: T) { - err.note( - &format!("`{}` is a function, perhaps you wish to call it", name,), - ); - } - if let SelfSource::MethodCall(expr) = source { - if let Ok(expr_string) = tcx.sess.source_map().span_to_snippet(expr.span) { - report_function(&mut err, expr_string); - } else if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind { - if let Some(segment) = path.segments.last() { - report_function(&mut err, segment.ident); + let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() { + let local_id = def_id.expect_local(); + let hir_id = tcx.hir().local_def_id_to_hir_id(local_id); + let node = tcx.hir().get(hir_id); + let fields = node.tuple_fields(); + + if let Some(fields) = fields + && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) { + Some((fields, of)) + } else { + None } + } else { + None + }; + + // If the function is a tuple constructor, we recommend that they call it + if let Some((fields, kind)) = suggest { + suggest_call_constructor(expr.span, kind, fields.len(), &mut err); + } else { + // General case + err.span_label( + expr.span, + "this is a function, perhaps you wish to call it", + ); } } } @@ -985,7 +876,7 @@ trait bound{s}", } } - let mut label_span_not_found = || { + let label_span_not_found = |err: &mut DiagnosticBuilder<'_, _>| { if unsatisfied_predicates.is_empty() { err.span_label(span, format!("{item_kind} not found in `{ty_str}`")); let is_string_or_ref_str = match actual.kind() { @@ -1071,62 +962,14 @@ trait bound{s}", // If the method name is the name of a field with a function or closure type, // give a helping note that it has to be called as `(x.f)(...)`. if let SelfSource::MethodCall(expr) = source { - let field_receiver = - self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() { - ty::Adt(def, substs) if !def.is_enum() => { - let variant = &def.non_enum_variant(); - self.tcx.find_field_index(item_name, variant).map(|index| { - let field = &variant.fields[index]; - let field_ty = field.ty(tcx, substs); - (field, field_ty) - }) - } - _ => None, - }); - - if let Some((field, field_ty)) = field_receiver { - let scope = self.tcx.parent_module(self.body_id).to_def_id(); - let is_accessible = field.vis.is_accessible_from(scope, self.tcx); - - if is_accessible { - if self.is_fn_ty(field_ty, span) { - let expr_span = expr.span.to(item_name.span); - err.multipart_suggestion( - &format!( - "to call the function stored in `{}`, \ - surround the field access with parentheses", - item_name, - ), - vec![ - (expr_span.shrink_to_lo(), '('.to_string()), - (expr_span.shrink_to_hi(), ')'.to_string()), - ], - Applicability::MachineApplicable, - ); - } else { - let call_expr = self - .tcx - .hir() - .expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); - - if let Some(span) = call_expr.span.trim_start(item_name.span) { - err.span_suggestion( - span, - "remove the arguments", - String::new(), - Applicability::MaybeIncorrect, - ); - } - } - } - - let field_kind = if is_accessible { "field" } else { "private field" }; - err.span_label(item_name.span, format!("{}, not a method", field_kind)); - } else if lev_candidate.is_none() && !custom_span_label { - label_span_not_found(); + if !self.suggest_field_call(span, rcvr_ty, expr, item_name, &mut err) + && lev_candidate.is_none() + && !custom_span_label + { + label_span_not_found(&mut err); } } else if !custom_span_label { - label_span_not_found(); + label_span_not_found(&mut err); } if let SelfSource::MethodCall(expr) = source @@ -1313,6 +1156,187 @@ trait bound{s}", None } + fn suggest_field_call( + &self, + span: Span, + rcvr_ty: Ty<'tcx>, + expr: &hir::Expr<'_>, + item_name: Ident, + err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>, + ) -> bool { + let tcx = self.tcx; + let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() { + ty::Adt(def, substs) if !def.is_enum() => { + let variant = &def.non_enum_variant(); + tcx.find_field_index(item_name, variant).map(|index| { + let field = &variant.fields[index]; + let field_ty = field.ty(tcx, substs); + (field, field_ty) + }) + } + _ => None, + }); + if let Some((field, field_ty)) = field_receiver { + let scope = tcx.parent_module(self.body_id).to_def_id(); + let is_accessible = field.vis.is_accessible_from(scope, tcx); + + if is_accessible { + if self.is_fn_ty(field_ty, span) { + let expr_span = expr.span.to(item_name.span); + err.multipart_suggestion( + &format!( + "to call the function stored in `{}`, \ + surround the field access with parentheses", + item_name, + ), + vec![ + (expr_span.shrink_to_lo(), '('.to_string()), + (expr_span.shrink_to_hi(), ')'.to_string()), + ], + Applicability::MachineApplicable, + ); + } else { + let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id)); + + if let Some(span) = call_expr.span.trim_start(item_name.span) { + err.span_suggestion( + span, + "remove the arguments", + String::new(), + Applicability::MaybeIncorrect, + ); + } + } + } + + let field_kind = if is_accessible { "field" } else { "private field" }; + err.span_label(item_name.span, format!("{}, not a method", field_kind)); + return true; + } + false + } + + fn suggest_constraining_numerical_ty( + &self, + tcx: TyCtxt<'tcx>, + actual: Ty<'tcx>, + source: SelfSource<'_>, + span: Span, + item_kind: &str, + item_name: Ident, + ty_str: &str, + ) -> bool { + let found_candidate = all_traits(self.tcx) + .into_iter() + .any(|info| self.associated_value(info.def_id, item_name).is_some()); + let found_assoc = |ty: Ty<'tcx>| { + simplify_type(tcx, ty, TreatParams::AsPlaceholders) + .and_then(|simp| { + tcx.incoherent_impls(simp) + .iter() + .find_map(|&id| self.associated_value(id, item_name)) + }) + .is_some() + }; + let found_candidate = found_candidate + || found_assoc(tcx.types.i8) + || found_assoc(tcx.types.i16) + || found_assoc(tcx.types.i32) + || found_assoc(tcx.types.i64) + || found_assoc(tcx.types.i128) + || found_assoc(tcx.types.u8) + || found_assoc(tcx.types.u16) + || found_assoc(tcx.types.u32) + || found_assoc(tcx.types.u64) + || found_assoc(tcx.types.u128) + || found_assoc(tcx.types.f32) + || found_assoc(tcx.types.f32); + if found_candidate + && actual.is_numeric() + && !actual.has_concrete_skeleton() + && let SelfSource::MethodCall(expr) = source + { + let mut err = struct_span_err!( + tcx.sess, + span, + E0689, + "can't call {} `{}` on ambiguous numeric type `{}`", + item_kind, + item_name, + ty_str + ); + let concrete_type = if actual.is_integral() { "i32" } else { "f32" }; + match expr.kind { + ExprKind::Lit(ref lit) => { + // numeric literal + let snippet = tcx + .sess + .source_map() + .span_to_snippet(lit.span) + .unwrap_or_else(|_| "".to_owned()); + + // If this is a floating point literal that ends with '.', + // get rid of it to stop this from becoming a member access. + let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); + + err.span_suggestion( + lit.span, + &format!( + "you must specify a concrete type for this numeric value, \ + like `{}`", + concrete_type + ), + format!("{snippet}_{concrete_type}"), + Applicability::MaybeIncorrect, + ); + } + ExprKind::Path(QPath::Resolved(_, path)) => { + // local binding + if let hir::def::Res::Local(hir_id) = path.res { + let span = tcx.hir().span(hir_id); + let snippet = tcx.sess.source_map().span_to_snippet(span); + let filename = tcx.sess.source_map().span_to_filename(span); + + let parent_node = + self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)); + let msg = format!( + "you must specify a type for this binding, like `{}`", + concrete_type, + ); + + match (filename, parent_node, snippet) { + ( + FileName::Real(_), + Node::Local(hir::Local { + source: hir::LocalSource::Normal, + ty, + .. + }), + Ok(ref snippet), + ) => { + err.span_suggestion( + // account for `let x: _ = 42;` + // ^^^^ + span.to(ty.as_ref().map(|ty| ty.span).unwrap_or(span)), + &msg, + format!("{}: {}", snippet, concrete_type), + Applicability::MaybeIncorrect, + ); + } + _ => { + err.span_label(span, msg); + } + } + } + } + _ => {} + } + err.emit(); + return true; + } + false + } + crate fn note_unmet_impls_on_type( &self, err: &mut Diagnostic, diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs index 0d5e7b28a4e..f7bb30cd13e 100644 --- a/compiler/rustc_typeck/src/check/mod.rs +++ b/compiler/rustc_typeck/src/check/mod.rs @@ -98,12 +98,15 @@ pub use diverges::Diverges; pub use expectation::Expectation; pub use fn_ctxt::*; +use hir::def::CtorOf; pub use inherited::{Inherited, InheritedBuilder}; use crate::astconv::AstConv; use crate::check::gather_locals::GatherLocalsVisitor; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan}; +use rustc_errors::{ + pluralize, struct_span_err, Applicability, DiagnosticBuilder, EmissionGuarantee, MultiSpan, +}; use rustc_hir as hir; use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -988,3 +991,36 @@ fn has_expected_num_generic_args<'tcx>( generics.count() == expected + if generics.has_self { 1 } else { 0 } }) } + +/// Suggests calling the constructor of a tuple struct or enum variant +/// +/// * `snippet` - The snippet of code that references the constructor +/// * `span` - The span of the snippet +/// * `params` - The number of parameters the constructor accepts +/// * `err` - A mutable diagnostic builder to add the suggestion to +fn suggest_call_constructor( + span: Span, + kind: CtorOf, + params: usize, + err: &mut DiagnosticBuilder<'_, G>, +) { + // Note: tuple-structs don't have named fields, so just use placeholders + let args = vec!["_"; params].join(", "); + let applicable = if params > 0 { + Applicability::HasPlaceholders + } else { + // When n = 0, it's an empty-tuple struct/enum variant + // so we trivially know how to construct it + Applicability::MachineApplicable + }; + let kind = match kind { + CtorOf::Struct => "a struct", + CtorOf::Variant => "an enum variant", + }; + err.span_label(span, &format!("this is the constructor of {kind}")); + err.multipart_suggestion( + "call the constructor", + vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), format!(")({args})"))], + applicable, + ); +} diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index d3545236e3d..118807a8286 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -18,6 +18,7 @@ use rustc_middle::ty; use rustc_middle::ty::DefIdTree; use rustc_middle::ty::TyCtxt; +use rustc_span::symbol::kw; use rustc_span::{sym, Symbol}; use rustc_target::spec::abi::Abi; @@ -679,7 +680,7 @@ fn resolved_path<'cx>( if print_all { for seg in &path.segments[..path.segments.len() - 1] { - write!(w, "{}::", seg.name)?; + write!(w, "{}::", if seg.name == kw::PathRoot { "" } else { seg.name.as_str() })?; } } if w.alternate() { diff --git a/src/librustdoc/html/static/js/search.js b/src/librustdoc/html/static/js/search.js index c4e74ea0657..c1d2ec540b0 100644 --- a/src/librustdoc/html/static/js/search.js +++ b/src/librustdoc/html/static/js/search.js @@ -310,10 +310,20 @@ window.initSearch = function(rawSearchIndex) { */ function getIdentEndPosition(parserState) { let end = parserState.pos; + let foundExclamation = false; while (parserState.pos < parserState.length) { const c = parserState.userQuery[parserState.pos]; if (!isIdentCharacter(c)) { - if (isErrorCharacter(c)) { + if (c === "!") { + if (foundExclamation) { + throw new Error("Cannot have more than one `!` in an ident"); + } else if (parserState.pos + 1 < parserState.length && + isIdentCharacter(parserState.userQuery[parserState.pos + 1])) + { + throw new Error("`!` can only be at the end of an ident"); + } + foundExclamation = true; + } else if (isErrorCharacter(c)) { throw new Error(`Unexpected \`${c}\``); } else if ( isStopCharacter(c) || @@ -329,6 +339,7 @@ window.initSearch = function(rawSearchIndex) { } // Skip current ":". parserState.pos += 1; + foundExclamation = false; } else { throw new Error(`Unexpected \`${c}\``); } @@ -591,7 +602,7 @@ window.initSearch = function(rawSearchIndex) { * * The supported syntax by this parser is as follow: * - * ident = *(ALPHA / DIGIT / "_") + * ident = *(ALPHA / DIGIT / "_") [!] * path = ident *(DOUBLE-COLON ident) * arg = path [generics] * arg-without-generic = path diff --git a/src/test/rustdoc-js-std/parser-errors.js b/src/test/rustdoc-js-std/parser-errors.js index 779ab867c12..dc42031e05f 100644 --- a/src/test/rustdoc-js-std/parser-errors.js +++ b/src/test/rustdoc-js-std/parser-errors.js @@ -35,6 +35,8 @@ const QUERY = [ "a,:", " a<> :", "mod : :", + "a!a", + "a!!", ]; const PARSED = [ @@ -362,4 +364,22 @@ const PARSED = [ userQuery: "mod : :", error: 'Unexpected `:`', }, + { + elems: [], + foundElems: 0, + original: "a!a", + returned: [], + typeFilter: -1, + userQuery: "a!a", + error: '`!` can only be at the end of an ident', + }, + { + elems: [], + foundElems: 0, + original: "a!!", + returned: [], + typeFilter: -1, + userQuery: "a!!", + error: 'Cannot have more than one `!` in an ident', + }, ]; diff --git a/src/test/rustdoc-js-std/parser-ident.js b/src/test/rustdoc-js-std/parser-ident.js new file mode 100644 index 00000000000..4b5ab01ac76 --- /dev/null +++ b/src/test/rustdoc-js-std/parser-ident.js @@ -0,0 +1,93 @@ +const QUERY = [ + "R", + "!", + "a!", + "a!::b", + "a!::b!", +]; + +const PARSED = [ + { + elems: [{ + name: "r", + fullPath: ["r"], + pathWithoutLast: [], + pathLast: "r", + generics: [ + { + name: "!", + fullPath: ["!"], + pathWithoutLast: [], + pathLast: "!", + generics: [], + }, + ], + }], + foundElems: 1, + original: "R", + returned: [], + typeFilter: -1, + userQuery: "r", + error: null, + }, + { + elems: [{ + name: "!", + fullPath: ["!"], + pathWithoutLast: [], + pathLast: "!", + generics: [], + }], + foundElems: 1, + original: "!", + returned: [], + typeFilter: -1, + userQuery: "!", + error: null, + }, + { + elems: [{ + name: "a!", + fullPath: ["a!"], + pathWithoutLast: [], + pathLast: "a!", + generics: [], + }], + foundElems: 1, + original: "a!", + returned: [], + typeFilter: -1, + userQuery: "a!", + error: null, + }, + { + elems: [{ + name: "a!::b", + fullPath: ["a!", "b"], + pathWithoutLast: ["a!"], + pathLast: "b", + generics: [], + }], + foundElems: 1, + original: "a!::b", + returned: [], + typeFilter: -1, + userQuery: "a!::b", + error: null, + }, + { + elems: [{ + name: "a!::b!", + fullPath: ["a!", "b!"], + pathWithoutLast: ["a!"], + pathLast: "b!", + generics: [], + }], + foundElems: 1, + original: "a!::b!", + returned: [], + typeFilter: -1, + userQuery: "a!::b!", + error: null, + }, +]; diff --git a/src/test/rustdoc-js-std/parser-returned.js b/src/test/rustdoc-js-std/parser-returned.js index b45466aa940..6fce17dcabd 100644 --- a/src/test/rustdoc-js-std/parser-returned.js +++ b/src/test/rustdoc-js-std/parser-returned.js @@ -1,4 +1,10 @@ -const QUERY = ['-> F

', '-> P', '->,a', 'aaaaa->a']; +const QUERY = [ + "-> F

", + "-> P", + "->,a", + "aaaaa->a", + "-> !", +]; const PARSED = [ { @@ -75,4 +81,19 @@ const PARSED = [ userQuery: "aaaaa->a", error: null, }, + { + elems: [], + foundElems: 1, + original: "-> !", + returned: [{ + name: "!", + fullPath: ["!"], + pathWithoutLast: [], + pathLast: "!", + generics: [], + }], + typeFilter: -1, + userQuery: "-> !", + error: null, + }, ]; diff --git a/src/test/rustdoc/issue-95873.rs b/src/test/rustdoc/issue-95873.rs new file mode 100644 index 00000000000..ff33fb63a0b --- /dev/null +++ b/src/test/rustdoc/issue-95873.rs @@ -0,0 +1,2 @@ +// @has issue_95873/index.html "//*[@class='item-left import-item']" "pub use ::std as x;" +pub use ::std as x; diff --git a/src/test/rustdoc/issue-96381.rs b/src/test/rustdoc/issue-96381.rs new file mode 100644 index 00000000000..f0f123f85a0 --- /dev/null +++ b/src/test/rustdoc/issue-96381.rs @@ -0,0 +1,16 @@ +// should-fail + +#![allow(unused)] + +trait Foo: Sized { + fn bar(i: i32, t: T, s: &Self) -> (T, i32); +} + +impl Foo for () { + fn bar(i: _, t: _, s: _) -> _ { + //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions + (1, 2) + } +} + +fn main() {} diff --git a/src/test/ui/functions-closures/fn-help-with-err.rs b/src/test/ui/functions-closures/fn-help-with-err.rs index f8a81af786f..3d2bcb8ad35 100644 --- a/src/test/ui/functions-closures/fn-help-with-err.rs +++ b/src/test/ui/functions-closures/fn-help-with-err.rs @@ -3,14 +3,14 @@ fn main() { let arc = std::sync::Arc::new(oops); //~^ ERROR cannot find value `oops` in this scope //~| NOTE not found - // The error "note: `arc` is a function, perhaps you wish to call it" MUST NOT appear. + // The error "note: this is a function, perhaps you wish to call it" MUST NOT appear. arc.blablabla(); //~^ ERROR no method named `blablabla` //~| NOTE method not found let arc2 = std::sync::Arc::new(|| 1); - // The error "note: `arc2` is a function, perhaps you wish to call it" SHOULD appear + // The error "note: this is a function, perhaps you wish to call it" SHOULD appear arc2.blablabla(); //~^ ERROR no method named `blablabla` //~| NOTE method not found - //~| NOTE `arc2` is a function, perhaps you wish to call it + //~| NOTE this is a function, perhaps you wish to call it } diff --git a/src/test/ui/functions-closures/fn-help-with-err.stderr b/src/test/ui/functions-closures/fn-help-with-err.stderr index 4d6b3282ad9..3e42cb1fb6e 100644 --- a/src/test/ui/functions-closures/fn-help-with-err.stderr +++ b/src/test/ui/functions-closures/fn-help-with-err.stderr @@ -14,9 +14,9 @@ error[E0599]: no method named `blablabla` found for struct `Arc<[closure@$DIR/fn --> $DIR/fn-help-with-err.rs:12:10 | LL | arc2.blablabla(); - | ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:40]>` - | - = note: `arc2` is a function, perhaps you wish to call it + | ---- ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:40]>` + | | + | this is a function, perhaps you wish to call it error: aborting due to 3 previous errors diff --git a/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr b/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr index 9cf8ff76c87..66cffa9e36c 100644 --- a/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr +++ b/src/test/ui/impl-trait/issues/issue-88236-2.nll.stderr @@ -24,10 +24,14 @@ LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send LL | x | ^ returning this value requires that `'b` must outlive `'static` | -help: to allow this `impl Trait` to capture borrowed data with lifetime `'b`, add `'b` as a bound +help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'b` lifetime bound | LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> + 'b { | ++++ +help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'b` lifetime bound + | +LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a + 'b> { + | ++++ error: implementation of `Hrtb` is not general enough --> $DIR/issue-88236-2.rs:20:5 diff --git a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr index 0059f729bae..5a190649b63 100644 --- a/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr +++ b/src/test/ui/impl-trait/must_outlive_least_region_or_bound.nll.stderr @@ -32,7 +32,14 @@ LL | fn elided2(x: &i32) -> impl Copy + 'static { x } | | | let's call the lifetime of this reference `'1` | - = help: consider replacing `'1` with `'static` +help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x` + | +LL | fn elided2(x: &i32) -> impl Copy + '_ { x } + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn elided2(x: &'static i32) -> impl Copy + 'static { x } + | ~~~~~~~~~~~~ error: lifetime may not live long enough --> $DIR/must_outlive_least_region_or_bound.rs:11:55 @@ -40,7 +47,14 @@ error: lifetime may not live long enough LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x } | -- lifetime `'a` defined here ^ returning this value requires that `'a` must outlive `'static` | - = help: consider replacing `'a` with `'static` +help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x` + | +LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'a { x } + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn explicit2<'a>(x: &'static i32) -> impl Copy + 'static { x } + | ~~~~~~~~~~~~ error[E0621]: explicit lifetime required in the type of `x` --> $DIR/must_outlive_least_region_or_bound.rs:13:41 @@ -57,6 +71,15 @@ LL | fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } | - ^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` | | | let's call the lifetime of this reference `'1` + | +help: to declare that the trait object captures data from argument `x`, you can add an explicit `'_` lifetime bound + | +LL | fn elided5(x: &i32) -> (Box, impl Debug) { (Box::new(x), x) } + | ++++ +help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'_` lifetime bound + | +LL | fn elided5(x: &i32) -> (Box, impl Debug + '_) { (Box::new(x), x) } + | ++++ error: lifetime may not live long enough --> $DIR/must_outlive_least_region_or_bound.rs:29:69 @@ -64,7 +87,14 @@ error: lifetime may not live long enough LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x } | -- lifetime `'a` defined here ^ returning this value requires that `'a` must outlive `'static` | - = help: consider replacing `'a` with `'static` +help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x` + | +LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'a { x } + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn with_bound<'a>(x: &'static i32) -> impl LifetimeTrait<'a> + 'static { x } + | ~~~~~~~~~~~~ error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds --> $DIR/must_outlive_least_region_or_bound.rs:34:5 diff --git a/src/test/ui/issues/issue-16922.nll.stderr b/src/test/ui/issues/issue-16922.nll.stderr index 7f4f5b22eb3..9d9f32a97c0 100644 --- a/src/test/ui/issues/issue-16922.nll.stderr +++ b/src/test/ui/issues/issue-16922.nll.stderr @@ -5,6 +5,11 @@ LL | fn foo(value: &T) -> Box { | - let's call the lifetime of this reference `'1` LL | Box::new(value) as Box | ^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static` + | +help: to declare that the trait object captures data from argument `value`, you can add an explicit `'_` lifetime bound + | +LL | fn foo(value: &T) -> Box { + | ++++ error: aborting due to previous error diff --git a/src/test/ui/issues/issue-29124.stderr b/src/test/ui/issues/issue-29124.stderr index 42d89cd01a4..c5d2ec08409 100644 --- a/src/test/ui/issues/issue-29124.stderr +++ b/src/test/ui/issues/issue-29124.stderr @@ -2,17 +2,17 @@ error[E0599]: no method named `x` found for fn item `fn() -> Ret {Obj::func}` in --> $DIR/issue-29124.rs:15:15 | LL | Obj::func.x(); - | ^ method not found in `fn() -> Ret {Obj::func}` - | - = note: `Obj::func` is a function, perhaps you wish to call it + | --------- ^ method not found in `fn() -> Ret {Obj::func}` + | | + | this is a function, perhaps you wish to call it error[E0599]: no method named `x` found for fn item `fn() -> Ret {func}` in the current scope --> $DIR/issue-29124.rs:17:10 | LL | func.x(); - | ^ method not found in `fn() -> Ret {func}` - | - = note: `func` is a function, perhaps you wish to call it + | ---- ^ method not found in `fn() -> Ret {func}` + | | + | this is a function, perhaps you wish to call it error: aborting due to 2 previous errors diff --git a/src/test/ui/issues/issue-57362-1.stderr b/src/test/ui/issues/issue-57362-1.stderr index 5c611cd43d3..8e19f14009a 100644 --- a/src/test/ui/issues/issue-57362-1.stderr +++ b/src/test/ui/issues/issue-57362-1.stderr @@ -2,9 +2,10 @@ error[E0599]: no method named `f` found for fn pointer `fn(&u8)` in the current --> $DIR/issue-57362-1.rs:20:7 | LL | a.f(); - | ^ method not found in `fn(&u8)` + | - ^ method not found in `fn(&u8)` + | | + | this is a function, perhaps you wish to call it | - = note: `a` is a function, perhaps you wish to call it = help: items from traits can only be used if the trait is implemented and in scope note: `Trait` defines an item `f`, perhaps you need to implement it --> $DIR/issue-57362-1.rs:8:1 diff --git a/src/test/ui/nll/mir_check_cast_unsize.stderr b/src/test/ui/nll/mir_check_cast_unsize.stderr index 364d6c17ea7..8d02ef71d1b 100644 --- a/src/test/ui/nll/mir_check_cast_unsize.stderr +++ b/src/test/ui/nll/mir_check_cast_unsize.stderr @@ -5,6 +5,11 @@ LL | fn bar<'a>(x: &'a u32) -> &'static dyn Debug { | -- lifetime `'a` defined here LL | x | ^ returning this value requires that `'a` must outlive `'static` + | +help: to declare that the trait object captures data from argument `x`, you can add an explicit `'a` lifetime bound + | +LL | fn bar<'a>(x: &'a u32) -> &'static dyn Debug + 'a { + | ++++ error: aborting due to previous error diff --git a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr index ae02c58d080..43695a7511d 100644 --- a/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr +++ b/src/test/ui/object-lifetime/object-lifetime-default-from-box-error.nll.stderr @@ -6,6 +6,11 @@ LL | fn load(ss: &mut SomeStruct) -> Box { ... LL | ss.r | ^^^^ returning this value requires that `'1` must outlive `'static` + | +help: to declare that the trait object captures data from argument `ss`, you can add an explicit `'_` lifetime bound + | +LL | fn load(ss: &mut SomeStruct) -> Box { + | ++++ error[E0507]: cannot move out of `ss.r` which is behind a mutable reference --> $DIR/object-lifetime-default-from-box-error.rs:18:5 diff --git a/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr b/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr index 4d72724586e..724b06ce8b1 100644 --- a/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr +++ b/src/test/ui/regions/region-object-lifetime-in-coercion.nll.stderr @@ -5,6 +5,15 @@ LL | fn a(v: &[u8]) -> Box { | - let's call the lifetime of this reference `'1` LL | let x: Box = Box::new(v); | ^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'1` must outlive `'static` + | +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` + | +LL | fn a(v: &[u8]) -> Box { + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn a(v: &'static [u8]) -> Box { + | ~~~~~~~~~~~~~ error: lifetime may not live long enough --> $DIR/region-object-lifetime-in-coercion.rs:19:5 @@ -13,6 +22,15 @@ LL | fn b(v: &[u8]) -> Box { | - let's call the lifetime of this reference `'1` LL | Box::new(v) | ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` + | +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` + | +LL | fn b(v: &[u8]) -> Box { + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn b(v: &'static [u8]) -> Box { + | ~~~~~~~~~~~~~ error: lifetime may not live long enough --> $DIR/region-object-lifetime-in-coercion.rs:27:5 @@ -22,6 +40,11 @@ LL | fn c(v: &[u8]) -> Box { ... LL | Box::new(v) | ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` + | +help: to declare that the trait object captures data from argument `v`, you can add an explicit `'_` lifetime bound + | +LL | fn c(v: &[u8]) -> Box { + | ++++ error: lifetime may not live long enough --> $DIR/region-object-lifetime-in-coercion.rs:33:5 diff --git a/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr b/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr index 1a79da2776b..473c99b672f 100644 --- a/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-2.nll.stderr @@ -5,6 +5,15 @@ LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { | -- lifetime `'a` defined here LL | Box::new(B(&*v)) as Box | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` + | +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` + | +LL | fn g<'a, T: 'static>(v: Box + 'a>) -> Box { + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn g<'a, T: 'static>(v: Box<(dyn A + 'static)>) -> Box { + | ~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0515]: cannot return value referencing local data `*v` --> $DIR/regions-close-object-into-object-2.rs:13:5 diff --git a/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr b/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr index 993b13ddbf8..05ddc09b2d0 100644 --- a/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr +++ b/src/test/ui/regions/regions-close-object-into-object-4.nll.stderr @@ -29,6 +29,15 @@ LL | fn i<'a, T, U>(v: Box+'a>) -> Box { | -- lifetime `'a` defined here LL | Box::new(B(&*v)) as Box | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static` + | +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v` + | +LL | fn i<'a, T, U>(v: Box+'a>) -> Box { + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn i<'a, T, U>(v: Box<(dyn A + 'static)>) -> Box { + | ~~~~~~~~~~~~~~~~~~~~~~~~~ error[E0515]: cannot return value referencing local data `*v` --> $DIR/regions-close-object-into-object-4.rs:13:5 diff --git a/src/test/ui/regions/regions-proc-bound-capture.nll.stderr b/src/test/ui/regions/regions-proc-bound-capture.nll.stderr index 6120a53eb09..ce4d2d4d111 100644 --- a/src/test/ui/regions/regions-proc-bound-capture.nll.stderr +++ b/src/test/ui/regions/regions-proc-bound-capture.nll.stderr @@ -6,6 +6,15 @@ LL | fn static_proc(x: &isize) -> Box (isize) + 'static> { LL | // This is illegal, because the region bound on `proc` is 'static. LL | Box::new(move || { *x }) | ^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` + | +help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x` + | +LL | fn static_proc(x: &isize) -> Box (isize) + '_> { + | ~~ +help: alternatively, add an explicit `'static` bound to this reference + | +LL | fn static_proc(x: &'static isize) -> Box (isize) + 'static> { + | ~~~~~~~~~~~~~~ error: aborting due to previous error diff --git a/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr index 3ed3827b97d..6c65e4f0175 100644 --- a/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr +++ b/src/test/ui/suggestions/lifetimes/trait-object-nested-in-impl-trait.nll.stderr @@ -9,10 +9,14 @@ LL | | remaining: self.0.iter(), LL | | } | |_________^ returning this value requires that `'1` must outlive `'static` | -help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as a bound +help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'_` lifetime bound | LL | fn iter(&self) -> impl Iterator> + '_ { | ++++ +help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound + | +LL | fn iter(&self) -> impl Iterator> { + | ++++ error: lifetime may not live long enough --> $DIR/trait-object-nested-in-impl-trait.rs:39:9 @@ -24,6 +28,11 @@ LL | | current: None, LL | | remaining: self.0.iter(), LL | | } | |_________^ returning this value requires that `'1` must outlive `'static` + | +help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound + | +LL | fn iter(&self) -> impl Iterator> + '_ { + | ++++ error: lifetime may not live long enough --> $DIR/trait-object-nested-in-impl-trait.rs:50:9 @@ -35,6 +44,11 @@ LL | | current: None, LL | | remaining: self.0.iter(), LL | | } | |_________^ returning this value requires that `'a` must outlive `'static` + | +help: to declare that the trait object captures data from argument `self`, you can add an explicit `'a` lifetime bound + | +LL | fn iter<'a>(&'a self) -> impl Iterator> + 'a { + | ++++ error: lifetime may not live long enough --> $DIR/trait-object-nested-in-impl-trait.rs:61:9 @@ -47,10 +61,14 @@ LL | | remaining: self.0.iter(), LL | | } | |_________^ returning this value requires that `'a` must outlive `'static` | -help: to allow this `impl Trait` to capture borrowed data with lifetime `'a`, add `'a` as a bound +help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'a` lifetime bound | LL | fn iter<'a>(&'a self) -> impl Iterator> + 'a { | ++++ +help: to declare that the trait object captures data from argument `self`, you can add an explicit `'a` lifetime bound + | +LL | fn iter<'a>(&'a self) -> impl Iterator> { + | ++++ error: aborting due to 4 previous errors diff --git a/src/test/ui/typeck/issue-87181/empty-tuple-method.rs b/src/test/ui/typeck/issue-87181/empty-tuple-method.rs new file mode 100644 index 00000000000..1875d8280cb --- /dev/null +++ b/src/test/ui/typeck/issue-87181/empty-tuple-method.rs @@ -0,0 +1,14 @@ +struct Bar { + bar: T +} + +struct Foo(); +impl Foo { + fn foo() { } +} + +fn main() { + let thing = Bar { bar: Foo }; + thing.bar.foo(); + //~^ ERROR no method named `foo` found for fn item `fn() -> Foo {Foo}` in the current scope [E0599] +} diff --git a/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr b/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr new file mode 100644 index 00000000000..6ed70b301e4 --- /dev/null +++ b/src/test/ui/typeck/issue-87181/empty-tuple-method.stderr @@ -0,0 +1,16 @@ +error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo}` in the current scope + --> $DIR/empty-tuple-method.rs:12:15 + | +LL | thing.bar.foo(); + | --------- ^^^ method not found in `fn() -> Foo {Foo}` + | | + | this is the constructor of a struct + | +help: call the constructor + | +LL | (thing.bar)().foo(); + | + +++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/typeck/issue-87181/enum-variant.rs b/src/test/ui/typeck/issue-87181/enum-variant.rs new file mode 100644 index 00000000000..3b926b90f10 --- /dev/null +++ b/src/test/ui/typeck/issue-87181/enum-variant.rs @@ -0,0 +1,16 @@ +struct Bar { + bar: T +} + +enum Foo{ + Tup() +} +impl Foo { + fn foo() { } +} + +fn main() { + let thing = Bar { bar: Foo::Tup }; + thing.bar.foo(); + //~^ ERROR no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` in the current scope [E0599] +} diff --git a/src/test/ui/typeck/issue-87181/enum-variant.stderr b/src/test/ui/typeck/issue-87181/enum-variant.stderr new file mode 100644 index 00000000000..a3a818696ab --- /dev/null +++ b/src/test/ui/typeck/issue-87181/enum-variant.stderr @@ -0,0 +1,16 @@ +error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` in the current scope + --> $DIR/enum-variant.rs:14:15 + | +LL | thing.bar.foo(); + | --------- ^^^ method not found in `fn() -> Foo {Foo::Tup}` + | | + | this is the constructor of an enum variant + | +help: call the constructor + | +LL | (thing.bar)().foo(); + | + +++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/typeck/issue-87181/tuple-field.rs b/src/test/ui/typeck/issue-87181/tuple-field.rs new file mode 100644 index 00000000000..00e3b460ecf --- /dev/null +++ b/src/test/ui/typeck/issue-87181/tuple-field.rs @@ -0,0 +1,14 @@ +struct Bar { + bar: T +} + +struct Foo(char, u16); +impl Foo { + fn foo() { } +} + +fn main() { + let thing = Bar { bar: Foo }; + thing.bar.0; + //~^ ERROR no field `0` on type `fn(char, u16) -> Foo {Foo}` [E0609] +} diff --git a/src/test/ui/typeck/issue-87181/tuple-field.stderr b/src/test/ui/typeck/issue-87181/tuple-field.stderr new file mode 100644 index 00000000000..4d22ada0247 --- /dev/null +++ b/src/test/ui/typeck/issue-87181/tuple-field.stderr @@ -0,0 +1,16 @@ +error[E0609]: no field `0` on type `fn(char, u16) -> Foo {Foo}` + --> $DIR/tuple-field.rs:12:15 + | +LL | thing.bar.0; + | --------- ^ + | | + | this is the constructor of a struct + | +help: call the constructor + | +LL | (thing.bar)(_, _).0; + | + +++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0609`. diff --git a/src/test/ui/typeck/issue-87181/tuple-method.rs b/src/test/ui/typeck/issue-87181/tuple-method.rs new file mode 100644 index 00000000000..e88f642b070 --- /dev/null +++ b/src/test/ui/typeck/issue-87181/tuple-method.rs @@ -0,0 +1,14 @@ +struct Bar { + bar: T +} + +struct Foo(u8, i32); +impl Foo { + fn foo() { } +} + +fn main() { + let thing = Bar { bar: Foo }; + thing.bar.foo(); + //~^ ERROR no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}` in the current scope [E0599] +} diff --git a/src/test/ui/typeck/issue-87181/tuple-method.stderr b/src/test/ui/typeck/issue-87181/tuple-method.stderr new file mode 100644 index 00000000000..1e392e17984 --- /dev/null +++ b/src/test/ui/typeck/issue-87181/tuple-method.stderr @@ -0,0 +1,16 @@ +error[E0599]: no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}` in the current scope + --> $DIR/tuple-method.rs:12:15 + | +LL | thing.bar.foo(); + | --------- ^^^ method not found in `fn(u8, i32) -> Foo {Foo}` + | | + | this is the constructor of a struct + | +help: call the constructor + | +LL | (thing.bar)(_, _).foo(); + | + +++++++ + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0599`. diff --git a/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr index 0b6d94e71f0..e9883903674 100644 --- a/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr +++ b/src/test/ui/unboxed-closures/unboxed-closures-static-call-wrong-trait.stderr @@ -2,9 +2,9 @@ error[E0599]: no method named `call` found for closure `[closure@$DIR/unboxed-cl --> $DIR/unboxed-closures-static-call-wrong-trait.rs:7:10 | LL | mut_.call((0, )); - | ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:31]` - | - = note: `mut_` is a function, perhaps you wish to call it + | ---- ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:31]` + | | + | this is a function, perhaps you wish to call it error: aborting due to previous error diff --git a/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr b/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr index 53d45f6a8f2..0ffb77cf021 100644 --- a/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr +++ b/src/test/ui/underscore-lifetime/dyn-trait-underscore.nll.stderr @@ -6,6 +6,11 @@ LL | fn a(items: &[T]) -> Box> { LL | // ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to `'static` LL | Box::new(items.iter()) | ^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static` + | +help: to declare that the trait object captures data from argument `items`, you can add an explicit `'_` lifetime bound + | +LL | fn a(items: &[T]) -> Box + '_> { + | ++++ error: aborting due to previous error