Auto merge of #96459 - Dylan-DPC:rollup-de6ud9d, r=Dylan-DPC
Rollup of 6 pull requests Successful merges: - #92569 (Improve Error Messaging for Unconstructed Structs and Enum Variants in Generic Contexts) - #96370 (Cleanup `report_method_error` a bit) - #96383 (Fix erased region escaping into wfcheck due to #95395) - #96385 (Recover most `impl Trait` and `dyn Trait` lifetime bound suggestions under NLL) - #96410 (rustdoc: do not write `{{root}}` in `pub use ::foo` docs) - #96430 (Fix handling of `!` in rustdoc search) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
99b70ee230
39 changed files with 906 additions and 432 deletions
|
|
@ -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 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, 'tcx> {
|
|||
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())),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3314,6 +3314,12 @@ impl<'hir> Node<'hir> {
|
|||
_ => 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.
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ mod trait_impl_difference;
|
|||
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 {
|
||||
|
|
|
|||
|
|
@ -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 @@ use rustc_span::Span;
|
|||
|
||||
/// 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<AnonymousParamInfo<'tcx>> {
|
||||
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<AnonymousParamInfo<'_>> {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -2681,21 +2681,21 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
|
|||
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(
|
||||
|
|
|
|||
|
|
@ -21,11 +21,13 @@ use crate::errors::{
|
|||
};
|
||||
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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
self.tcx().ty_error()
|
||||
}
|
||||
|
||||
fn check_call_constructor<G: EmissionGuarantee>(
|
||||
&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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
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);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use rustc_errors::{
|
|||
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::cmp::Ordering;
|
|||
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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
(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(|_| "<numeric literal>".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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
|
||||
if self.is_fn_ty(rcvr_ty, span) {
|
||||
fn report_function<T: std::fmt::Display>(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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
// 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 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||
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(|_| "<numeric literal>".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,
|
||||
|
|
|
|||
|
|
@ -98,12 +98,15 @@ pub use check::{check_item_type, check_wf_new};
|
|||
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<G: EmissionGuarantee>(
|
||||
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,
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue