Auto merge of #101167 - matthiaskrgr:rollup-yt3jdmp, r=matthiaskrgr
Rollup of 7 pull requests Successful merges: - #100898 (Do not report too many expr field candidates) - #101056 (Add the syntax of references to their documentation summary.) - #101106 (Rustdoc-Json: Retain Stripped Modules when they are imported, not when they have items) - #101131 (CTFE: exposing pointers and calling extern fn is just impossible) - #101141 (Simplify `get_trait_ref` fn used for `virtual_function_elimination`) - #101146 (Various changes to logging of borrowck-related code) - #101156 (Remove `Sync` requirement from lint pass objects) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
9f4d5d2a28
42 changed files with 339 additions and 200 deletions
|
@ -105,8 +105,8 @@ impl<'tcx> fmt::Debug for OutlivesConstraint<'tcx> {
|
||||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
write!(
|
write!(
|
||||||
formatter,
|
formatter,
|
||||||
"({:?}: {:?}) due to {:?} ({:?})",
|
"({:?}: {:?}) due to {:?} ({:?}) ({:?})",
|
||||||
self.sup, self.sub, self.locations, self.variance_info
|
self.sup, self.sub, self.locations, self.variance_info, self.category,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1119,6 +1119,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
/// short a lifetime. (But sometimes it is more useful to report
|
/// short a lifetime. (But sometimes it is more useful to report
|
||||||
/// it as a more direct conflict between the execution of a
|
/// it as a more direct conflict between the execution of a
|
||||||
/// `Drop::drop` with an aliasing borrow.)
|
/// `Drop::drop` with an aliasing borrow.)
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
pub(crate) fn report_borrowed_value_does_not_live_long_enough(
|
pub(crate) fn report_borrowed_value_does_not_live_long_enough(
|
||||||
&mut self,
|
&mut self,
|
||||||
location: Location,
|
location: Location,
|
||||||
|
@ -1126,13 +1127,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
place_span: (Place<'tcx>, Span),
|
place_span: (Place<'tcx>, Span),
|
||||||
kind: Option<WriteKind>,
|
kind: Option<WriteKind>,
|
||||||
) {
|
) {
|
||||||
debug!(
|
|
||||||
"report_borrowed_value_does_not_live_long_enough(\
|
|
||||||
{:?}, {:?}, {:?}, {:?}\
|
|
||||||
)",
|
|
||||||
location, borrow, place_span, kind
|
|
||||||
);
|
|
||||||
|
|
||||||
let drop_span = place_span.1;
|
let drop_span = place_span.1;
|
||||||
let root_place =
|
let root_place =
|
||||||
self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
|
self.prefixes(borrow.borrowed_place.as_ref(), PrefixSet::All).last().unwrap();
|
||||||
|
@ -1189,10 +1183,8 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
|
let kind_place = kind.filter(|_| place_desc.is_some()).map(|k| (k, place_span.0));
|
||||||
let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
|
let explanation = self.explain_why_borrow_contains_point(location, &borrow, kind_place);
|
||||||
|
|
||||||
debug!(
|
debug!(?place_desc, ?explanation);
|
||||||
"report_borrowed_value_does_not_live_long_enough(place_desc: {:?}, explanation: {:?})",
|
|
||||||
place_desc, explanation
|
|
||||||
);
|
|
||||||
let err = match (place_desc, explanation) {
|
let err = match (place_desc, explanation) {
|
||||||
// If the outlives constraint comes from inside the closure,
|
// If the outlives constraint comes from inside the closure,
|
||||||
// for example:
|
// for example:
|
||||||
|
@ -1464,6 +1456,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
fn report_temporary_value_does_not_live_long_enough(
|
fn report_temporary_value_does_not_live_long_enough(
|
||||||
&mut self,
|
&mut self,
|
||||||
location: Location,
|
location: Location,
|
||||||
|
@ -1473,13 +1466,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
proper_span: Span,
|
proper_span: Span,
|
||||||
explanation: BorrowExplanation<'tcx>,
|
explanation: BorrowExplanation<'tcx>,
|
||||||
) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
|
) -> DiagnosticBuilder<'cx, ErrorGuaranteed> {
|
||||||
debug!(
|
|
||||||
"report_temporary_value_does_not_live_long_enough(\
|
|
||||||
{:?}, {:?}, {:?}, {:?}\
|
|
||||||
)",
|
|
||||||
location, borrow, drop_span, proper_span
|
|
||||||
);
|
|
||||||
|
|
||||||
if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
|
if let BorrowExplanation::MustBeValidFor { category, span, from_closure: false, .. } =
|
||||||
explanation
|
explanation
|
||||||
{
|
{
|
||||||
|
|
|
@ -336,26 +336,22 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
/// - second half is the place being accessed
|
/// - second half is the place being accessed
|
||||||
///
|
///
|
||||||
/// [d]: https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points
|
/// [d]: https://rust-lang.github.io/rfcs/2094-nll.html#leveraging-intuition-framing-errors-in-terms-of-points
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
pub(crate) fn explain_why_borrow_contains_point(
|
pub(crate) fn explain_why_borrow_contains_point(
|
||||||
&self,
|
&self,
|
||||||
location: Location,
|
location: Location,
|
||||||
borrow: &BorrowData<'tcx>,
|
borrow: &BorrowData<'tcx>,
|
||||||
kind_place: Option<(WriteKind, Place<'tcx>)>,
|
kind_place: Option<(WriteKind, Place<'tcx>)>,
|
||||||
) -> BorrowExplanation<'tcx> {
|
) -> BorrowExplanation<'tcx> {
|
||||||
debug!(
|
|
||||||
"explain_why_borrow_contains_point(location={:?}, borrow={:?}, kind_place={:?})",
|
|
||||||
location, borrow, kind_place
|
|
||||||
);
|
|
||||||
|
|
||||||
let regioncx = &self.regioncx;
|
let regioncx = &self.regioncx;
|
||||||
let body: &Body<'_> = &self.body;
|
let body: &Body<'_> = &self.body;
|
||||||
let tcx = self.infcx.tcx;
|
let tcx = self.infcx.tcx;
|
||||||
|
|
||||||
let borrow_region_vid = borrow.region;
|
let borrow_region_vid = borrow.region;
|
||||||
debug!("explain_why_borrow_contains_point: borrow_region_vid={:?}", borrow_region_vid);
|
debug!(?borrow_region_vid);
|
||||||
|
|
||||||
let region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location);
|
let region_sub = self.regioncx.find_sub_region_live_at(borrow_region_vid, location);
|
||||||
debug!("explain_why_borrow_contains_point: region_sub={:?}", region_sub);
|
debug!(?region_sub);
|
||||||
|
|
||||||
match find_use::find(body, regioncx, tcx, region_sub, location) {
|
match find_use::find(body, regioncx, tcx, region_sub, location) {
|
||||||
Some(Cause::LiveVar(local, location)) => {
|
Some(Cause::LiveVar(local, location)) => {
|
||||||
|
@ -408,17 +404,11 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
opt_place_desc,
|
opt_place_desc,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug!(
|
debug!("Could not generate a region name");
|
||||||
"explain_why_borrow_contains_point: \
|
|
||||||
Could not generate a region name"
|
|
||||||
);
|
|
||||||
BorrowExplanation::Unexplained
|
BorrowExplanation::Unexplained
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
debug!(
|
debug!("Could not generate an error region vid");
|
||||||
"explain_why_borrow_contains_point: \
|
|
||||||
Could not generate an error region vid"
|
|
||||||
);
|
|
||||||
BorrowExplanation::Unexplained
|
BorrowExplanation::Unexplained
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -975,6 +975,7 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(self, flow_state))]
|
||||||
fn check_access_for_conflict(
|
fn check_access_for_conflict(
|
||||||
&mut self,
|
&mut self,
|
||||||
location: Location,
|
location: Location,
|
||||||
|
@ -983,11 +984,6 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
rw: ReadOrWrite,
|
rw: ReadOrWrite,
|
||||||
flow_state: &Flows<'cx, 'tcx>,
|
flow_state: &Flows<'cx, 'tcx>,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
debug!(
|
|
||||||
"check_access_for_conflict(location={:?}, place_span={:?}, sd={:?}, rw={:?})",
|
|
||||||
location, place_span, sd, rw,
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut error_reported = false;
|
let mut error_reported = false;
|
||||||
let tcx = self.infcx.tcx;
|
let tcx = self.infcx.tcx;
|
||||||
let body = self.body;
|
let body = self.body;
|
||||||
|
@ -1451,13 +1447,13 @@ impl<'cx, 'tcx> MirBorrowckCtxt<'cx, 'tcx> {
|
||||||
|
|
||||||
/// Checks whether a borrow of this place is invalidated when the function
|
/// Checks whether a borrow of this place is invalidated when the function
|
||||||
/// exits
|
/// exits
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
fn check_for_invalidation_at_exit(
|
fn check_for_invalidation_at_exit(
|
||||||
&mut self,
|
&mut self,
|
||||||
location: Location,
|
location: Location,
|
||||||
borrow: &BorrowData<'tcx>,
|
borrow: &BorrowData<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
) {
|
) {
|
||||||
debug!("check_for_invalidation_at_exit({:?})", borrow);
|
|
||||||
let place = borrow.borrowed_place;
|
let place = borrow.borrowed_place;
|
||||||
let mut root_place = PlaceRef { local: place.local, projection: &[] };
|
let mut root_place = PlaceRef { local: place.local, projection: &[] };
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,7 @@ pub(crate) fn places_conflict<'tcx>(
|
||||||
/// access depth. The `bias` parameter is used to determine how the unknowable (comparing runtime
|
/// access depth. The `bias` parameter is used to determine how the unknowable (comparing runtime
|
||||||
/// array indices, for example) should be interpreted - this depends on what the caller wants in
|
/// array indices, for example) should be interpreted - this depends on what the caller wants in
|
||||||
/// order to make the conservative choice and preserve soundness.
|
/// order to make the conservative choice and preserve soundness.
|
||||||
|
#[instrument(level = "debug", skip(tcx, body))]
|
||||||
pub(super) fn borrow_conflicts_with_place<'tcx>(
|
pub(super) fn borrow_conflicts_with_place<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
body: &Body<'tcx>,
|
body: &Body<'tcx>,
|
||||||
|
@ -53,11 +54,6 @@ pub(super) fn borrow_conflicts_with_place<'tcx>(
|
||||||
access: AccessDepth,
|
access: AccessDepth,
|
||||||
bias: PlaceConflictBias,
|
bias: PlaceConflictBias,
|
||||||
) -> bool {
|
) -> bool {
|
||||||
debug!(
|
|
||||||
"borrow_conflicts_with_place({:?}, {:?}, {:?}, {:?})",
|
|
||||||
borrow_place, access_place, access, bias,
|
|
||||||
);
|
|
||||||
|
|
||||||
// This Local/Local case is handled by the more general code below, but
|
// This Local/Local case is handled by the more general code below, but
|
||||||
// it's so common that it's a speed win to check for it first.
|
// it's so common that it's a speed win to check for it first.
|
||||||
if let Some(l1) = borrow_place.as_local() && let Some(l2) = access_place.as_local() {
|
if let Some(l1) = borrow_place.as_local() && let Some(l2) = access_place.as_local() {
|
||||||
|
@ -140,10 +136,9 @@ fn place_components_conflict<'tcx>(
|
||||||
for (i, (borrow_c, &access_c)) in
|
for (i, (borrow_c, &access_c)) in
|
||||||
iter::zip(borrow_place.projection, access_place.projection).enumerate()
|
iter::zip(borrow_place.projection, access_place.projection).enumerate()
|
||||||
{
|
{
|
||||||
debug!("borrow_conflicts_with_place: borrow_c = {:?}", borrow_c);
|
debug!(?borrow_c, ?access_c);
|
||||||
let borrow_proj_base = &borrow_place.projection[..i];
|
|
||||||
|
|
||||||
debug!("borrow_conflicts_with_place: access_c = {:?}", access_c);
|
let borrow_proj_base = &borrow_place.projection[..i];
|
||||||
|
|
||||||
// Borrow and access path both have more components.
|
// Borrow and access path both have more components.
|
||||||
//
|
//
|
||||||
|
@ -180,7 +175,7 @@ fn place_components_conflict<'tcx>(
|
||||||
// idea, at least for now, so just give up and
|
// idea, at least for now, so just give up and
|
||||||
// report a conflict. This is unsafe code anyway so
|
// report a conflict. This is unsafe code anyway so
|
||||||
// the user could always use raw pointers.
|
// the user could always use raw pointers.
|
||||||
debug!("borrow_conflicts_with_place: arbitrary -> conflict");
|
debug!("arbitrary -> conflict");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
Overlap::EqualOrDisjoint => {
|
Overlap::EqualOrDisjoint => {
|
||||||
|
@ -189,7 +184,7 @@ fn place_components_conflict<'tcx>(
|
||||||
Overlap::Disjoint => {
|
Overlap::Disjoint => {
|
||||||
// We have proven the borrow disjoint - further
|
// We have proven the borrow disjoint - further
|
||||||
// projections will remain disjoint.
|
// projections will remain disjoint.
|
||||||
debug!("borrow_conflicts_with_place: disjoint");
|
debug!("disjoint");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1167,8 +1167,9 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
/// Therefore, this method should only be used in diagnostic code,
|
/// Therefore, this method should only be used in diagnostic code,
|
||||||
/// where displaying *some* named universal region is better than
|
/// where displaying *some* named universal region is better than
|
||||||
/// falling back to 'static.
|
/// falling back to 'static.
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
pub(crate) fn approx_universal_upper_bound(&self, r: RegionVid) -> RegionVid {
|
pub(crate) fn approx_universal_upper_bound(&self, r: RegionVid) -> RegionVid {
|
||||||
debug!("approx_universal_upper_bound(r={:?}={})", r, self.region_value_str(r));
|
debug!("{}", self.region_value_str(r));
|
||||||
|
|
||||||
// Find the smallest universal region that contains all other
|
// Find the smallest universal region that contains all other
|
||||||
// universal regions within `region`.
|
// universal regions within `region`.
|
||||||
|
@ -1177,7 +1178,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
let static_r = self.universal_regions.fr_static;
|
let static_r = self.universal_regions.fr_static;
|
||||||
for ur in self.scc_values.universal_regions_outlived_by(r_scc) {
|
for ur in self.scc_values.universal_regions_outlived_by(r_scc) {
|
||||||
let new_lub = self.universal_region_relations.postdom_upper_bound(lub, ur);
|
let new_lub = self.universal_region_relations.postdom_upper_bound(lub, ur);
|
||||||
debug!("approx_universal_upper_bound: ur={:?} lub={:?} new_lub={:?}", ur, lub, new_lub);
|
debug!(?ur, ?lub, ?new_lub);
|
||||||
// The upper bound of two non-static regions is static: this
|
// The upper bound of two non-static regions is static: this
|
||||||
// means we know nothing about the relationship between these
|
// means we know nothing about the relationship between these
|
||||||
// two regions. Pick a 'better' one to use when constructing
|
// two regions. Pick a 'better' one to use when constructing
|
||||||
|
@ -1201,7 +1202,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!("approx_universal_upper_bound: r={:?} lub={:?}", r, lub);
|
debug!(?r, ?lub);
|
||||||
|
|
||||||
lub
|
lub
|
||||||
}
|
}
|
||||||
|
@ -2048,6 +2049,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
/// creating a constraint path that forces `R` to outlive
|
/// creating a constraint path that forces `R` to outlive
|
||||||
/// `from_region`, and then finding the best choices within that
|
/// `from_region`, and then finding the best choices within that
|
||||||
/// path to blame.
|
/// path to blame.
|
||||||
|
#[instrument(level = "debug", skip(self, target_test))]
|
||||||
pub(crate) fn best_blame_constraint(
|
pub(crate) fn best_blame_constraint(
|
||||||
&self,
|
&self,
|
||||||
body: &Body<'tcx>,
|
body: &Body<'tcx>,
|
||||||
|
@ -2055,16 +2057,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
from_region_origin: NllRegionVariableOrigin,
|
from_region_origin: NllRegionVariableOrigin,
|
||||||
target_test: impl Fn(RegionVid) -> bool,
|
target_test: impl Fn(RegionVid) -> bool,
|
||||||
) -> BlameConstraint<'tcx> {
|
) -> BlameConstraint<'tcx> {
|
||||||
debug!(
|
|
||||||
"best_blame_constraint(from_region={:?}, from_region_origin={:?})",
|
|
||||||
from_region, from_region_origin
|
|
||||||
);
|
|
||||||
|
|
||||||
// Find all paths
|
// Find all paths
|
||||||
let (path, target_region) =
|
let (path, target_region) =
|
||||||
self.find_constraint_paths_between_regions(from_region, target_test).unwrap();
|
self.find_constraint_paths_between_regions(from_region, target_test).unwrap();
|
||||||
debug!(
|
debug!(
|
||||||
"best_blame_constraint: path={:#?}",
|
"path={:#?}",
|
||||||
path.iter()
|
path.iter()
|
||||||
.map(|c| format!(
|
.map(|c| format!(
|
||||||
"{:?} ({:?}: {:?})",
|
"{:?} ({:?}: {:?})",
|
||||||
|
@ -2116,7 +2113,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
debug!("best_blame_constraint: categorized_path={:#?}", categorized_path);
|
debug!("categorized_path={:#?}", categorized_path);
|
||||||
|
|
||||||
// To find the best span to cite, we first try to look for the
|
// To find the best span to cite, we first try to look for the
|
||||||
// final constraint that is interesting and where the `sup` is
|
// final constraint that is interesting and where the `sup` is
|
||||||
|
@ -2214,10 +2211,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
let best_choice =
|
let best_choice =
|
||||||
if blame_source { range.rev().find(find_region) } else { range.find(find_region) };
|
if blame_source { range.rev().find(find_region) } else { range.find(find_region) };
|
||||||
|
|
||||||
debug!(
|
debug!(?best_choice, ?blame_source);
|
||||||
"best_blame_constraint: best_choice={:?} blame_source={}",
|
|
||||||
best_choice, blame_source
|
|
||||||
);
|
|
||||||
|
|
||||||
if let Some(i) = best_choice {
|
if let Some(i) = best_choice {
|
||||||
if let Some(next) = categorized_path.get(i + 1) {
|
if let Some(next) = categorized_path.get(i + 1) {
|
||||||
|
@ -2254,7 +2248,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||||
// appears to be the most interesting point to report to the
|
// appears to be the most interesting point to report to the
|
||||||
// user via an even more ad-hoc guess.
|
// user via an even more ad-hoc guess.
|
||||||
categorized_path.sort_by(|p0, p1| p0.category.cmp(&p1.category));
|
categorized_path.sort_by(|p0, p1| p0.category.cmp(&p1.category));
|
||||||
debug!("best_blame_constraint: sorted_path={:#?}", categorized_path);
|
debug!("sorted_path={:#?}", categorized_path);
|
||||||
|
|
||||||
categorized_path.remove(0)
|
categorized_path.remove(0)
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
/// **Any `rustc_infer::infer` operations that might generate region
|
/// **Any `rustc_infer::infer` operations that might generate region
|
||||||
/// constraints should occur within this method so that those
|
/// constraints should occur within this method so that those
|
||||||
/// constraints can be properly localized!**
|
/// constraints can be properly localized!**
|
||||||
#[instrument(skip(self, category, op), level = "trace")]
|
#[instrument(skip(self, op), level = "trace")]
|
||||||
pub(super) fn fully_perform_op<R, Op>(
|
pub(super) fn fully_perform_op<R, Op>(
|
||||||
&mut self,
|
&mut self,
|
||||||
locations: Locations,
|
locations: Locations,
|
||||||
|
|
|
@ -1043,6 +1043,7 @@ impl<'a, 'tcx> TypeChecker<'a, 'tcx> {
|
||||||
let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation;
|
let CanonicalUserTypeAnnotation { span, ref user_ty, inferred_ty } = *user_annotation;
|
||||||
let inferred_ty = self.normalize(inferred_ty, Locations::All(span));
|
let inferred_ty = self.normalize(inferred_ty, Locations::All(span));
|
||||||
let annotation = self.instantiate_canonical_with_fresh_inference_vars(span, user_ty);
|
let annotation = self.instantiate_canonical_with_fresh_inference_vars(span, user_ty);
|
||||||
|
debug!(?annotation);
|
||||||
match annotation {
|
match annotation {
|
||||||
UserType::Ty(mut ty) => {
|
UserType::Ty(mut ty) => {
|
||||||
ty = self.normalize(ty, Locations::All(span));
|
ty = self.normalize(ty, Locations::All(span));
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::traits::*;
|
use crate::traits::*;
|
||||||
|
|
||||||
use rustc_middle::ty::{self, subst::GenericArgKind, ExistentialPredicate, Ty, TyCtxt};
|
use rustc_middle::ty::{self, subst::GenericArgKind, Ty};
|
||||||
use rustc_session::config::Lto;
|
use rustc_session::config::Lto;
|
||||||
use rustc_symbol_mangling::typeid_for_trait_ref;
|
use rustc_symbol_mangling::typeid_for_trait_ref;
|
||||||
use rustc_target::abi::call::FnAbi;
|
use rustc_target::abi::call::FnAbi;
|
||||||
|
@ -29,7 +29,7 @@ impl<'a, 'tcx> VirtualIndex {
|
||||||
&& bx.cx().sess().lto() == Lto::Fat
|
&& bx.cx().sess().lto() == Lto::Fat
|
||||||
{
|
{
|
||||||
let typeid =
|
let typeid =
|
||||||
bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), get_trait_ref(bx.tcx(), ty)));
|
bx.typeid_metadata(typeid_for_trait_ref(bx.tcx(), expect_dyn_trait_in_self(ty)));
|
||||||
let vtable_byte_offset = self.0 * bx.data_layout().pointer_size.bytes();
|
let vtable_byte_offset = self.0 * bx.data_layout().pointer_size.bytes();
|
||||||
let type_checked_load = bx.type_checked_load(llvtable, vtable_byte_offset, typeid);
|
let type_checked_load = bx.type_checked_load(llvtable, vtable_byte_offset, typeid);
|
||||||
let func = bx.extract_value(type_checked_load, 0);
|
let func = bx.extract_value(type_checked_load, 0);
|
||||||
|
@ -64,17 +64,13 @@ impl<'a, 'tcx> VirtualIndex {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_trait_ref<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> ty::PolyExistentialTraitRef<'tcx> {
|
/// This takes a valid `self` receiver type and extracts the principal trait
|
||||||
|
/// ref of the type.
|
||||||
|
fn expect_dyn_trait_in_self<'tcx>(ty: Ty<'tcx>) -> ty::PolyExistentialTraitRef<'tcx> {
|
||||||
for arg in ty.peel_refs().walk() {
|
for arg in ty.peel_refs().walk() {
|
||||||
if let GenericArgKind::Type(ty) = arg.unpack() {
|
if let GenericArgKind::Type(ty) = arg.unpack() {
|
||||||
if let ty::Dynamic(trait_refs, _) = ty.kind() {
|
if let ty::Dynamic(data, _) = ty.kind() {
|
||||||
return trait_refs[0].map_bound(|trait_ref| match trait_ref {
|
return data.principal().expect("expected principal trait object");
|
||||||
ExistentialPredicate::Trait(tr) => tr,
|
|
||||||
ExistentialPredicate::Projection(proj) => proj.trait_ref(tcx),
|
|
||||||
ExistentialPredicate::AutoTrait(_) => {
|
|
||||||
bug!("auto traits don't have functions")
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,7 +15,6 @@ use crate::interpret::{
|
||||||
/// The CTFE machine has some custom error kinds.
|
/// The CTFE machine has some custom error kinds.
|
||||||
#[derive(Clone, Debug)]
|
#[derive(Clone, Debug)]
|
||||||
pub enum ConstEvalErrKind {
|
pub enum ConstEvalErrKind {
|
||||||
NeedsRfc(String),
|
|
||||||
ConstAccessesStatic,
|
ConstAccessesStatic,
|
||||||
ModifiedGlobal,
|
ModifiedGlobal,
|
||||||
AssertFailure(AssertKind<ConstInt>),
|
AssertFailure(AssertKind<ConstInt>),
|
||||||
|
@ -42,9 +41,6 @@ impl fmt::Display for ConstEvalErrKind {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
use self::ConstEvalErrKind::*;
|
use self::ConstEvalErrKind::*;
|
||||||
match *self {
|
match *self {
|
||||||
NeedsRfc(ref msg) => {
|
|
||||||
write!(f, "\"{}\" needs an rfc before being allowed inside constants", msg)
|
|
||||||
}
|
|
||||||
ConstAccessesStatic => write!(f, "constant accesses static"),
|
ConstAccessesStatic => write!(f, "constant accesses static"),
|
||||||
ModifiedGlobal => {
|
ModifiedGlobal => {
|
||||||
write!(f, "modifying a static's initial value from another static's initializer")
|
write!(f, "modifying a static's initial value from another static's initializer")
|
||||||
|
|
|
@ -269,9 +269,10 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
||||||
);
|
);
|
||||||
throw_inval!(AlreadyReported(guar));
|
throw_inval!(AlreadyReported(guar));
|
||||||
} else {
|
} else {
|
||||||
|
// `find_mir_or_eval_fn` checks that this is a const fn before even calling us,
|
||||||
|
// so this should be unreachable.
|
||||||
let path = ecx.tcx.def_path_str(def.did);
|
let path = ecx.tcx.def_path_str(def.did);
|
||||||
Err(ConstEvalErrKind::NeedsRfc(format!("calling extern function `{}`", path))
|
bug!("trying to call extern function `{path}` at compile-time");
|
||||||
.into())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => Ok(ecx.tcx.instance_mir(instance)),
|
_ => Ok(ecx.tcx.instance_mir(instance)),
|
||||||
|
@ -339,11 +340,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
||||||
|
|
||||||
// CTFE-specific intrinsics.
|
// CTFE-specific intrinsics.
|
||||||
let Some(ret) = target else {
|
let Some(ret) = target else {
|
||||||
return Err(ConstEvalErrKind::NeedsRfc(format!(
|
throw_unsup_format!("intrinsic `{intrinsic_name}` is not supported at compile-time");
|
||||||
"calling intrinsic `{}`",
|
|
||||||
intrinsic_name
|
|
||||||
))
|
|
||||||
.into());
|
|
||||||
};
|
};
|
||||||
match intrinsic_name {
|
match intrinsic_name {
|
||||||
sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
|
sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
|
||||||
|
@ -400,11 +397,9 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
return Err(ConstEvalErrKind::NeedsRfc(format!(
|
throw_unsup_format!(
|
||||||
"calling intrinsic `{}`",
|
"intrinsic `{intrinsic_name}` is not supported at compile-time"
|
||||||
intrinsic_name
|
);
|
||||||
))
|
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -447,7 +442,7 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
||||||
_left: &ImmTy<'tcx>,
|
_left: &ImmTy<'tcx>,
|
||||||
_right: &ImmTy<'tcx>,
|
_right: &ImmTy<'tcx>,
|
||||||
) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
|
) -> InterpResult<'tcx, (Scalar, bool, Ty<'tcx>)> {
|
||||||
Err(ConstEvalErrKind::NeedsRfc("pointer arithmetic or comparison".to_string()).into())
|
throw_unsup_format!("pointer arithmetic or comparison is not supported at compile-time");
|
||||||
}
|
}
|
||||||
|
|
||||||
fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
|
fn before_terminator(ecx: &mut InterpCx<'mir, 'tcx, Self>) -> InterpResult<'tcx> {
|
||||||
|
@ -469,7 +464,8 @@ impl<'mir, 'tcx> interpret::Machine<'mir, 'tcx> for CompileTimeInterpreter<'mir,
|
||||||
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
_ecx: &mut InterpCx<'mir, 'tcx, Self>,
|
||||||
_ptr: Pointer<AllocId>,
|
_ptr: Pointer<AllocId>,
|
||||||
) -> InterpResult<'tcx> {
|
) -> InterpResult<'tcx> {
|
||||||
Err(ConstEvalErrKind::NeedsRfc("exposing pointers".to_string()).into())
|
// This is only reachable with -Zunleash-the-miri-inside-of-you.
|
||||||
|
throw_unsup_format!("exposing pointers is not possible at compile-time")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
|
|
@ -479,6 +479,7 @@ pub macro compile_time_machine(<$mir: lifetime, $tcx: lifetime>) {
|
||||||
) -> InterpResult<$tcx, Pointer<Option<AllocId>>> {
|
) -> InterpResult<$tcx, Pointer<Option<AllocId>>> {
|
||||||
// Allow these casts, but make the pointer not dereferenceable.
|
// Allow these casts, but make the pointer not dereferenceable.
|
||||||
// (I.e., they behave like transmutation.)
|
// (I.e., they behave like transmutation.)
|
||||||
|
// This is correct because no pointers can ever be exposed in compile-time evaluation.
|
||||||
Ok(Pointer::from_addr(addr))
|
Ok(Pointer::from_addr(addr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -372,7 +372,7 @@ impl<'cx, 'tcx> TypeFolder<'tcx> for Canonicalizer<'cx, 'tcx> {
|
||||||
debug!(
|
debug!(
|
||||||
"canonical: region var found with vid {:?}, \
|
"canonical: region var found with vid {:?}, \
|
||||||
opportunistically resolved to {:?}",
|
opportunistically resolved to {:?}",
|
||||||
vid, r
|
vid, resolved_vid
|
||||||
);
|
);
|
||||||
let r = self.tcx.reuse_or_mk_region(r, ty::ReVar(resolved_vid));
|
let r = self.tcx.reuse_or_mk_region(r, ty::ReVar(resolved_vid));
|
||||||
self.canonicalize_mode.canonicalize_free_region(self, r)
|
self.canonicalize_mode.canonicalize_free_region(self, r)
|
||||||
|
|
|
@ -63,8 +63,8 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
|
||||||
Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>,
|
Canonical<'tcx, QueryResponse<'tcx, T>>: ArenaAllocatable<'tcx>,
|
||||||
{
|
{
|
||||||
let query_response = self.make_query_response(inference_vars, answer, fulfill_cx)?;
|
let query_response = self.make_query_response(inference_vars, answer, fulfill_cx)?;
|
||||||
|
debug!("query_response = {:#?}", query_response);
|
||||||
let canonical_result = self.canonicalize_response(query_response);
|
let canonical_result = self.canonicalize_response(query_response);
|
||||||
|
|
||||||
debug!("canonical_result = {:#?}", canonical_result);
|
debug!("canonical_result = {:#?}", canonical_result);
|
||||||
|
|
||||||
Ok(self.tcx.arena.alloc(canonical_result))
|
Ok(self.tcx.arena.alloc(canonical_result))
|
||||||
|
@ -125,6 +125,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
|
||||||
debug!("ambig_errors = {:#?}", ambig_errors);
|
debug!("ambig_errors = {:#?}", ambig_errors);
|
||||||
|
|
||||||
let region_obligations = self.take_registered_region_obligations();
|
let region_obligations = self.take_registered_region_obligations();
|
||||||
|
debug!(?region_obligations);
|
||||||
let region_constraints = self.with_region_constraints(|region_constraints| {
|
let region_constraints = self.with_region_constraints(|region_constraints| {
|
||||||
make_query_region_constraints(
|
make_query_region_constraints(
|
||||||
tcx,
|
tcx,
|
||||||
|
@ -132,6 +133,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
|
||||||
region_constraints,
|
region_constraints,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
debug!(?region_constraints);
|
||||||
|
|
||||||
let certainty =
|
let certainty =
|
||||||
if ambig_errors.is_empty() { Certainty::Proven } else { Certainty::Ambiguous };
|
if ambig_errors.is_empty() { Certainty::Proven } else { Certainty::Ambiguous };
|
||||||
|
@ -632,6 +634,8 @@ pub fn make_query_region_constraints<'tcx>(
|
||||||
assert!(verifys.is_empty());
|
assert!(verifys.is_empty());
|
||||||
assert!(givens.is_empty());
|
assert!(givens.is_empty());
|
||||||
|
|
||||||
|
debug!(?constraints);
|
||||||
|
|
||||||
let outlives: Vec<_> = constraints
|
let outlives: Vec<_> = constraints
|
||||||
.iter()
|
.iter()
|
||||||
.map(|(k, _)| match *k {
|
.map(|(k, _)| match *k {
|
||||||
|
|
|
@ -504,7 +504,7 @@ pub enum FixupError<'tcx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// See the `region_obligations` field for more information.
|
/// See the `region_obligations` field for more information.
|
||||||
#[derive(Clone)]
|
#[derive(Clone, Debug)]
|
||||||
pub struct RegionObligation<'tcx> {
|
pub struct RegionObligation<'tcx> {
|
||||||
pub sub_region: ty::Region<'tcx>,
|
pub sub_region: ty::Region<'tcx>,
|
||||||
pub sup_type: Ty<'tcx>,
|
pub sup_type: Ty<'tcx>,
|
||||||
|
@ -2027,16 +2027,6 @@ impl RegionVariableOrigin {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> fmt::Debug for RegionObligation<'tcx> {
|
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
||||||
write!(
|
|
||||||
f,
|
|
||||||
"RegionObligation(sub_region={:?}, sup_type={:?})",
|
|
||||||
self.sub_region, self.sup_type
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Replaces substs that reference param or infer variables with suitable
|
/// Replaces substs that reference param or infer variables with suitable
|
||||||
/// placeholders. This function is meant to remove these param and infer
|
/// placeholders. This function is meant to remove these param and infer
|
||||||
/// substs when they're not actually needed to evaluate a constant.
|
/// substs when they're not actually needed to evaluate a constant.
|
||||||
|
|
|
@ -92,6 +92,7 @@ impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
|
||||||
sub_region: Region<'tcx>,
|
sub_region: Region<'tcx>,
|
||||||
cause: &ObligationCause<'tcx>,
|
cause: &ObligationCause<'tcx>,
|
||||||
) {
|
) {
|
||||||
|
debug!(?sup_type, ?sub_region, ?cause);
|
||||||
let origin = SubregionOrigin::from_obligation_cause(cause, || {
|
let origin = SubregionOrigin::from_obligation_cause(cause, || {
|
||||||
infer::RelateParamBound(
|
infer::RelateParamBound(
|
||||||
cause.span,
|
cause.span,
|
||||||
|
@ -248,14 +249,13 @@ where
|
||||||
/// - `origin`, the reason we need this constraint
|
/// - `origin`, the reason we need this constraint
|
||||||
/// - `ty`, the type `T`
|
/// - `ty`, the type `T`
|
||||||
/// - `region`, the region `'a`
|
/// - `region`, the region `'a`
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
pub fn type_must_outlive(
|
pub fn type_must_outlive(
|
||||||
&mut self,
|
&mut self,
|
||||||
origin: infer::SubregionOrigin<'tcx>,
|
origin: infer::SubregionOrigin<'tcx>,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
region: ty::Region<'tcx>,
|
region: ty::Region<'tcx>,
|
||||||
) {
|
) {
|
||||||
debug!("type_must_outlive(ty={:?}, region={:?}, origin={:?})", ty, region, origin);
|
|
||||||
|
|
||||||
assert!(!ty.has_escaping_bound_vars());
|
assert!(!ty.has_escaping_bound_vars());
|
||||||
|
|
||||||
let mut components = smallvec![];
|
let mut components = smallvec![];
|
||||||
|
|
|
@ -243,6 +243,5 @@ macro_rules! declare_combined_early_lint_pass {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A lint pass boxed up as a trait object.
|
/// A lint pass boxed up as a trait object.
|
||||||
pub type EarlyLintPassObject = Box<dyn EarlyLintPass + sync::Send + sync::Sync + 'static>;
|
pub type EarlyLintPassObject = Box<dyn EarlyLintPass + sync::Send + 'static>;
|
||||||
pub type LateLintPassObject =
|
pub type LateLintPassObject = Box<dyn for<'tcx> LateLintPass<'tcx> + sync::Send + 'static>;
|
||||||
Box<dyn for<'tcx> LateLintPass<'tcx> + sync::Send + sync::Sync + 'static>;
|
|
||||||
|
|
|
@ -157,6 +157,7 @@ symbols! {
|
||||||
BTreeSet,
|
BTreeSet,
|
||||||
BinaryHeap,
|
BinaryHeap,
|
||||||
Borrow,
|
Borrow,
|
||||||
|
BorrowMut,
|
||||||
Break,
|
Break,
|
||||||
C,
|
C,
|
||||||
CStr,
|
CStr,
|
||||||
|
|
|
@ -120,6 +120,7 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> {
|
||||||
EarlyBinder(value).subst(self.tcx(), substs)
|
EarlyBinder(value).subst(self.tcx(), substs)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[instrument(level = "debug", skip(self))]
|
||||||
fn relate_mir_and_user_ty(
|
fn relate_mir_and_user_ty(
|
||||||
&mut self,
|
&mut self,
|
||||||
mir_ty: Ty<'tcx>,
|
mir_ty: Ty<'tcx>,
|
||||||
|
@ -132,8 +133,8 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> {
|
||||||
|
|
||||||
let ty = tcx.type_of(def_id);
|
let ty = tcx.type_of(def_id);
|
||||||
let ty = self.subst(ty, substs);
|
let ty = self.subst(ty, substs);
|
||||||
debug!("relate_type_and_user_type: ty of def-id is {:?}", ty);
|
|
||||||
let ty = self.normalize(ty);
|
let ty = self.normalize(ty);
|
||||||
|
debug!("relate_type_and_user_type: ty of def-id is {:?}", ty);
|
||||||
|
|
||||||
self.relate(mir_ty, Variance::Invariant, ty)?;
|
self.relate(mir_ty, Variance::Invariant, ty)?;
|
||||||
|
|
||||||
|
@ -144,7 +145,7 @@ impl<'me, 'tcx> AscribeUserTypeCx<'me, 'tcx> {
|
||||||
// outlives" error messages.
|
// outlives" error messages.
|
||||||
let instantiated_predicates =
|
let instantiated_predicates =
|
||||||
self.tcx().predicates_of(def_id).instantiate(self.tcx(), substs);
|
self.tcx().predicates_of(def_id).instantiate(self.tcx(), substs);
|
||||||
debug!(?instantiated_predicates.predicates);
|
debug!(?instantiated_predicates);
|
||||||
for instantiated_predicate in instantiated_predicates.predicates {
|
for instantiated_predicate in instantiated_predicates.predicates {
|
||||||
let instantiated_predicate = self.normalize(instantiated_predicate);
|
let instantiated_predicate = self.normalize(instantiated_predicate);
|
||||||
self.prove_predicate(instantiated_predicate, span);
|
self.prove_predicate(instantiated_predicate, span);
|
||||||
|
|
|
@ -2605,34 +2605,41 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
if let Some((fields, substs)) =
|
if let Some((fields, substs)) =
|
||||||
self.get_field_candidates_considering_privacy(span, expr_t, mod_id)
|
self.get_field_candidates_considering_privacy(span, expr_t, mod_id)
|
||||||
{
|
{
|
||||||
for candidate_field in fields {
|
let candidate_fields: Vec<_> = fields
|
||||||
if let Some(mut field_path) = self.check_for_nested_field_satisfying(
|
.filter_map(|candidate_field| {
|
||||||
|
self.check_for_nested_field_satisfying(
|
||||||
span,
|
span,
|
||||||
&|candidate_field, _| candidate_field.ident(self.tcx()) == field,
|
&|candidate_field, _| candidate_field.ident(self.tcx()) == field,
|
||||||
candidate_field,
|
candidate_field,
|
||||||
substs,
|
substs,
|
||||||
vec![],
|
vec![],
|
||||||
mod_id,
|
mod_id,
|
||||||
) {
|
)
|
||||||
// field_path includes `field` that we're looking for, so pop it.
|
})
|
||||||
|
.map(|mut field_path| {
|
||||||
field_path.pop();
|
field_path.pop();
|
||||||
|
field_path
|
||||||
let field_path_str = field_path
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|id| id.name.to_ident_string())
|
.map(|id| id.name.to_ident_string())
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(".");
|
.join(".")
|
||||||
debug!("field_path_str: {:?}", field_path_str);
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
err.span_suggestion_verbose(
|
let len = candidate_fields.len();
|
||||||
|
if len > 0 {
|
||||||
|
err.span_suggestions(
|
||||||
field.span.shrink_to_lo(),
|
field.span.shrink_to_lo(),
|
||||||
"one of the expressions' fields has a field of the same name",
|
format!(
|
||||||
format!("{field_path_str}."),
|
"{} of the expressions' fields {} a field of the same name",
|
||||||
|
if len > 1 { "some" } else { "one" },
|
||||||
|
if len > 1 { "have" } else { "has" },
|
||||||
|
),
|
||||||
|
candidate_fields.iter().map(|path| format!("{path}.")),
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1351,11 +1351,24 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
) {
|
) {
|
||||||
if let SelfSource::MethodCall(expr) = source
|
if let SelfSource::MethodCall(expr) = source
|
||||||
&& let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
|
&& let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id()
|
||||||
&& let Some((fields, substs)) = self.get_field_candidates_considering_privacy(span, actual, mod_id)
|
&& let Some((fields, substs)) =
|
||||||
|
self.get_field_candidates_considering_privacy(span, actual, mod_id)
|
||||||
{
|
{
|
||||||
let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
|
let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
|
||||||
for candidate_field in fields {
|
|
||||||
if let Some(field_path) = self.check_for_nested_field_satisfying(
|
let lang_items = self.tcx.lang_items();
|
||||||
|
let never_mention_traits = [
|
||||||
|
lang_items.clone_trait(),
|
||||||
|
lang_items.deref_trait(),
|
||||||
|
lang_items.deref_mut_trait(),
|
||||||
|
self.tcx.get_diagnostic_item(sym::AsRef),
|
||||||
|
self.tcx.get_diagnostic_item(sym::AsMut),
|
||||||
|
self.tcx.get_diagnostic_item(sym::Borrow),
|
||||||
|
self.tcx.get_diagnostic_item(sym::BorrowMut),
|
||||||
|
];
|
||||||
|
let candidate_fields: Vec<_> = fields
|
||||||
|
.filter_map(|candidate_field| {
|
||||||
|
self.check_for_nested_field_satisfying(
|
||||||
span,
|
span,
|
||||||
&|_, field_ty| {
|
&|_, field_ty| {
|
||||||
self.lookup_probe(
|
self.lookup_probe(
|
||||||
|
@ -1363,32 +1376,45 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
item_name,
|
item_name,
|
||||||
field_ty,
|
field_ty,
|
||||||
call_expr,
|
call_expr,
|
||||||
ProbeScope::AllTraits,
|
ProbeScope::TraitsInScope,
|
||||||
)
|
)
|
||||||
.is_ok()
|
.map_or(false, |pick| {
|
||||||
|
!never_mention_traits
|
||||||
|
.iter()
|
||||||
|
.flatten()
|
||||||
|
.any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id)
|
||||||
|
})
|
||||||
},
|
},
|
||||||
candidate_field,
|
candidate_field,
|
||||||
substs,
|
substs,
|
||||||
vec![],
|
vec![],
|
||||||
mod_id,
|
mod_id,
|
||||||
) {
|
)
|
||||||
let field_path_str = field_path
|
})
|
||||||
|
.map(|field_path| {
|
||||||
|
field_path
|
||||||
.iter()
|
.iter()
|
||||||
.map(|id| id.name.to_ident_string())
|
.map(|id| id.name.to_ident_string())
|
||||||
.collect::<Vec<String>>()
|
.collect::<Vec<String>>()
|
||||||
.join(".");
|
.join(".")
|
||||||
debug!("field_path_str: {:?}", field_path_str);
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
err.span_suggestion_verbose(
|
let len = candidate_fields.len();
|
||||||
|
if len > 0 {
|
||||||
|
err.span_suggestions(
|
||||||
item_name.span.shrink_to_lo(),
|
item_name.span.shrink_to_lo(),
|
||||||
"one of the expressions' fields has a method of the same name",
|
format!(
|
||||||
format!("{field_path_str}."),
|
"{} of the expressions' fields {} a method of the same name",
|
||||||
|
if len > 1 { "some" } else { "one" },
|
||||||
|
if len > 1 { "have" } else { "has" },
|
||||||
|
),
|
||||||
|
candidate_fields.iter().map(|path| format!("{path}.")),
|
||||||
Applicability::MaybeIncorrect,
|
Applicability::MaybeIncorrect,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fn check_for_unwrap_self(
|
fn check_for_unwrap_self(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
@ -1223,7 +1223,7 @@ mod prim_usize {}
|
||||||
#[doc(alias = "&")]
|
#[doc(alias = "&")]
|
||||||
#[doc(alias = "&mut")]
|
#[doc(alias = "&mut")]
|
||||||
//
|
//
|
||||||
/// References, both shared and mutable.
|
/// References, `&T` and `&mut T`.
|
||||||
///
|
///
|
||||||
/// A reference represents a borrow of some owned value. You can get one by using the `&` or `&mut`
|
/// A reference represents a borrow of some owned value. You can get one by using the `&` or `&mut`
|
||||||
/// operators on a value, or by using a [`ref`](../std/keyword.ref.html) or
|
/// operators on a value, or by using a [`ref`](../std/keyword.ref.html) or
|
||||||
|
|
|
@ -1223,7 +1223,7 @@ mod prim_usize {}
|
||||||
#[doc(alias = "&")]
|
#[doc(alias = "&")]
|
||||||
#[doc(alias = "&mut")]
|
#[doc(alias = "&mut")]
|
||||||
//
|
//
|
||||||
/// References, both shared and mutable.
|
/// References, `&T` and `&mut T`.
|
||||||
///
|
///
|
||||||
/// A reference represents a borrow of some owned value. You can get one by using the `&` or `&mut`
|
/// A reference represents a borrow of some owned value. You can get one by using the `&` or `&mut`
|
||||||
/// operators on a value, or by using a [`ref`](../std/keyword.ref.html) or
|
/// operators on a value, or by using a [`ref`](../std/keyword.ref.html) or
|
||||||
|
|
|
@ -187,3 +187,9 @@ while work_list:
|
||||||
check_generic_bound(bound)
|
check_generic_bound(bound)
|
||||||
if item["inner"]["default"]:
|
if item["inner"]["default"]:
|
||||||
check_type(item["inner"]["default"])
|
check_type(item["inner"]["default"])
|
||||||
|
elif item["kind"] == "import":
|
||||||
|
if item["inner"]["id"]:
|
||||||
|
inner_id = item["inner"]["id"]
|
||||||
|
assert valid_id(inner_id)
|
||||||
|
if inner_id in crate["index"] and inner_id not in visited:
|
||||||
|
work_list.add(inner_id)
|
||||||
|
|
|
@ -46,10 +46,14 @@ impl JsonRenderer<'_> {
|
||||||
clean::KeywordItem => return None,
|
clean::KeywordItem => return None,
|
||||||
clean::StrippedItem(ref inner) => {
|
clean::StrippedItem(ref inner) => {
|
||||||
match &**inner {
|
match &**inner {
|
||||||
// We document non-empty stripped modules as with `Module::is_stripped` set to
|
// We document stripped modules as with `Module::is_stripped` set to
|
||||||
// `true`, to prevent contained items from being orphaned for downstream users,
|
// `true`, to prevent contained items from being orphaned for downstream users,
|
||||||
// as JSON does no inlining.
|
// as JSON does no inlining.
|
||||||
clean::ModuleItem(m) if !m.items.is_empty() => from_clean_item(item, self.tcx),
|
clean::ModuleItem(_)
|
||||||
|
if self.imported_items.contains(&item_id.expect_def_id()) =>
|
||||||
|
{
|
||||||
|
from_clean_item(item, self.tcx)
|
||||||
|
}
|
||||||
_ => return None,
|
_ => return None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
38
src/librustdoc/json/import_finder.rs
Normal file
38
src/librustdoc/json/import_finder.rs
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
|
use rustc_hir::def_id::DefId;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
clean::{self, Import, ImportSource, Item},
|
||||||
|
fold::DocFolder,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Get the id's of all items that are `pub use`d in the crate.
|
||||||
|
///
|
||||||
|
/// We need this to know if a stripped module is `pub use mod::*`, to decide
|
||||||
|
/// if it needs to be kept in the index, despite being stripped.
|
||||||
|
///
|
||||||
|
/// See [#100973](https://github.com/rust-lang/rust/issues/100973) and
|
||||||
|
/// [#101103](https://github.com/rust-lang/rust/issues/101103) for times when
|
||||||
|
/// this information is needed.
|
||||||
|
pub(crate) fn get_imports(krate: clean::Crate) -> (clean::Crate, FxHashSet<DefId>) {
|
||||||
|
let mut finder = ImportFinder { imported: FxHashSet::default() };
|
||||||
|
let krate = finder.fold_crate(krate);
|
||||||
|
(krate, finder.imported)
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ImportFinder {
|
||||||
|
imported: FxHashSet<DefId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DocFolder for ImportFinder {
|
||||||
|
fn fold_item(&mut self, i: Item) -> Option<Item> {
|
||||||
|
match *i.kind {
|
||||||
|
clean::ImportItem(Import { source: ImportSource { did: Some(did), .. }, .. }) => {
|
||||||
|
self.imported.insert(did);
|
||||||
|
Some(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => Some(self.fold_item_recur(i)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,6 +5,7 @@
|
||||||
//! docs for usage and details.
|
//! docs for usage and details.
|
||||||
|
|
||||||
mod conversions;
|
mod conversions;
|
||||||
|
mod import_finder;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use std::cell::RefCell;
|
||||||
use std::fs::{create_dir_all, File};
|
use std::fs::{create_dir_all, File};
|
||||||
|
@ -12,7 +13,7 @@ use std::io::{BufWriter, Write};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use rustc_data_structures::fx::FxHashMap;
|
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
|
||||||
use rustc_hir::def_id::DefId;
|
use rustc_hir::def_id::DefId;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
use rustc_session::Session;
|
use rustc_session::Session;
|
||||||
|
@ -39,6 +40,7 @@ pub(crate) struct JsonRenderer<'tcx> {
|
||||||
/// The directory where the blob will be written to.
|
/// The directory where the blob will be written to.
|
||||||
out_path: PathBuf,
|
out_path: PathBuf,
|
||||||
cache: Rc<Cache>,
|
cache: Rc<Cache>,
|
||||||
|
imported_items: FxHashSet<DefId>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> JsonRenderer<'tcx> {
|
impl<'tcx> JsonRenderer<'tcx> {
|
||||||
|
@ -159,12 +161,16 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
) -> Result<(Self, clean::Crate), Error> {
|
) -> Result<(Self, clean::Crate), Error> {
|
||||||
debug!("Initializing json renderer");
|
debug!("Initializing json renderer");
|
||||||
|
|
||||||
|
let (krate, imported_items) = import_finder::get_imports(krate);
|
||||||
|
|
||||||
Ok((
|
Ok((
|
||||||
JsonRenderer {
|
JsonRenderer {
|
||||||
tcx,
|
tcx,
|
||||||
index: Rc::new(RefCell::new(FxHashMap::default())),
|
index: Rc::new(RefCell::new(FxHashMap::default())),
|
||||||
out_path: options.output,
|
out_path: options.output,
|
||||||
cache: Rc::new(cache),
|
cache: Rc::new(cache),
|
||||||
|
imported_items,
|
||||||
},
|
},
|
||||||
krate,
|
krate,
|
||||||
))
|
))
|
||||||
|
|
28
src/test/rustdoc-json/reexport/glob_collision.rs
Normal file
28
src/test/rustdoc-json/reexport/glob_collision.rs
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
// Regression test for https://github.com/rust-lang/rust/issues/100973
|
||||||
|
|
||||||
|
#![feature(no_core)]
|
||||||
|
#![no_core]
|
||||||
|
|
||||||
|
// @set m1 = "$.index[*][?(@.name == 'm1' && @.kind == 'module')].id"
|
||||||
|
// @is "$.index[*][?(@.name == 'm1' && @.kind == 'module')].inner.items" []
|
||||||
|
// @is "$.index[*][?(@.name == 'm1' && @.kind == 'module')].inner.is_stripped" true
|
||||||
|
mod m1 {
|
||||||
|
pub fn f() {}
|
||||||
|
}
|
||||||
|
// @set m2 = "$.index[*][?(@.name == 'm2' && @.kind == 'module')].id"
|
||||||
|
// @is "$.index[*][?(@.name == 'm2' && @.kind == 'module')].inner.items" []
|
||||||
|
// @is "$.index[*][?(@.name == 'm2' && @.kind == 'module')].inner.is_stripped" true
|
||||||
|
mod m2 {
|
||||||
|
pub fn f(_: u8) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @set m1_use = "$.index[*][?(@.inner.name=='m1')].id"
|
||||||
|
// @is "$.index[*][?(@.inner.name=='m1')].inner.id" $m1
|
||||||
|
// @is "$.index[*][?(@.inner.name=='m1')].inner.glob" true
|
||||||
|
pub use m1::*;
|
||||||
|
// @set m2_use = "$.index[*][?(@.inner.name=='m2')].id"
|
||||||
|
// @is "$.index[*][?(@.inner.name=='m2')].inner.id" $m2
|
||||||
|
// @is "$.index[*][?(@.inner.name=='m2')].inner.glob" true
|
||||||
|
pub use m2::*;
|
||||||
|
|
||||||
|
// @ismany "$.index[*][?(@.inner.is_crate==true)].inner.items[*]" $m1_use $m2_use
|
8
src/test/rustdoc-json/reexport/glob_empty_mod.rs
Normal file
8
src/test/rustdoc-json/reexport/glob_empty_mod.rs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
// Regression test for https://github.com/rust-lang/rust/issues/100973
|
||||||
|
|
||||||
|
// @is "$.index[*][?(@.name=='m1' && @.kind == 'module')].inner.is_stripped" true
|
||||||
|
// @set m1 = "$.index[*][?(@.name=='m1')].id"
|
||||||
|
mod m1 {}
|
||||||
|
|
||||||
|
// @is "$.index[*][?(@.inner.name=='m1' && @.kind=='import')].inner.id" $m1
|
||||||
|
pub use m1::*;
|
|
@ -1,8 +1,7 @@
|
||||||
#![feature(no_core)]
|
#![feature(no_core)]
|
||||||
#![no_core]
|
#![no_core]
|
||||||
|
|
||||||
// @is "$.index[*][?(@.name=='foo')].kind" \"module\"
|
// @!has "$.index[*][?(@.name=='foo')]"
|
||||||
// @is "$.index[*][?(@.name=='foo')].inner.is_stripped" "true"
|
|
||||||
mod foo {
|
mod foo {
|
||||||
// @has "$.index[*][?(@.name=='Foo')]"
|
// @has "$.index[*][?(@.name=='Foo')]"
|
||||||
pub struct Foo;
|
pub struct Foo;
|
||||||
|
|
14
src/test/rustdoc-json/reexport/mod_not_included.rs
Normal file
14
src/test/rustdoc-json/reexport/mod_not_included.rs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
// Regression test for https://github.com/rust-lang/rust/issues/101103
|
||||||
|
|
||||||
|
#![feature(no_core)]
|
||||||
|
#![no_core]
|
||||||
|
|
||||||
|
mod m1 {
|
||||||
|
pub fn x() {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use m1::x;
|
||||||
|
|
||||||
|
// @has "$.index[*][?(@.name=='x' && @.kind=='function')]"
|
||||||
|
// @has "$.index[*][?(@.kind=='import' && @.inner.name=='x')].inner.source" '"m1::x"'
|
||||||
|
// @!has "$.index[*][?(@.name=='m1')]"
|
|
@ -6,8 +6,7 @@
|
||||||
#![no_core]
|
#![no_core]
|
||||||
#![feature(no_core)]
|
#![feature(no_core)]
|
||||||
|
|
||||||
// @is "$.index[*][?(@.name=='style')].kind" \"module\"
|
// @!has "$.index[*][?(@.name=='style')]"
|
||||||
// @is "$.index[*][?(@.name=='style')].inner.is_stripped" "true"
|
|
||||||
mod style {
|
mod style {
|
||||||
// @set color_struct_id = "$.index[*][?(@.kind=='struct' && @.name=='Color')].id"
|
// @set color_struct_id = "$.index[*][?(@.kind=='struct' && @.name=='Color')].id"
|
||||||
pub struct Color;
|
pub struct Color;
|
||||||
|
|
|
@ -3,8 +3,7 @@
|
||||||
#![no_core]
|
#![no_core]
|
||||||
#![feature(no_core)]
|
#![feature(no_core)]
|
||||||
|
|
||||||
// @is "$.index[*][?(@.name=='inner')].kind" \"module\"
|
// @!has "$.index[*][?(@.kind=='inner')]"
|
||||||
// @is "$.index[*][?(@.name=='inner')].inner.is_stripped" "true"
|
|
||||||
mod inner {
|
mod inner {
|
||||||
// @has "$.index[*][?(@.name=='Public')]"
|
// @has "$.index[*][?(@.name=='Public')]"
|
||||||
pub struct Public;
|
pub struct Public;
|
||||||
|
|
|
@ -2,16 +2,15 @@
|
||||||
#![no_core]
|
#![no_core]
|
||||||
#![feature(no_core)]
|
#![feature(no_core)]
|
||||||
|
|
||||||
// @is "$.index[*][?(@.name=='inner')].kind" \"module\"
|
// @!has "$.index[*][?(@.name=='inner')]"
|
||||||
// @is "$.index[*][?(@.name=='inner')].inner.is_stripped" "true"
|
|
||||||
mod inner {
|
mod inner {
|
||||||
// @set pub_id = "$.index[*][?(@.name=='Public')].id"
|
// @set pub_id = "$.index[*][?(@.name=='Public')].id"
|
||||||
pub struct Public;
|
pub struct Public;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @is "$.index[*][?(@.kind=='import')].inner.name" \"Public\"
|
// @is "$.index[*][?(@.kind=='import')].inner.name" \"Public\"
|
||||||
|
// @is "$.index[*][?(@.kind=='import')].inner.id" $pub_id
|
||||||
// @set use_id = "$.index[*][?(@.kind=='import')].id"
|
// @set use_id = "$.index[*][?(@.kind=='import')].id"
|
||||||
pub use inner::Public;
|
pub use inner::Public;
|
||||||
|
|
||||||
// @ismany "$.index[*][?(@.name=='inner')].inner.items[*]" $pub_id
|
|
||||||
// @ismany "$.index[*][?(@.name=='simple_private')].inner.items[*]" $use_id
|
// @ismany "$.index[*][?(@.name=='simple_private')].inner.items[*]" $use_id
|
||||||
|
|
|
@ -12,7 +12,7 @@ mod pub_inner_unreachable {
|
||||||
pub fn pub_inner_1() {}
|
pub fn pub_inner_1() {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// @has "$.index[*][?(@.name=='pub_inner_reachable')]"
|
// @!has "$.index[*][?(@.name=='pub_inner_reachable')]"
|
||||||
mod pub_inner_reachable {
|
mod pub_inner_reachable {
|
||||||
// @has "$.index[*][?(@.name=='pub_inner_2')]"
|
// @has "$.index[*][?(@.name=='pub_inner_2')]"
|
||||||
pub fn pub_inner_2() {}
|
pub fn pub_inner_2() {}
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
static PTR_INT_CAST: () = {
|
static PTR_INT_CAST: () = {
|
||||||
let x = &0 as *const _ as usize;
|
let x = &0 as *const _ as usize;
|
||||||
//~^ ERROR could not evaluate static initializer
|
//~^ ERROR could not evaluate static initializer
|
||||||
//~| "exposing pointers" needs an rfc before being allowed inside constants
|
//~| exposing pointers
|
||||||
let _v = x == x;
|
let _v = x == x;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -19,4 +19,7 @@ static PTR_INT_TRANSMUTE: () = unsafe {
|
||||||
//~| unable to turn pointer into raw bytes
|
//~| unable to turn pointer into raw bytes
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// I'd love to test pointer comparison, but that is not possible since
|
||||||
|
// their `PartialEq` impl is non-`const`.
|
||||||
|
|
||||||
fn main() {}
|
fn main() {}
|
||||||
|
|
|
@ -2,7 +2,7 @@ error[E0080]: could not evaluate static initializer
|
||||||
--> $DIR/ptr_arith.rs:9:13
|
--> $DIR/ptr_arith.rs:9:13
|
||||||
|
|
|
|
||||||
LL | let x = &0 as *const _ as usize;
|
LL | let x = &0 as *const _ as usize;
|
||||||
| ^^^^^^^^^^^^^^^^^^^^^^^ "exposing pointers" needs an rfc before being allowed inside constants
|
| ^^^^^^^^^^^^^^^^^^^^^^^ exposing pointers is not possible at compile-time
|
||||||
|
|
||||||
error[E0080]: could not evaluate static initializer
|
error[E0080]: could not evaluate static initializer
|
||||||
--> $DIR/ptr_arith.rs:17:14
|
--> $DIR/ptr_arith.rs:17:14
|
||||||
|
|
|
@ -10,10 +10,6 @@ LL | let _y = x.clone();
|
||||||
= help: items from traits can only be used if the trait is implemented and in scope
|
= help: items from traits can only be used if the trait is implemented and in scope
|
||||||
= note: the following trait defines an item `clone`, perhaps you need to implement it:
|
= note: the following trait defines an item `clone`, perhaps you need to implement it:
|
||||||
candidate #1: `Clone`
|
candidate #1: `Clone`
|
||||||
help: one of the expressions' fields has a method of the same name
|
|
||||||
|
|
|
||||||
LL | let _y = x.i.clone();
|
|
||||||
| ++
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -10,10 +10,6 @@ LL | let _d = c.clone();
|
||||||
= help: items from traits can only be used if the trait is implemented and in scope
|
= help: items from traits can only be used if the trait is implemented and in scope
|
||||||
= note: the following trait defines an item `clone`, perhaps you need to implement it:
|
= note: the following trait defines an item `clone`, perhaps you need to implement it:
|
||||||
candidate #1: `Clone`
|
candidate #1: `Clone`
|
||||||
help: one of the expressions' fields has a method of the same name
|
|
||||||
|
|
|
||||||
LL | let _d = c.x.clone();
|
|
||||||
| ++
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
|
@ -10,14 +10,6 @@ LL | let _y = x.clone();
|
||||||
= help: items from traits can only be used if the trait is implemented and in scope
|
= help: items from traits can only be used if the trait is implemented and in scope
|
||||||
= note: the following trait defines an item `clone`, perhaps you need to implement it:
|
= note: the following trait defines an item `clone`, perhaps you need to implement it:
|
||||||
candidate #1: `Clone`
|
candidate #1: `Clone`
|
||||||
help: one of the expressions' fields has a method of the same name
|
|
||||||
|
|
|
||||||
LL | let _y = x.i.clone();
|
|
||||||
| ++
|
|
||||||
help: one of the expressions' fields has a method of the same name
|
|
||||||
|
|
|
||||||
LL | let _y = x.j.x.clone();
|
|
||||||
| ++++
|
|
||||||
|
|
||||||
error: aborting due to previous error
|
error: aborting due to previous error
|
||||||
|
|
||||||
|
|
29
src/test/ui/suggestions/too-many-field-suggestions.rs
Normal file
29
src/test/ui/suggestions/too-many-field-suggestions.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
struct Thing {
|
||||||
|
a0: Foo,
|
||||||
|
a1: Foo,
|
||||||
|
a2: Foo,
|
||||||
|
a3: Foo,
|
||||||
|
a4: Foo,
|
||||||
|
a5: Foo,
|
||||||
|
a6: Foo,
|
||||||
|
a7: Foo,
|
||||||
|
a8: Foo,
|
||||||
|
a9: Foo,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Foo {
|
||||||
|
field: Field,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Field;
|
||||||
|
|
||||||
|
impl Foo {
|
||||||
|
fn bar(&self) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bar(t: Thing) {
|
||||||
|
t.bar(); //~ ERROR no method named `bar` found for struct `Thing`
|
||||||
|
t.field; //~ ERROR no field `field` on type `Thing`
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
44
src/test/ui/suggestions/too-many-field-suggestions.stderr
Normal file
44
src/test/ui/suggestions/too-many-field-suggestions.stderr
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
error[E0599]: no method named `bar` found for struct `Thing` in the current scope
|
||||||
|
--> $DIR/too-many-field-suggestions.rs:25:7
|
||||||
|
|
|
||||||
|
LL | struct Thing {
|
||||||
|
| ------------ method `bar` not found for this struct
|
||||||
|
...
|
||||||
|
LL | t.bar();
|
||||||
|
| ^^^ method not found in `Thing`
|
||||||
|
|
|
||||||
|
help: some of the expressions' fields have a method of the same name
|
||||||
|
|
|
||||||
|
LL | t.a0.bar();
|
||||||
|
| +++
|
||||||
|
LL | t.a1.bar();
|
||||||
|
| +++
|
||||||
|
LL | t.a2.bar();
|
||||||
|
| +++
|
||||||
|
LL | t.a3.bar();
|
||||||
|
| +++
|
||||||
|
and 6 other candidates
|
||||||
|
|
||||||
|
error[E0609]: no field `field` on type `Thing`
|
||||||
|
--> $DIR/too-many-field-suggestions.rs:26:7
|
||||||
|
|
|
||||||
|
LL | t.field;
|
||||||
|
| ^^^^^ unknown field
|
||||||
|
|
|
||||||
|
= note: available fields are: `a0`, `a1`, `a2`, `a3`, `a4` ... and 5 others
|
||||||
|
help: some of the expressions' fields have a field of the same name
|
||||||
|
|
|
||||||
|
LL | t.a0.field;
|
||||||
|
| +++
|
||||||
|
LL | t.a1.field;
|
||||||
|
| +++
|
||||||
|
LL | t.a2.field;
|
||||||
|
| +++
|
||||||
|
LL | t.a3.field;
|
||||||
|
| +++
|
||||||
|
and 6 other candidates
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0599, E0609.
|
||||||
|
For more information about an error, try `rustc --explain E0599`.
|
Loading…
Add table
Add a link
Reference in a new issue