Skip to content

Commit

Permalink
Auto merge of #97624 - matthiaskrgr:rollup-rtcqjx9, r=matthiaskrgr
Browse files Browse the repository at this point in the history
Rollup of 4 pull requests

Successful merges:

 - #96271 (suggest `?` when method is missing on `Result<T, _>` but found on `T`)
 - #97264 (Suggest `extern crate foo` when failing to resolve `use foo`)
 - #97592 (rustdoc: also index impl trait and raw pointers)
 - #97621 (update Miri)

Failed merges:

r? `@ghost`
`@rustbot` modify labels: rollup
  • Loading branch information
bors committed Jun 1, 2022
2 parents 8256e97 + 89e765f commit b5a2d27
Show file tree
Hide file tree
Showing 38 changed files with 647 additions and 69 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock
Expand Up @@ -5509,6 +5509,8 @@ dependencies = [
"pretty_assertions 1.2.1",
"regex",
"rustc_version",
"serde",
"serde_json",
]

[[package]]
Expand Down
15 changes: 12 additions & 3 deletions compiler/rustc_resolve/src/diagnostics.rs
Expand Up @@ -1839,9 +1839,18 @@ impl<'a> Resolver<'a> {
)),
)
} else if self.session.edition() == Edition::Edition2015 {
(format!("maybe a missing crate `{}`?", ident), None)
(
format!("maybe a missing crate `{ident}`?"),
Some((
vec![],
format!(
"consider adding `extern crate {ident}` to use the `{ident}` crate"
),
Applicability::MaybeIncorrect,
)),
)
} else {
(format!("could not find `{}` in the crate root", ident), None)
(format!("could not find `{ident}` in the crate root"), None)
}
} else if i > 0 {
let parent = path[i - 1].ident.name;
Expand All @@ -1852,7 +1861,7 @@ impl<'a> Resolver<'a> {
"the list of imported crates".to_owned()
}
kw::PathRoot | kw::Crate => "the crate root".to_owned(),
_ => format!("`{}`", parent),
_ => format!("`{parent}`"),
};

let mut msg = format!("could not find `{}` in {}", ident, parent);
Expand Down
4 changes: 4 additions & 0 deletions compiler/rustc_resolve/src/imports.rs
Expand Up @@ -475,6 +475,10 @@ impl<'a, 'b> ImportResolver<'a, 'b> {
}

if let Some((suggestions, msg, applicability)) = err.suggestion {
if suggestions.is_empty() {
diag.help(&msg);
continue;
}
diag.multipart_suggestion(&msg, suggestions, applicability);
}
}
Expand Down
203 changes: 151 additions & 52 deletions compiler/rustc_typeck/src/check/method/suggest.rs
Expand Up @@ -978,45 +978,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
label_span_not_found(&mut err);
}

if let SelfSource::MethodCall(expr) = source
&& let Some((fields, substs)) = self.get_field_candidates(span, actual)
{
let call_expr =
self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
for candidate_field in fields.iter() {
if let Some(field_path) = self.check_for_nested_field_satisfying(
span,
&|_, field_ty| {
self.lookup_probe(
span,
item_name,
field_ty,
call_expr,
ProbeScope::AllTraits,
)
.is_ok()
},
candidate_field,
substs,
vec![],
self.tcx.parent_module(expr.hir_id).to_def_id(),
) {
let field_path_str = field_path
.iter()
.map(|id| id.name.to_ident_string())
.collect::<Vec<String>>()
.join(".");
debug!("field_path_str: {:?}", field_path_str);
self.check_for_field_method(&mut err, source, span, actual, item_name);

err.span_suggestion_verbose(
item_name.span.shrink_to_lo(),
"one of the expressions' fields has a method of the same name",
format!("{field_path_str}."),
Applicability::MaybeIncorrect,
);
}
}
}
self.check_for_unwrap_self(&mut err, source, span, actual, item_name);

bound_spans.sort();
bound_spans.dedup();
Expand Down Expand Up @@ -1343,6 +1307,145 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
false
}

fn check_for_field_method(
&self,
err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
source: SelfSource<'tcx>,
span: Span,
actual: Ty<'tcx>,
item_name: Ident,
) {
if let SelfSource::MethodCall(expr) = source
&& let Some((fields, substs)) = self.get_field_candidates(span, actual)
{
let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
for candidate_field in fields.iter() {
if let Some(field_path) = self.check_for_nested_field_satisfying(
span,
&|_, field_ty| {
self.lookup_probe(
span,
item_name,
field_ty,
call_expr,
ProbeScope::AllTraits,
)
.is_ok()
},
candidate_field,
substs,
vec![],
self.tcx.parent_module(expr.hir_id).to_def_id(),
) {
let field_path_str = field_path
.iter()
.map(|id| id.name.to_ident_string())
.collect::<Vec<String>>()
.join(".");
debug!("field_path_str: {:?}", field_path_str);

err.span_suggestion_verbose(
item_name.span.shrink_to_lo(),
"one of the expressions' fields has a method of the same name",
format!("{field_path_str}."),
Applicability::MaybeIncorrect,
);
}
}
}
}

fn check_for_unwrap_self(
&self,
err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
source: SelfSource<'tcx>,
span: Span,
actual: Ty<'tcx>,
item_name: Ident,
) {
let tcx = self.tcx;
let SelfSource::MethodCall(expr) = source else { return; };
let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id));

let ty::Adt(kind, substs) = actual.kind() else { return; };
if !kind.is_enum() {
return;
}

let matching_variants: Vec<_> = kind
.variants()
.iter()
.flat_map(|variant| {
let [field] = &variant.fields[..] else { return None; };
let field_ty = field.ty(tcx, substs);

// Skip `_`, since that'll just lead to ambiguity.
if self.resolve_vars_if_possible(field_ty).is_ty_var() {
return None;
}

self.lookup_probe(span, item_name, field_ty, call_expr, ProbeScope::AllTraits)
.ok()
.map(|pick| (variant, field, pick))
})
.collect();

let ret_ty_matches = |diagnostic_item| {
if let Some(ret_ty) = self
.ret_coercion
.as_ref()
.map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty()))
&& let ty::Adt(kind, _) = ret_ty.kind()
&& tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did())
{
true
} else {
false
}
};

match &matching_variants[..] {
[(_, field, pick)] => {
let self_ty = field.ty(tcx, substs);
err.span_note(
tcx.def_span(pick.item.def_id),
&format!("the method `{item_name}` exists on the type `{self_ty}`"),
);
let (article, kind, variant, question) =
if Some(kind.did()) == tcx.get_diagnostic_item(sym::Result) {
("a", "Result", "Err", ret_ty_matches(sym::Result))
} else if Some(kind.did()) == tcx.get_diagnostic_item(sym::Option) {
("an", "Option", "None", ret_ty_matches(sym::Option))
} else {
return;
};
if question {
err.span_suggestion_verbose(
expr.span.shrink_to_hi(),
format!(
"use the `?` operator to extract the `{self_ty}` value, propagating \
{article} `{kind}::{variant}` value to the caller"
),
"?".to_owned(),
Applicability::MachineApplicable,
);
} else {
err.span_suggestion_verbose(
expr.span.shrink_to_hi(),
format!(
"consider using `{kind}::expect` to unwrap the `{self_ty}` value, \
panicking if the value is {article} `{kind}::{variant}`"
),
".expect(\"REASON\")".to_owned(),
Applicability::HasPlaceholders,
);
}
}
// FIXME(compiler-errors): Support suggestions for other matching enum variants
_ => {}
}
}

pub(crate) fn note_unmet_impls_on_type(
&self,
err: &mut Diagnostic,
Expand Down Expand Up @@ -1662,13 +1765,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "),
(self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"),
] {
match self.lookup_probe(
span,
item_name,
*rcvr_ty,
rcvr,
crate::check::method::probe::ProbeScope::AllTraits,
) {
match self.lookup_probe(span, item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) {
Ok(pick) => {
// If the method is defined for the receiver we have, it likely wasn't `use`d.
// We point at the method, but we just skip the rest of the check for arbitrary
Expand Down Expand Up @@ -1700,13 +1797,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
(self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Arc), "Arc::new"),
(self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"),
] {
if let Some(new_rcvr_t) = *rcvr_ty && let Ok(pick) = self.lookup_probe(
span,
item_name,
new_rcvr_t,
rcvr,
crate::check::method::probe::ProbeScope::AllTraits,
) {
if let Some(new_rcvr_t) = *rcvr_ty
&& let Ok(pick) = self.lookup_probe(
span,
item_name,
new_rcvr_t,
rcvr,
ProbeScope::AllTraits,
)
{
debug!("try_alt_rcvr: pick candidate {:?}", pick);
let did = Some(pick.item.container.id());
// We don't want to suggest a container type when the missing
Expand Down
4 changes: 4 additions & 0 deletions src/librustdoc/clean/types.rs
Expand Up @@ -1667,6 +1667,10 @@ impl Type {
matches!(self, Type::Generic(_))
}

pub(crate) fn is_impl_trait(&self) -> bool {
matches!(self, Type::ImplTrait(_))
}

pub(crate) fn is_primitive(&self) -> bool {
self.primitive_type().is_some()
}
Expand Down
35 changes: 27 additions & 8 deletions src/librustdoc/html/render/search_index.rs
Expand Up @@ -226,17 +226,17 @@ fn get_index_type_name(clean_type: &clean::Type) -> Option<Symbol> {
Some(path.segments.last().unwrap().name)
}
// We return an empty name because we don't care about the generic name itself.
clean::Generic(_) => Some(kw::Empty),
clean::Generic(_) | clean::ImplTrait(_) => Some(kw::Empty),
clean::Primitive(ref p) => Some(p.as_sym()),
clean::BorrowedRef { ref type_, .. } => get_index_type_name(type_),
clean::BorrowedRef { ref type_, .. } | clean::RawPointer(_, ref type_) => {
get_index_type_name(type_)
}
clean::BareFunction(_)
| clean::Tuple(_)
| clean::Slice(_)
| clean::Array(_, _)
| clean::RawPointer(_, _)
| clean::QPath { .. }
| clean::Infer
| clean::ImplTrait(_) => None,
| clean::Infer => None,
}
}

Expand Down Expand Up @@ -264,10 +264,12 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
mut generics: Vec<TypeWithKind>,
cache: &Cache,
) {
let is_full_generic = ty.is_full_generic();
// generics and impl trait are both identified by their generics,
// rather than a type name itself
let anonymous = ty.is_full_generic() || ty.is_impl_trait();
let generics_empty = generics.is_empty();

if is_full_generic {
if anonymous {
if generics_empty {
// This is a type parameter with no trait bounds (for example: `T` in
// `fn f<T>(p: T)`, so not useful for the rustdoc search because we would end up
Expand Down Expand Up @@ -318,7 +320,7 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
if index_ty.name.as_ref().map(|s| s.is_empty() && generics_empty).unwrap_or(true) {
return;
}
if is_full_generic {
if anonymous {
// We remove the name of the full generic because we have no use for it.
index_ty.name = Some(String::new());
res.push(TypeWithKind::from((index_ty, ItemType::Generic)));
Expand Down Expand Up @@ -398,6 +400,23 @@ fn add_generics_and_bounds_as_types<'tcx, 'a>(
}
insert_ty(res, tcx, arg.clone(), ty_generics, cache);
}
} else if let Type::ImplTrait(ref bounds) = *arg {
let mut ty_generics = Vec::new();
for bound in bounds {
if let Some(path) = bound.get_trait_path() {
let ty = Type::Path { path };
add_generics_and_bounds_as_types(
self_,
generics,
&ty,
tcx,
recurse + 1,
&mut ty_generics,
cache,
);
}
}
insert_ty(res, tcx, arg.clone(), ty_generics, cache);
} else {
// This is not a type parameter. So for example if we have `T, U: Option<T>`, and we're
// looking at `Option`, we enter this "else" condition, otherwise if it's `T`, we don't.
Expand Down

0 comments on commit b5a2d27

Please sign in to comment.