Skip to content

Commit eb792e8

Browse files
authored
Rollup merge of rust-lang#101100 - compiler-errors:generalize-call-suggestions, r=petrochenkov
Make call suggestions more general and more accurate Cleans up some suggestions that have to do with adding `()` to make typeck happy. 1. Drive-by rename of `expr_t` to `base_ty` since it's the type of the `base_expr` 1. Autoderef until we get to a callable type in `suggest_fn_call`. 1. Don't erroneously suggest calling constructor when a method/field does not exist on it. 1. Suggest calling a method receiver if its function output has a method (e.g. `fn.method()` => `fn().method()`) 1. Extend call suggestions to type parameters, fn pointers, trait objects where possible 1. Suggest calling in operators too (fixes rust-lang#101054) 1. Use `/* {ty} */` as argument placeholder instead of just `_`, which is confusing and makes suggestions look less like `if let` syntax.
2 parents eda68d7 + 1256530 commit eb792e8

35 files changed

+609
-416
lines changed

Diff for: compiler/rustc_errors/src/lib.rs

+15-4
Original file line numberDiff line numberDiff line change
@@ -1249,9 +1249,13 @@ impl HandlerInner {
12491249
}
12501250

12511251
fn treat_err_as_bug(&self) -> bool {
1252-
self.flags
1253-
.treat_err_as_bug
1254-
.map_or(false, |c| self.err_count() + self.lint_err_count >= c.get())
1252+
self.flags.treat_err_as_bug.map_or(false, |c| {
1253+
self.err_count()
1254+
+ self.lint_err_count
1255+
+ self.delayed_span_bugs.len()
1256+
+ self.delayed_good_path_bugs.len()
1257+
>= c.get()
1258+
})
12551259
}
12561260

12571261
fn print_error_count(&mut self, registry: &Registry) {
@@ -1407,7 +1411,14 @@ impl HandlerInner {
14071411
// This is technically `self.treat_err_as_bug()` but `delay_span_bug` is called before
14081412
// incrementing `err_count` by one, so we need to +1 the comparing.
14091413
// FIXME: Would be nice to increment err_count in a more coherent way.
1410-
if self.flags.treat_err_as_bug.map_or(false, |c| self.err_count() + 1 >= c.get()) {
1414+
if self.flags.treat_err_as_bug.map_or(false, |c| {
1415+
self.err_count()
1416+
+ self.lint_err_count
1417+
+ self.delayed_span_bugs.len()
1418+
+ self.delayed_good_path_bugs.len()
1419+
+ 1
1420+
>= c.get()
1421+
}) {
14111422
// FIXME: don't abort here if report_delayed_bugs is off
14121423
self.span_bug(sp, msg);
14131424
}

Diff for: compiler/rustc_lint_defs/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ macro_rules! pluralize {
4141
/// All suggestions are marked with an `Applicability`. Tools use the applicability of a suggestion
4242
/// to determine whether it should be automatically applied or if the user should be consulted
4343
/// before applying the suggestion.
44-
#[derive(Copy, Clone, Debug, PartialEq, Hash, Encodable, Decodable, Serialize, Deserialize)]
44+
#[derive(Copy, Clone, Debug, Hash, Encodable, Decodable, Serialize, Deserialize)]
45+
#[derive(PartialEq, Eq, PartialOrd, Ord)]
4546
pub enum Applicability {
4647
/// The suggestion is definitely what the user intended, or maintains the exact meaning of the code.
4748
/// This suggestion should be automatically applied.

Diff for: compiler/rustc_middle/src/ty/structural_impls.rs

+6
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,12 @@ impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for Vec<T> {
844844
}
845845
}
846846

847+
impl<'tcx, T: TypeVisitable<'tcx>> TypeVisitable<'tcx> for &[T] {
848+
fn visit_with<V: TypeVisitor<'tcx>>(&self, visitor: &mut V) -> ControlFlow<V::BreakTy> {
849+
self.iter().try_for_each(|t| t.visit_with(visitor))
850+
}
851+
}
852+
847853
impl<'tcx, T: TypeFoldable<'tcx>> TypeFoldable<'tcx> for Box<[T]> {
848854
fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
849855
self.try_map_id(|t| t.try_fold_with(folder))

Diff for: compiler/rustc_typeck/src/check/expr.rs

+42-60
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ use crate::errors::{
2121
};
2222
use crate::type_error_struct;
2323

24-
use super::suggest_call_constructor;
2524
use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
2625
use rustc_ast as ast;
2726
use rustc_data_structures::fx::FxHashMap;
@@ -44,7 +43,7 @@ use rustc_middle::middle::stability;
4443
use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase};
4544
use rustc_middle::ty::error::TypeError::FieldMisMatch;
4645
use rustc_middle::ty::subst::SubstsRef;
47-
use rustc_middle::ty::{self, AdtKind, DefIdTree, Ty, TypeVisitable};
46+
use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable};
4847
use rustc_session::parse::feature_err;
4948
use rustc_span::hygiene::DesugaringKind;
5049
use rustc_span::lev_distance::find_best_match_for_name;
@@ -2141,15 +2140,15 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21412140
field: Ident,
21422141
) -> Ty<'tcx> {
21432142
debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field);
2144-
let expr_t = self.check_expr(base);
2145-
let expr_t = self.structurally_resolved_type(base.span, expr_t);
2143+
let base_ty = self.check_expr(base);
2144+
let base_ty = self.structurally_resolved_type(base.span, base_ty);
21462145
let mut private_candidate = None;
2147-
let mut autoderef = self.autoderef(expr.span, expr_t);
2148-
while let Some((base_t, _)) = autoderef.next() {
2149-
debug!("base_t: {:?}", base_t);
2150-
match base_t.kind() {
2146+
let mut autoderef = self.autoderef(expr.span, base_ty);
2147+
while let Some((deref_base_ty, _)) = autoderef.next() {
2148+
debug!("deref_base_ty: {:?}", deref_base_ty);
2149+
match deref_base_ty.kind() {
21512150
ty::Adt(base_def, substs) if !base_def.is_enum() => {
2152-
debug!("struct named {:?}", base_t);
2151+
debug!("struct named {:?}", deref_base_ty);
21532152
let (ident, def_scope) =
21542153
self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id);
21552154
let fields = &base_def.non_enum_variant().fields;
@@ -2197,23 +2196,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
21972196
// (#90483) apply adjustments to avoid ExprUseVisitor from
21982197
// creating erroneous projection.
21992198
self.apply_adjustments(base, adjustments);
2200-
self.ban_private_field_access(expr, expr_t, field, did);
2199+
self.ban_private_field_access(expr, base_ty, field, did);
22012200
return field_ty;
22022201
}
22032202

22042203
if field.name == kw::Empty {
2205-
} else if self.method_exists(field, expr_t, expr.hir_id, true) {
2206-
self.ban_take_value_of_method(expr, expr_t, field);
2207-
} else if !expr_t.is_primitive_ty() {
2208-
self.ban_nonexisting_field(field, base, expr, expr_t);
2204+
} else if self.method_exists(field, base_ty, expr.hir_id, true) {
2205+
self.ban_take_value_of_method(expr, base_ty, field);
2206+
} else if !base_ty.is_primitive_ty() {
2207+
self.ban_nonexisting_field(field, base, expr, base_ty);
22092208
} else {
22102209
let field_name = field.to_string();
22112210
let mut err = type_error_struct!(
22122211
self.tcx().sess,
22132212
field.span,
2214-
expr_t,
2213+
base_ty,
22152214
E0610,
2216-
"`{expr_t}` is a primitive type and therefore doesn't have fields",
2215+
"`{base_ty}` is a primitive type and therefore doesn't have fields",
22172216
);
22182217
let is_valid_suffix = |field: &str| {
22192218
if field == "f32" || field == "f64" {
@@ -2251,7 +2250,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22512250
None
22522251
}
22532252
};
2254-
if let ty::Infer(ty::IntVar(_)) = expr_t.kind()
2253+
if let ty::Infer(ty::IntVar(_)) = base_ty.kind()
22552254
&& let ExprKind::Lit(Spanned {
22562255
node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed),
22572256
..
@@ -2280,35 +2279,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
22802279
self.tcx().ty_error()
22812280
}
22822281

2283-
fn check_call_constructor(
2284-
&self,
2285-
err: &mut Diagnostic,
2286-
base: &'tcx hir::Expr<'tcx>,
2287-
def_id: DefId,
2288-
) {
2289-
if let Some(local_id) = def_id.as_local() {
2290-
let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id);
2291-
let node = self.tcx.hir().get(hir_id);
2292-
2293-
if let Some(fields) = node.tuple_fields() {
2294-
let kind = match self.tcx.opt_def_kind(local_id) {
2295-
Some(DefKind::Ctor(of, _)) => of,
2296-
_ => return,
2297-
};
2298-
2299-
suggest_call_constructor(base.span, kind, fields.len(), err);
2300-
}
2301-
} else {
2302-
// The logic here isn't smart but `associated_item_def_ids`
2303-
// doesn't work nicely on local.
2304-
if let DefKind::Ctor(of, _) = self.tcx.def_kind(def_id) {
2305-
let parent_def_id = self.tcx.parent(def_id);
2306-
let fields = self.tcx.associated_item_def_ids(parent_def_id);
2307-
suggest_call_constructor(base.span, of, fields.len(), err);
2308-
}
2309-
}
2310-
}
2311-
23122282
fn suggest_await_on_field_access(
23132283
&self,
23142284
err: &mut Diagnostic,
@@ -2351,40 +2321,52 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
23512321

23522322
fn ban_nonexisting_field(
23532323
&self,
2354-
field: Ident,
2324+
ident: Ident,
23552325
base: &'tcx hir::Expr<'tcx>,
23562326
expr: &'tcx hir::Expr<'tcx>,
2357-
expr_t: Ty<'tcx>,
2327+
base_ty: Ty<'tcx>,
23582328
) {
23592329
debug!(
2360-
"ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, expr_ty={:?}",
2361-
field, base, expr, expr_t
2330+
"ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}",
2331+
ident, base, expr, base_ty
23622332
);
2363-
let mut err = self.no_such_field_err(field, expr_t, base.hir_id);
2333+
let mut err = self.no_such_field_err(ident, base_ty, base.hir_id);
23642334

2365-
match *expr_t.peel_refs().kind() {
2335+
match *base_ty.peel_refs().kind() {
23662336
ty::Array(_, len) => {
2367-
self.maybe_suggest_array_indexing(&mut err, expr, base, field, len);
2337+
self.maybe_suggest_array_indexing(&mut err, expr, base, ident, len);
23682338
}
23692339
ty::RawPtr(..) => {
2370-
self.suggest_first_deref_field(&mut err, expr, base, field);
2340+
self.suggest_first_deref_field(&mut err, expr, base, ident);
23712341
}
23722342
ty::Adt(def, _) if !def.is_enum() => {
2373-
self.suggest_fields_on_recordish(&mut err, def, field, expr.span);
2343+
self.suggest_fields_on_recordish(&mut err, def, ident, expr.span);
23742344
}
23752345
ty::Param(param_ty) => {
23762346
self.point_at_param_definition(&mut err, param_ty);
23772347
}
23782348
ty::Opaque(_, _) => {
2379-
self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs());
2380-
}
2381-
ty::FnDef(def_id, _) => {
2382-
self.check_call_constructor(&mut err, base, def_id);
2349+
self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs());
23832350
}
23842351
_ => {}
23852352
}
23862353

2387-
if field.name == kw::Await {
2354+
self.suggest_fn_call(&mut err, base, base_ty, |output_ty| {
2355+
if let ty::Adt(def, _) = output_ty.kind() && !def.is_enum() {
2356+
def.non_enum_variant().fields.iter().any(|field| {
2357+
field.ident(self.tcx) == ident
2358+
&& field.vis.is_accessible_from(expr.hir_id.owner.to_def_id(), self.tcx)
2359+
})
2360+
} else if let ty::Tuple(tys) = output_ty.kind()
2361+
&& let Ok(idx) = ident.as_str().parse::<usize>()
2362+
{
2363+
idx < tys.len()
2364+
} else {
2365+
false
2366+
}
2367+
});
2368+
2369+
if ident.name == kw::Await {
23882370
// We know by construction that `<expr>.await` is either on Rust 2015
23892371
// or results in `ExprKind::Await`. Suggest switching the edition to 2018.
23902372
err.note("to `.await` a `Future`, switch to Rust 2018 or later");

0 commit comments

Comments
 (0)