diff options
| author | b-naber <bn263@gmx.de> | 2021-02-18 21:01:44 +0100 |
|---|---|---|
| committer | b-naber <bn263@gmx.de> | 2021-05-11 14:09:46 +0200 |
| commit | e4d9bc66f65fd3d206587c07e33c4877fda073f9 (patch) | |
| tree | 1cbfff1dc8de7fd37b9c550e063d9e6256599676 /compiler | |
| parent | fe62c6e2958abfe54a9410a24a5750baf4c157e0 (diff) | |
| download | rust-e4d9bc66f65fd3d206587c07e33c4877fda073f9.tar.gz rust-e4d9bc66f65fd3d206587c07e33c4877fda073f9.zip | |
improve diagnosts for GATs
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/rustc_ast_lowering/src/path.rs | 5 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/middle/resolve_lifetime.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/query/mod.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/context.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_middle/src/ty/query/mod.rs | 4 | ||||
| -rw-r--r-- | compiler/rustc_resolve/src/late/lifetimes.rs | 132 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/astconv/generics.rs | 134 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/astconv/mod.rs | 11 | ||||
| -rw-r--r-- | compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs | 800 |
9 files changed, 849 insertions, 261 deletions
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs index 46dac2f1af4..82a0983e2a1 100644 --- a/compiler/rustc_ast_lowering/src/path.rs +++ b/compiler/rustc_ast_lowering/src/path.rs @@ -24,6 +24,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { param_mode: ParamMode, mut itctx: ImplTraitContext<'_, 'hir>, ) -> hir::QPath<'hir> { + debug!("lower_qpath(id: {:?}, qself: {:?}, p: {:?})", id, qself, p); let qself_position = qself.as_ref().map(|q| q.position); let qself = qself.as_ref().map(|q| self.lower_ty(&q.ty, itctx.reborrow())); @@ -222,6 +223,10 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { itctx: ImplTraitContext<'_, 'hir>, explicit_owner: Option<NodeId>, ) -> hir::PathSegment<'hir> { + debug!( + "path_span: {:?}, lower_path_segment(segment: {:?}, expected_lifetimes: {:?})", + path_span, segment, expected_lifetimes + ); let (mut generic_args, infer_args) = if let Some(ref generic_args) = segment.args { let msg = "parenthesized type parameters may only be used with a `Fn` trait"; match **generic_args { diff --git a/compiler/rustc_middle/src/middle/resolve_lifetime.rs b/compiler/rustc_middle/src/middle/resolve_lifetime.rs index aa6488b329e..2665ea8d7fd 100644 --- a/compiler/rustc_middle/src/middle/resolve_lifetime.rs +++ b/compiler/rustc_middle/src/middle/resolve_lifetime.rs @@ -49,6 +49,20 @@ pub enum Region { Free(DefId, /* lifetime decl */ DefId), } +/// This is used in diagnostics to improve suggestions for missing generic arguments. +/// It gives information on the type of lifetimes that are in scope for a particular `PathSegment`, +/// so that we can e.g. suggest elided-lifetimes-in-paths of the form <'_, '_> e.g. +#[derive(Clone, PartialEq, Eq, Hash, TyEncodable, TyDecodable, Debug, HashStable)] +pub enum LifetimeScopeForPath { + // Contains all lifetime names that are in scope and could possibly be used in generics + // arguments of path. + NonElided(Vec<String>), + + // Information that allows us to suggest args of the form `<'_>` in case + // no generic arguments were provided for a path. + Elided, +} + /// A set containing, at most, one known element. /// If two distinct values are inserted into a set, then it /// becomes `Many`, which can be used to detect ambiguities. diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 3ffc3641b62..3c5440f5b68 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -1301,6 +1301,10 @@ rustc_queries! { desc { "looking up late bound vars" } } + query lifetime_scope_map(_: LocalDefId) -> Option<FxHashMap<ItemLocalId, LifetimeScopeForPath>> { + desc { "finds the lifetime scope for an HirId of a PathSegment" } + } + query visibility(def_id: DefId) -> ty::Visibility { eval_always desc { |tcx| "computing visibility of `{}`", tcx.def_path_str(def_id) } diff --git a/compiler/rustc_middle/src/ty/context.rs b/compiler/rustc_middle/src/ty/context.rs index 10f7cc87c7c..eaf0887c4e7 100644 --- a/compiler/rustc_middle/src/ty/context.rs +++ b/compiler/rustc_middle/src/ty/context.rs @@ -9,7 +9,7 @@ use crate::infer::canonical::{Canonical, CanonicalVarInfo, CanonicalVarInfos}; use crate::lint::{struct_lint_level, LintDiagnosticBuilder, LintLevelSource}; use crate::middle; use crate::middle::cstore::{CrateStoreDyn, EncodedMetadata}; -use crate::middle::resolve_lifetime::{self, ObjectLifetimeDefault}; +use crate::middle::resolve_lifetime::{self, LifetimeScopeForPath, ObjectLifetimeDefault}; use crate::middle::stability; use crate::mir::interpret::{self, Allocation, ConstValue, Scalar}; use crate::mir::{Body, Field, Local, Place, PlaceElem, ProjectionKind, Promoted}; @@ -2686,6 +2686,10 @@ impl<'tcx> TyCtxt<'tcx> { .iter(), ) } + + pub fn lifetime_scope(self, id: HirId) -> Option<LifetimeScopeForPath> { + self.lifetime_scope_map(id.owner).and_then(|mut map| map.remove(&id.local_id)) + } } impl TyCtxtAt<'tcx> { diff --git a/compiler/rustc_middle/src/ty/query/mod.rs b/compiler/rustc_middle/src/ty/query/mod.rs index 068f8148611..3c772a14647 100644 --- a/compiler/rustc_middle/src/ty/query/mod.rs +++ b/compiler/rustc_middle/src/ty/query/mod.rs @@ -9,7 +9,9 @@ use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel}; use crate::middle::lib_features::LibFeatures; use crate::middle::privacy::AccessLevels; use crate::middle::region; -use crate::middle::resolve_lifetime::{ObjectLifetimeDefault, Region, ResolveLifetimes}; +use crate::middle::resolve_lifetime::{ + LifetimeScopeForPath, ObjectLifetimeDefault, Region, ResolveLifetimes, +}; use crate::middle::stability::{self, DeprecationEntry}; use crate::mir; use crate::mir::interpret::GlobalId; diff --git a/compiler/rustc_resolve/src/late/lifetimes.rs b/compiler/rustc_resolve/src/late/lifetimes.rs index e8d21af4358..bf69defb4e6 100644 --- a/compiler/rustc_resolve/src/late/lifetimes.rs +++ b/compiler/rustc_resolve/src/late/lifetimes.rs @@ -8,11 +8,11 @@ use crate::late::diagnostics::{ForLifetimeSpanType, MissingLifetimeSpot}; use rustc_ast::walk_list; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap}; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder}; use rustc_hir as hir; use rustc_hir::def::{DefKind, Res}; -use rustc_hir::def_id::DefIdMap; +use rustc_hir::def_id::{DefIdMap, LocalDefId}; use rustc_hir::hir_id::ItemLocalId; use rustc_hir::intravisit::{self, NestedVisitorMap, Visitor}; use rustc_hir::{GenericArg, GenericParam, LifetimeName, Node, ParamName, QPath}; @@ -22,7 +22,7 @@ use rustc_middle::middle::resolve_lifetime::*; use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, TyCtxt}; use rustc_middle::{bug, span_bug}; use rustc_session::lint; -use rustc_span::def_id::{DefId, LocalDefId}; +use rustc_span::def_id::DefId; use rustc_span::symbol::{kw, sym, Ident, Symbol}; use rustc_span::Span; use std::borrow::Cow; @@ -158,6 +158,9 @@ struct NamedRegionMap { // - trait refs // - bound types (like `T` in `for<'a> T<'a>: Foo`) late_bound_vars: HirIdMap<Vec<ty::BoundVariableKind>>, + + // maps `PathSegment` `HirId`s to lifetime scopes. + scope_for_path: Option<FxHashMap<LocalDefId, FxHashMap<ItemLocalId, LifetimeScopeForPath>>>, } crate struct LifetimeContext<'a, 'tcx> { @@ -195,7 +198,9 @@ enum Scope<'a> { /// it should be shifted by the number of `Binder`s in between the /// declaration `Binder` and the location it's referenced from. Binder { - lifetimes: FxHashMap<hir::ParamName, Region>, + /// We use an IndexMap here because we want these lifetimes in order + /// for diagnostics. + lifetimes: FxIndexMap<hir::ParamName, Region>, /// if we extend this scope with another scope, what is the next index /// we should use for an early-bound region? @@ -379,6 +384,10 @@ pub fn provide(providers: &mut ty::query::Providers) { } }, late_bound_vars_map: |tcx, id| resolve_lifetimes_for(tcx, id).late_bound_vars.get(&id), + lifetime_scope_map: |tcx, id| { + let item_id = item_for(tcx, id); + do_resolve(tcx, item_id, false, true).scope_for_path.unwrap().remove(&id) + }, ..*providers }; @@ -419,7 +428,7 @@ fn resolve_lifetimes_trait_definition( tcx: TyCtxt<'_>, local_def_id: LocalDefId, ) -> ResolveLifetimes { - do_resolve(tcx, local_def_id, true) + convert_named_region_map(do_resolve(tcx, local_def_id, true, false)) } /// Computes the `ResolveLifetimes` map that contains data for an entire `Item`. @@ -427,19 +436,21 @@ fn resolve_lifetimes_trait_definition( /// `named_region_map`, `is_late_bound_map`, etc. #[tracing::instrument(level = "debug", skip(tcx))] fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> ResolveLifetimes { - do_resolve(tcx, local_def_id, false) + convert_named_region_map(do_resolve(tcx, local_def_id, false, false)) } fn do_resolve( tcx: TyCtxt<'_>, local_def_id: LocalDefId, trait_definition_only: bool, -) -> ResolveLifetimes { + with_scope_for_path: bool, +) -> NamedRegionMap { let item = tcx.hir().expect_item(tcx.hir().local_def_id_to_hir_id(local_def_id)); let mut named_region_map = NamedRegionMap { defs: Default::default(), late_bound: Default::default(), late_bound_vars: Default::default(), + scope_for_path: with_scope_for_path.then(|| Default::default()), }; let mut visitor = LifetimeContext { tcx, @@ -455,6 +466,10 @@ fn do_resolve( }; visitor.visit_item(item); + named_region_map +} + +fn convert_named_region_map(named_region_map: NamedRegionMap) -> ResolveLifetimes { let mut rl = ResolveLifetimes::default(); for (hir_id, v) in named_region_map.defs { @@ -567,6 +582,41 @@ fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty:: } } +#[tracing::instrument(level = "debug")] +fn get_lifetime_scopes_for_path(mut scope: &Scope<'_>) -> LifetimeScopeForPath { + let mut available_lifetimes = vec![]; + loop { + match scope { + Scope::Binder { lifetimes, s, .. } => { + available_lifetimes.extend(lifetimes.keys().filter_map(|p| match p { + hir::ParamName::Plain(ident) => Some(ident.name.to_string()), + _ => None, + })); + scope = s; + } + Scope::Body { s, .. } => { + scope = s; + } + Scope::Elision { elide, s } => { + if let Elide::Exact(_) = elide { + return LifetimeScopeForPath::Elided; + } else { + scope = s; + } + } + Scope::ObjectLifetimeDefault { s, .. } => { + scope = s; + } + Scope::Root => { + return LifetimeScopeForPath::NonElided(available_lifetimes); + } + Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => { + scope = s; + } + } + } +} + impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { /// Returns the binders in scope and the type of `Binder` that should be created for a poly trait ref. fn poly_trait_ref_binder_info(&mut self) -> (Vec<ty::BoundVariableKind>, BinderScopeType) { @@ -656,7 +706,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { self.map.late_bound_vars.insert(hir_id, vec![]); let scope = Scope::Binder { hir_id, - lifetimes: FxHashMap::default(), + lifetimes: FxIndexMap::default(), next_early_index: self.next_early_index(), s: self.scope, track_lifetime_uses: true, @@ -720,9 +770,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { // We need to add *all* deps, since opaque tys may want them from *us* for (&owner, defs) in resolved_lifetimes.defs.iter() { defs.iter().for_each(|(&local_id, region)| { - self.map - .defs - .insert(hir::HirId { owner, local_id }, region.clone()); + self.map.defs.insert(hir::HirId { owner, local_id }, *region); }); } for (&owner, late_bound) in resolved_lifetimes.late_bound.iter() { @@ -836,7 +884,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { }; self.missing_named_lifetime_spots .push(MissingLifetimeSpot::HigherRanked { span, span_type }); - let (lifetimes, binders): (FxHashMap<hir::ParamName, Region>, Vec<_>) = c + let (lifetimes, binders): (FxIndexMap<hir::ParamName, Region>, Vec<_>) = c .generic_params .iter() .filter_map(|param| match param.kind { @@ -1010,7 +1058,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { debug!(?index); let mut elision = None; - let mut lifetimes = FxHashMap::default(); + let mut lifetimes = FxIndexMap::default(); let mut non_lifetime_count = 0; for param in generics.params { match param.kind { @@ -1181,7 +1229,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let mut index = self.next_early_index(); let mut non_lifetime_count = 0; debug!("visit_ty: index = {}", index); - let lifetimes: FxHashMap<hir::ParamName, Region> = generics + let lifetimes: FxIndexMap<hir::ParamName, Region> = generics .params .iter() .filter_map(|param| match param.kind { @@ -1241,15 +1289,53 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { self.resolve_lifetime_ref(lifetime_ref); } + fn visit_assoc_type_binding(&mut self, type_binding: &'tcx hir::TypeBinding<'_>) { + let scope = self.scope; + if let Some(scope_for_path) = self.map.scope_for_path.as_mut() { + // We add lifetime scope information for `Ident`s in associated type bindings and use + // the `HirId` of the type binding as the key in `LifetimeMap` + let lifetime_scope = get_lifetime_scopes_for_path(scope); + let map = scope_for_path.entry(type_binding.hir_id.owner).or_default(); + map.insert(type_binding.hir_id.local_id, lifetime_scope); + } + hir::intravisit::walk_assoc_type_binding(self, type_binding); + } + fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) { for (i, segment) in path.segments.iter().enumerate() { let depth = path.segments.len() - i - 1; if let Some(ref args) = segment.args { self.visit_segment_args(path.res, depth, args); } + + let scope = self.scope; + if let Some(scope_for_path) = self.map.scope_for_path.as_mut() { + // Add lifetime scope information to path segment. Note we cannot call `visit_path_segment` + // here because that call would yield to resolution problems due to `walk_path_segment` + // being called, which processes the path segments generic args, which we have already + // processed using `visit_segment_args`. + let lifetime_scope = get_lifetime_scopes_for_path(scope); + if let Some(hir_id) = segment.hir_id { + let map = scope_for_path.entry(hir_id.owner).or_default(); + map.insert(hir_id.local_id, lifetime_scope); + } + } } } + fn visit_path_segment(&mut self, path_span: Span, path_segment: &'tcx hir::PathSegment<'tcx>) { + let scope = self.scope; + if let Some(scope_for_path) = self.map.scope_for_path.as_mut() { + let lifetime_scope = get_lifetime_scopes_for_path(scope); + if let Some(hir_id) = path_segment.hir_id { + let map = scope_for_path.entry(hir_id.owner).or_default(); + map.insert(hir_id.local_id, lifetime_scope); + } + } + + intravisit::walk_path_segment(self, path_span, path_segment); + } + fn visit_fn_decl(&mut self, fd: &'tcx hir::FnDecl<'tcx>) { let output = match fd.output { hir::FnRetTy::DefaultReturn(_) => None, @@ -1290,7 +1376,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { ref bound_generic_params, .. }) => { - let (lifetimes, binders): (FxHashMap<hir::ParamName, Region>, Vec<_>) = + let (lifetimes, binders): (FxIndexMap<hir::ParamName, Region>, Vec<_>) = bound_generic_params .iter() .filter_map(|param| match param.kind { @@ -1360,7 +1446,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { self.map.late_bound_vars.insert(*hir_id, binders); let scope = Scope::Binder { hir_id: *hir_id, - lifetimes: FxHashMap::default(), + lifetimes: FxIndexMap::default(), s: self.scope, next_early_index: self.next_early_index(), track_lifetime_uses: true, @@ -1388,7 +1474,7 @@ impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> { let (mut binders, scope_type) = self.poly_trait_ref_binder_info(); let initial_bound_vars = binders.len() as u32; - let mut lifetimes: FxHashMap<hir::ParamName, Region> = FxHashMap::default(); + let mut lifetimes: FxIndexMap<hir::ParamName, Region> = FxIndexMap::default(); let binders_iter = trait_ref .bound_generic_params .iter() @@ -2115,7 +2201,7 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { let mut non_lifetime_count = 0; let mut named_late_bound_vars = 0; - let lifetimes: FxHashMap<hir::ParamName, Region> = generics + let lifetimes: FxIndexMap<hir::ParamName, Region> = generics .params .iter() .filter_map(|param| match param.kind { @@ -3034,6 +3120,16 @@ impl<'a, 'tcx> LifetimeContext<'a, 'tcx> { } }; + // If we specifically need the `scope_for_path` map, then we're in the + // diagnostic pass and we don't want to emit more errors. + if self.map.scope_for_path.is_some() { + self.tcx.sess.delay_span_bug( + rustc_span::DUMMY_SP, + "Encountered unexpected errors during diagnostics related part", + ); + return; + } + let mut spans: Vec<_> = lifetime_refs.iter().map(|lt| lt.span).collect(); spans.sort(); let mut spans_dedup = spans.clone(); diff --git a/compiler/rustc_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs index 077375c7c3b..456f2a908a8 100644 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ b/compiler/rustc_typeck/src/astconv/generics.rs @@ -4,7 +4,7 @@ use crate::astconv::{ GenericArgCountResult, GenericArgPosition, }; use crate::errors::AssocTypeBindingNotAllowed; -use crate::structured_errors::{StructuredDiagnostic, WrongNumberOfGenericArgs}; +use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs}; use rustc_ast::ast::ParamKindOrd; use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorReported}; use rustc_hir as hir; @@ -438,6 +438,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { has_self: bool, infer_args: bool, ) -> GenericArgCountResult { + debug!( + "check_generic_arg_count(span: {:?}, def_id: {:?}, seg: {:?}, gen_params: {:?}, gen_args: {:?})", + span, def_id, seg, gen_params, gen_args + ); + let default_counts = gen_params.own_defaults(); let param_counts = gen_params.own_counts(); let named_type_param_count = param_counts.types - has_self as usize; @@ -453,63 +458,116 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let mut invalid_args = vec![]; - let mut check_generics = - |kind, expected_min, expected_max, provided, params_offset, args_offset, silent| { + let mut check_lifetime_args = |min_expected_args: usize, + max_expected_args: usize, + provided_args: usize, + late_bounds_ignore: bool| + -> bool { + if (min_expected_args..=max_expected_args).contains(&provided_args) { + return true; + } + + if late_bounds_ignore { + return true; + } + + if provided_args > max_expected_args { + invalid_args.extend( + gen_args.args[max_expected_args..provided_args].iter().map(|arg| arg.span()), + ); + }; + + let gen_args_info = if provided_args > min_expected_args { + invalid_args.extend( + gen_args.args[min_expected_args..provided_args].iter().map(|arg| arg.span()), + ); + let num_redundant_args = provided_args - min_expected_args; + GenericArgsInfo::ExcessLifetimes { num_redundant_args } + } else { + let num_missing_args = min_expected_args - provided_args; + GenericArgsInfo::MissingLifetimes { num_missing_args } + }; + + WrongNumberOfGenericArgs::new( + tcx, + gen_args_info, + seg, + gen_params, + has_self as usize, + gen_args, + def_id, + ) + .diagnostic() + .emit(); + + false + }; + + let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes }; + let max_expected_lifetime_args = param_counts.lifetimes; + let num_provided_lifetime_args = arg_counts.lifetimes; + + let lifetimes_correct = check_lifetime_args( + min_expected_lifetime_args, + max_expected_lifetime_args, + num_provided_lifetime_args, + explicit_late_bound == ExplicitLateBound::Yes, + ); + + let mut check_types_and_consts = + |expected_min, expected_max, provided, params_offset, args_offset| { + debug!( + "check_types_and_consts(expected_min: {:?}, expected_max: {:?}, \ + provided: {:?}, params_offset: {:?}, args_offset: {:?}", + expected_min, expected_max, provided, params_offset, args_offset + ); if (expected_min..=expected_max).contains(&provided) { return true; } - if silent { - return true; - } + let num_default_params = expected_max - expected_min; - if provided > expected_max { + let gen_args_info = if provided > expected_max { invalid_args.extend( gen_args.args[args_offset + expected_max..args_offset + provided] .iter() .map(|arg| arg.span()), ); + let num_redundant_args = provided - expected_max; + + GenericArgsInfo::ExcessTypesOrConsts { + num_redundant_args, + num_default_params, + args_offset, + } + } else { + let num_missing_args = expected_max - provided; + + GenericArgsInfo::MissingTypesOrConsts { + num_missing_args, + num_default_params, + args_offset, + } }; - WrongNumberOfGenericArgs { + debug!("gen_args_info: {:?}", gen_args_info); + + WrongNumberOfGenericArgs::new( tcx, - kind, - expected_min, - expected_max, - provided, - params_offset, - args_offset, - path_segment: seg, + gen_args_info, + seg, gen_params, + params_offset, gen_args, def_id, - span, - } + ) .diagnostic() .emit(); false }; - let lifetimes_correct = check_generics( - "lifetime", - if infer_lifetimes { 0 } else { param_counts.lifetimes }, - param_counts.lifetimes, - arg_counts.lifetimes, - has_self as usize, - 0, - explicit_late_bound == ExplicitLateBound::Yes, - ); - let args_correct = { - let kind = if param_counts.consts + arg_counts.consts == 0 { - "type" - } else if named_type_param_count + arg_counts.types == 0 { - "const" - } else { - "generic" - }; - let expected_min = if infer_args { 0 } else { @@ -517,15 +575,15 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { - default_counts.types - default_counts.consts }; + debug!("expected_min: {:?}", expected_min); + debug!("arg_counts.lifetimes: {:?}", arg_counts.lifetimes); - check_generics( - kind, + check_types_and_consts( expected_min, param_counts.consts + named_type_param_count, arg_counts.consts + arg_counts.types, param_counts.lifetimes + has_self as usize, arg_counts.lifetimes, - false, ) }; diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs index 2f2e90e4bd6..7696a0c69ca 100644 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ b/compiler/rustc_typeck/src/astconv/mod.rs @@ -120,6 +120,7 @@ pub enum SizedByDefault { #[derive(Debug)] struct ConvertedBinding<'a, 'tcx> { + hir_id: hir::HirId, item_name: Ident, kind: ConvertedBindingKind<'a, 'tcx>, gen_args: &'a GenericArgs<'a>, @@ -590,6 +591,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { } }; ConvertedBinding { + hir_id: binding.hir_id, item_name: binding.ident, kind, gen_args: binding.gen_args, @@ -609,6 +611,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { item_segment: &hir::PathSegment<'_>, parent_substs: SubstsRef<'tcx>, ) -> SubstsRef<'tcx> { + debug!( + "create_substs_for_associated_item(span: {:?}, item_def_id: {:?}, item_segment: {:?}", + span, item_def_id, item_segment + ); if tcx.generics_of(item_def_id).params.is_empty() { self.prohibit_generics(slice::from_ref(item_segment)); @@ -1071,9 +1077,10 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { // Include substitutions for generic parameters of associated types let projection_ty = candidate.map_bound(|trait_ref| { + let ident = Ident::new(assoc_ty.ident.name, binding.item_name.span); let item_segment = hir::PathSegment { - ident: assoc_ty.ident, - hir_id: None, + ident, + hir_id: Some(binding.hir_id), res: None, args: Some(binding.gen_args), infer_args: false, diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs index e35c1557466..c533ca28321 100644 --- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs +++ b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs @@ -1,34 +1,20 @@ use crate::structured_errors::StructuredDiagnostic; -use hir::def::DefKind; use rustc_errors::{pluralize, Applicability, DiagnosticBuilder, DiagnosticId}; use rustc_hir as hir; +use rustc_middle::middle::resolve_lifetime::LifetimeScopeForPath; use rustc_middle::ty::{self as ty, TyCtxt}; use rustc_session::Session; -use rustc_span::Span; use rustc_span::{def_id::DefId, MultiSpan}; +use GenericArgsInfo::*; + /// Handles the `wrong number of type / lifetime / ... arguments` family of error messages. pub struct WrongNumberOfGenericArgs<'a, 'tcx> { crate tcx: TyCtxt<'tcx>, - /// "type", "lifetime" etc., put verbatim into the message - crate kind: &'static str, - - /// Minimum number of expected generic arguments (e.g. `2` for `HashMap`) - crate expected_min: usize, + crate angle_brackets: AngleBrackets, - /// Maximum number of expected generic arguments (e.g. `3` for `HashMap`) - crate expected_max: usize, - - /// Number of generic arguments provided by the user - crate provided: usize, - - /// Offset into `gen_params` - depends on the `kind`; might be different than `args_offset` when - /// user passed e.g. more arguments than was actually expected - crate params_offset: usize, - - /// Offset into `gen_args` - depends on the `kind` - crate args_offset: usize, + crate gen_args_info: GenericArgsInfo, /// Offending path segment crate path_segment: &'a hir::PathSegment<'a>, @@ -36,64 +22,348 @@ pub struct WrongNumberOfGenericArgs<'a, 'tcx> { /// Generic parameters as expected by type or trait crate gen_params: &'a ty::Generics, + /// Index offset into parameters. Depends on whether `Self` is included and on + /// number of lifetime parameters in case we're processing missing or redundant + /// type or constant arguments. + crate params_offset: usize, + /// Generic arguments as provided by user crate gen_args: &'a hir::GenericArgs<'a>, /// DefId of the generic type crate def_id: DefId, +} + +// Provides information about the kind of arguments that were provided for +// the PathSegment, for which missing generic arguments were detected +#[derive(Debug)] +pub(crate) enum AngleBrackets { + // No angle brackets were provided, but generic arguments exist in elided form + Implied, + + // No angle brackets were provided + Missing, + + // Angle brackets are available, but missing some generic arguments + Available, +} - /// Offending place where the generic type has been misused - crate span: Span, +// Information about the kind of arguments that are either missing or are unexpected +#[derive(Debug)] +pub enum GenericArgsInfo { + MissingLifetimes { + num_missing_args: usize, + }, + ExcessLifetimes { + num_redundant_args: usize, + }, + MissingTypesOrConsts { + num_missing_args: usize, + + // type or const generic arguments can have default values + num_default_params: usize, + + // lifetime arguments precede type and const parameters, this + // field gives the number of generic lifetime arguments to let + // us infer the position of type and const generic arguments + // in the angle brackets + args_offset: usize, + }, + + ExcessTypesOrConsts { + num_redundant_args: usize, + + // type or const generic arguments can have default values + num_default_params: usize, + + // lifetime arguments precede type and const parameters, this + // field gives the number of generic lifetime arguments to let + // us infer the position of type and const generic arguments + // in the angle brackets + args_offset: usize, + }, } -impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> { - fn quantifier_and_bound(&self) -> (&'static str, usize) { - if self.expected_min == self.expected_max { - ("", self.expected_min) - } else if self.provided < self.expected_min { - ("at least ", self.expected_min) +impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { + pub fn new( + tcx: TyCtxt<'tcx>, + gen_args_info: GenericArgsInfo, + path_segment: &'a hir::PathSegment<'_>, + gen_params: &'a ty::Generics, + params_offset: usize, + gen_args: &'a hir::GenericArgs<'a>, + def_id: DefId, + ) -> Self { + let angle_brackets = if gen_args.is_empty() { + AngleBrackets::Missing } else { - ("at most ", self.expected_max) + if gen_args.span().is_none() { + AngleBrackets::Implied + } else { + AngleBrackets::Available + } + }; + + Self { + tcx, + angle_brackets, + gen_args_info, + path_segment, + gen_params, + params_offset, + gen_args, + def_id, } } - fn start_diagnostics(&self) -> DiagnosticBuilder<'tcx> { - let span = self.path_segment.ident.span; + fn missing_lifetimes(&self) -> bool { + match self.gen_args_info { + MissingLifetimes { .. } | ExcessLifetimes { .. } => true, + MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => false, + } + } - let msg = { - let def_path = self.tcx.def_path_str(self.def_id); - let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id); - let (quantifier, bound) = self.quantifier_and_bound(); + fn kind(&self) -> String { + if self.missing_lifetimes() { "lifetime".to_string() } else { "generic".to_string() } + } - if self.gen_args.span().is_some() { - format!( - "this {} takes {}{} {} argument{} but {}{} {} argument{} {} supplied", - def_kind, - quantifier, - bound, - self.kind, - pluralize!(bound), - if self.provided > 0 && self.provided < self.expected_min { - "only " - } else { - "" - }, - self.provided, - self.kind, - pluralize!(self.provided), - if self.provided == 1 { "was" } else { "were" }, - ) - } else { - format!("missing generics for {} `{}`", def_kind, def_path) + fn num_provided_args(&self) -> usize { + if self.missing_lifetimes() { + self.num_provided_lifetime_args() + } else { + self.num_provided_type_or_const_args() + } + } + + fn num_provided_lifetime_args(&self) -> usize { + match self.angle_brackets { + AngleBrackets::Missing => 0, + // Only lifetime arguments can be implied + AngleBrackets::Implied => self.gen_args.args.len(), + AngleBrackets::Available => self.gen_args.args.iter().fold(0, |acc, arg| match arg { + hir::GenericArg::Lifetime(_) => acc + 1, + _ => acc, + }), + } + } + + fn num_provided_type_or_const_args(&self) -> usize { + match self.angle_brackets { + AngleBrackets::Missing => 0, + // Only lifetime arguments can be implied + AngleBrackets::Implied => 0, + AngleBrackets::Available => self.gen_args.args.iter().fold(0, |acc, arg| match arg { + hir::GenericArg::Type(_) | hir::GenericArg::Const(_) => acc + 1, + _ => acc, + }), + } + } + + fn num_expected_lifetime_args(&self) -> usize { + let num_provided_args = self.num_provided_lifetime_args(); + match self.gen_args_info { + MissingLifetimes { num_missing_args } => num_provided_args + num_missing_args, + ExcessLifetimes { num_redundant_args } => num_provided_args - num_redundant_args, + _ => 0, + } + } + + fn num_expected_type_or_const_args(&self) -> usize { + let num_provided_args = self.num_provided_type_or_const_args(); + match self.gen_args_info { + MissingTypesOrConsts { num_missing_args, .. } => num_provided_args + num_missing_args, + ExcessTypesOrConsts { num_redundant_args, .. } => { + num_provided_args - num_redundant_args + } + _ => 0, + } + } + + // Gives the number of expected arguments taking into account default arguments + fn num_expected_type_or_const_args_including_defaults(&self) -> usize { + let provided_args = self.num_provided_type_or_const_args(); + match self.gen_args_info { + MissingTypesOrConsts { num_missing_args, num_default_params, .. } => { + provided_args + num_missing_args - num_default_params + } + ExcessTypesOrConsts { num_redundant_args, num_default_params, .. } => { + provided_args - num_redundant_args - num_default_params + } + _ => 0, + } + } + + fn num_missing_lifetime_args(&self) -> usize { + let missing_args = self.num_expected_lifetime_args() - self.num_provided_lifetime_args(); + assert!(missing_args > 0); + missing_args + } + + fn num_missing_type_or_const_args(&self) -> usize { + let missing_args = self.num_expected_type_or_const_args_including_defaults() + - self.num_provided_type_or_const_args(); + assert!(missing_args > 0); + missing_args + } + + fn num_excess_lifetime_args(&self) -> usize { + match self.gen_args_info { + ExcessLifetimes { num_redundant_args } => num_redundant_args, + _ => 0, + } + } + + fn num_excess_type_or_const_args(&self) -> usize { + match self.gen_args_info { + ExcessTypesOrConsts { num_redundant_args, .. } => num_redundant_args, + _ => 0, + } + } + + fn too_many_args_provided(&self) -> bool { + match self.gen_args_info { + MissingLifetimes { .. } | MissingTypesOrConsts { .. } => false, + ExcessLifetimes { num_redundant_args } + | ExcessTypesOrConsts { num_redundant_args, .. } => { + assert!(num_redundant_args > 0); + true + } + } + } + + fn not_enough_args_provided(&self) -> bool { + match self.gen_args_info { + MissingLifetimes { num_missing_args } + | MissingTypesOrConsts { num_missing_args, .. } => { + assert!(num_missing_args > 0); + true + } + ExcessLifetimes { .. } | ExcessTypesOrConsts { .. } => false, + } + } + + // Helper method to get the index offset in angle brackets, at which type or const arguments + // start appearing + fn get_lifetime_args_offset(&self) -> usize { + match self.gen_args_info { + MissingLifetimes { .. } | ExcessLifetimes { .. } => 0, + MissingTypesOrConsts { args_offset, .. } | ExcessTypesOrConsts { args_offset, .. } => { + args_offset } + } + } + + fn get_num_default_params(&self) -> usize { + match self.gen_args_info { + MissingTypesOrConsts { num_default_params, .. } + | ExcessTypesOrConsts { num_default_params, .. } => num_default_params, + _ => 0, + } + } + + // Helper function to choose a quantifier word for the number of expected arguments + // and to give a bound for the number of expected arguments + fn get_quantifier_and_bound(&self) -> (&'static str, usize) { + if self.get_num_default_params() == 0 { + match self.gen_args_info { + MissingLifetimes { .. } | ExcessLifetimes { .. } => { + ("", self.num_expected_lifetime_args()) + } + MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => { + ("", self.num_expected_type_or_const_args()) + } + } + } else { + match self.gen_args_info { + MissingLifetimes { .. } => ("at least ", self.num_expected_lifetime_args()), + MissingTypesOrConsts { .. } => { + ("at least ", self.num_expected_type_or_const_args_including_defaults()) + } + ExcessLifetimes { .. } => ("at most ", self.num_expected_lifetime_args()), + ExcessTypesOrConsts { .. } => ("at most ", self.num_expected_type_or_const_args()), + } + } + } + + // Creates lifetime name suggestions from the lifetime parameter names + fn get_lifetime_args_suggestions_from_param_names(&self, num_params_to_take: usize) -> String { + self.gen_params + .params + .iter() + .skip(self.params_offset + self.num_provided_lifetime_args()) + .take(num_params_to_take) + .map(|param| param.name.to_string()) + .collect::<Vec<_>>() + .join(", ") + } + + // Creates type or constant name suggestions from the provided parameter names + fn get_type_or_const_args_suggestions_from_param_names( + &self, + num_params_to_take: usize, + ) -> String { + self.gen_params + .params + .iter() + .skip(self.params_offset + self.num_provided_type_or_const_args()) + .take(num_params_to_take) + .map(|param| param.name.to_string()) + .collect::<Vec<_>>() + .join(", ") + } + + fn create_error_message(&self) -> String { + let def_path = self.tcx.def_path_str(self.def_id); + let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id); + let (quantifier, bound) = self.get_quantifier_and_bound(); + let kind = self.kind(); + let provided_lt_args = self.num_provided_lifetime_args(); + let provided_type_or_const_args = self.num_provided_type_or_const_args(); + + let get_verb = |num_args| if num_args == 1 { "was" } else { "were" }; + + let (provided_args_str, verb) = match self.gen_args_info { + MissingLifetimes { .. } | ExcessLifetimes { .. } => ( + format!("{} lifetime argument{}", provided_lt_args, pluralize!(provided_lt_args)), + get_verb(provided_lt_args), + ), + MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => ( + format!( + "{} generic argument{}", + provided_type_or_const_args, + pluralize!(provided_type_or_const_args) + ), + get_verb(provided_type_or_const_args), + ), }; + if self.gen_args.span().is_some() { + format!( + "this {} takes {}{} {} argument{} but {} {} supplied", + def_kind, + quantifier, + bound, + kind, + pluralize!(bound), + provided_args_str.as_str(), + verb + ) + } else { + format!("missing generics for {} `{}`", def_kind, def_path) + } + } + + fn start_diagnostics(&self) -> DiagnosticBuilder<'tcx> { + let span = self.path_segment.ident.span; + let msg = self.create_error_message(); + self.tcx.sess.struct_span_err_with_code(span, &msg, self.code()) } /// Builds the `expected 1 type argument / supplied 2 type arguments` message. fn notify(&self, err: &mut DiagnosticBuilder<'_>) { - let (quantifier, bound) = self.quantifier_and_bound(); + let (quantifier, bound) = self.get_quantifier_and_bound(); + let provided_args = self.num_provided_args(); err.span_label( self.path_segment.ident.span, @@ -101,12 +371,12 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> { "expected {}{} {} argument{}", quantifier, bound, - self.kind, + self.kind(), pluralize!(bound), ), ); - // When user's provided too many arguments, we don't highlight each of them, because it + // When too many arguments were provided, we don't highlight each of them, because it // would overlap with the suggestion to remove them: // // ``` @@ -114,21 +384,27 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> { // ----- ----- supplied 2 type arguments // ^^^^^^^ remove this type argument // ``` - if self.provided > self.expected_max { + if self.too_many_args_provided() { return; } - let args = self.gen_args.args.iter().skip(self.args_offset).take(self.provided).enumerate(); + let args = self + .gen_args + .args + .iter() + .skip(self.get_lifetime_args_offset()) + .take(provided_args) + .enumerate(); for (i, arg) in args { err.span_label( arg.span(), - if i + 1 == self.provided { + if i + 1 == provided_args { format!( "supplied {} {} argument{}", - self.provided, - self.kind, - pluralize!(self.provided) + provided_args, + self.kind(), + pluralize!(provided_args) ) } else { String::new() @@ -138,56 +414,24 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> { } fn suggest(&self, err: &mut DiagnosticBuilder<'_>) { - if self.provided == 0 { - if self.gen_args.span().is_some() { - self.suggest_adding_args(err); - } else { - self.suggest_creating_generics(err); - } - } else if self.provided < self.expected_min { - self.suggest_adding_args(err); - } else { - self.suggest_removing_args_or_generics(err); - } - } - - /// Suggests to create generics (`<...>`) when current invocation site contains no generics at - /// all: - /// - /// ```text - /// type Map = HashMap; - /// ``` - fn suggest_creating_generics(&self, err: &mut DiagnosticBuilder<'_>) { - let params = self - .gen_params - .params - .iter() - .skip(self.params_offset) - .take(self.expected_min) - .map(|param| param.name.to_string()) - .collect::<Vec<_>>() - .join(", "); - - let def_kind = self.tcx.def_kind(self.def_id); - - let sugg = if matches!(def_kind, DefKind::Fn | DefKind::AssocFn) { - format!("::<{}>", params) - } else { - format!("<{}>", params) - }; - - let msg = format!( - "use angle brackets to add missing {} argument{}", - self.kind, - pluralize!(self.expected_min), + debug!( + "suggest(self.provided {:?}, self.gen_args.span(): {:?})", + self.num_provided_args(), + self.gen_args.span(), ); - err.span_suggestion_verbose( - self.path_segment.ident.span.shrink_to_hi(), - &msg, - sugg, - Applicability::HasPlaceholders, - ); + match self.angle_brackets { + AngleBrackets::Missing | AngleBrackets::Implied => self.suggest_adding_args(err), + AngleBrackets::Available => { + if self.not_enough_args_provided() { + self.suggest_adding_args(err); + } else if self.too_many_args_provided() { + self.suggest_removing_args_or_generics(err); + } else { + unreachable!(); + } + } + } } /// Suggests to add missing argument(s) when current invocation site already contains some @@ -197,38 +441,167 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> { /// type Map = HashMap<String>; /// ``` fn suggest_adding_args(&self, err: &mut DiagnosticBuilder<'_>) { - assert!(!self.gen_args.is_empty()); - if self.gen_args.parenthesized { return; } - let missing_arg_count = self.expected_min - self.provided; + match self.gen_args_info { + MissingLifetimes { .. } => { + self.suggest_adding_lifetime_args(err); + } + MissingTypesOrConsts { .. } => { + self.suggest_adding_type_and_const_args(err); + } + _ => unreachable!(), + } + } - let (span, sugg_prefix) = if self.args_offset + self.provided == 0 { - let span = self.gen_args.args[0].span().shrink_to_lo(); - (span, "") + fn suggest_adding_lifetime_args(&self, err: &mut DiagnosticBuilder<'_>) { + debug!("suggest_adding_lifetime_args(path_segment: {:?})", self.path_segment); + let num_missing_args = self.num_missing_lifetime_args(); + let num_params_to_take = num_missing_args; + let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args)); + + // we first try to get lifetime name suggestions from scope or elision information. If none is + // available we use the parameter defintions + let suggested_args = if let Some(hir_id) = self.path_segment.hir_id { + if let Some(lifetimes_in_scope) = self.tcx.lifetime_scope(hir_id) { + match lifetimes_in_scope { + LifetimeScopeForPath::NonElided(param_names) => { + debug!("NonElided(param_names: {:?})", param_names); + + if param_names.len() >= num_params_to_take { + // use lifetime parameters in scope for suggestions + param_names + .iter() + .take(num_params_to_take) + .map(|p| (*p).clone()) + .collect::<Vec<_>>() + .join(", ") + } else { + // Not enough lifetime arguments in scope -> create suggestions from + // lifetime parameter names in definition. An error for the incorrect + // lifetime scope will be output later. + self.get_lifetime_args_suggestions_from_param_names(num_params_to_take) + } + } + LifetimeScopeForPath::Elided => { + debug!("Elided"); + // use suggestions of the form `<'_, '_>` in case lifetime can be elided + ["'_"].repeat(num_params_to_take).join(",") + } + } + } else { + self.get_lifetime_args_suggestions_from_param_names(num_params_to_take) + } } else { - let span = - self.gen_args.args[self.args_offset + self.provided - 1].span().shrink_to_hi(); - (span, ", ") + self.get_lifetime_args_suggestions_from_param_names(num_params_to_take) }; - let msg = format!("add missing {} argument{}", self.kind, pluralize!(missing_arg_count)); + debug!("suggested_args: {:?}", &suggested_args); - let sugg = self - .gen_params - .params - .iter() - .skip(self.params_offset + self.provided) - .take(missing_arg_count) - .map(|param| param.name.to_string()) - .collect::<Vec<_>>() - .join(", "); + match self.angle_brackets { + AngleBrackets::Missing => { + let span = self.path_segment.ident.span; + + // insert a suggestion of the form "Y<'a, 'b>" + let ident = self.path_segment.ident.name.to_ident_string(); + let sugg = format!("{}<{}>", ident, suggested_args); + debug!("sugg: {:?}", sugg); + + err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders); + } + + AngleBrackets::Available => { + // angle brackets exist, so we insert missing arguments after the existing args + + assert!(!self.gen_args.args.is_empty()); + + if self.num_provided_lifetime_args() > 0 { + let last_lt_arg_span = self.gen_args.args + [self.num_provided_lifetime_args() - 1] + .span() + .shrink_to_hi(); + let source_map = self.tcx.sess.source_map(); - let sugg = format!("{}{}", sugg_prefix, sugg); + if let Ok(last_gen_arg) = source_map.span_to_snippet(last_lt_arg_span) { + let sugg = format!("{}, {}", last_gen_arg, suggested_args); - err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders); + err.span_suggestion_verbose( + last_lt_arg_span, + &msg, + sugg, + Applicability::HasPlaceholders, + ); + } + } else { + // Non-lifetime arguments included in `gen_args` -> insert missing lifetimes before + // existing arguments + let first_arg_span = self.gen_args.args[0].span().shrink_to_lo(); + let source_map = self.tcx.sess.source_map(); + + if let Ok(first_gen_arg) = source_map.span_to_snippet(first_arg_span) { + let sugg = format!("{}, {}", suggested_args, first_gen_arg); + + err.span_suggestion_verbose( + first_arg_span, + &msg, + sugg, + Applicability::HasPlaceholders, + ); + } + } + } + AngleBrackets::Implied => { + // We never encounter missing lifetimes in situations in which lifetimes are elided + unreachable!(); + } + } + } + + fn suggest_adding_type_and_const_args(&self, err: &mut DiagnosticBuilder<'_>) { + let num_missing_args = self.num_missing_type_or_const_args(); + let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args)); + + let suggested_args = + self.get_type_or_const_args_suggestions_from_param_names(num_missing_args); + debug!("suggested_args: {:?}", suggested_args); + + match self.angle_brackets { + AngleBrackets::Missing | AngleBrackets::Implied => { + let span = self.path_segment.ident.span; + + // insert a suggestion of the form "Y<T, U>" + let ident = self.path_segment.ident.name.to_ident_string(); + let sugg = format!("{}<{}>", ident, suggested_args); + debug!("sugg: {:?}", sugg); + + err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders); + } + AngleBrackets::Available => { + // angle brackets exist, so we just insert missing arguments after the existing + // type or const args + + let index_last_provided_arg = + self.get_lifetime_args_offset() + self.num_provided_type_or_const_args() - 1; + if index_last_provided_arg < self.gen_args.args.len() { + let first_arg_span = + self.gen_args.args[index_last_provided_arg].span().shrink_to_hi(); + let source_map = self.tcx.sess.source_map(); + if let Ok(first_gen_arg) = source_map.span_to_snippet(first_arg_span) { + let sugg = format!("{}, {}", first_gen_arg, suggested_args); + debug!("sugg: {:?}", sugg); + + err.span_suggestion_verbose( + first_arg_span, + &msg, + sugg, + Applicability::HasPlaceholders, + ); + } + } + } + } } /// Suggests to remove redundant argument(s): @@ -237,12 +610,91 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> { /// type Map = HashMap<String, String, String, String>; /// ``` fn suggest_removing_args_or_generics(&self, err: &mut DiagnosticBuilder<'_>) { - assert!(self.provided > 0); + let num_provided_lt_args = self.num_provided_lifetime_args(); + let num_provided_type_const_args = self.num_provided_type_or_const_args(); + let num_provided_args = num_provided_lt_args + num_provided_type_const_args; + assert!(num_provided_args > 0); + + let num_redundant_lt_args = self.num_excess_lifetime_args(); + let num_redundant_type_or_const_args = self.num_excess_type_or_const_args(); + let num_redundant_args = num_redundant_lt_args + num_redundant_type_or_const_args; + + let redundant_lifetime_args = num_redundant_lt_args > 0; + let redundant_type_or_const_args = num_redundant_type_or_const_args > 0; + + let remove_entire_generics = num_redundant_args >= self.gen_args.args.len(); + + let remove_lifetime_args = |err: &mut DiagnosticBuilder<'_>| { + let idx_first_redundant_lt_args = self.num_expected_lifetime_args(); + let span_lo_redundant_lt_args = + self.gen_args.args[idx_first_redundant_lt_args].span().shrink_to_lo(); + let span_hi_redundant_lt_args = self.gen_args.args + [idx_first_redundant_lt_args + num_redundant_lt_args - 1] + .span() + .shrink_to_hi(); + let eat_comma = + idx_first_redundant_lt_args + num_redundant_lt_args - 1 != self.gen_args.args.len(); + + let span_redundant_lt_args = if eat_comma { + let span_hi = self.gen_args.args + [idx_first_redundant_lt_args + num_redundant_lt_args - 1] + .span() + .shrink_to_hi(); + span_lo_redundant_lt_args.to(span_hi) + } else { + span_lo_redundant_lt_args.to(span_hi_redundant_lt_args) + }; + debug!("span_redundant_lt_args: {:?}", span_redundant_lt_args); + + let msg_lifetimes = format!( + "remove {} {} argument{}", + if num_redundant_args == 1 { "this" } else { "these" }, + "lifetime", + pluralize!(num_redundant_lt_args), + ); + + err.span_suggestion( + span_redundant_lt_args, + &msg_lifetimes, + String::new(), + Applicability::MaybeIncorrect, + ); + }; + + let remove_type_or_const_args = |err: &mut DiagnosticBuilder<'_>| { + let idx_first_redundant_type_or_const_args = self.get_lifetime_args_offset() + + num_redundant_lt_args + + self.num_expected_type_or_const_args(); + + let span_lo_redundant_type_or_const_args = + self.gen_args.args[idx_first_redundant_type_or_const_args].span().shrink_to_lo(); - let redundant_args_count = self.provided - self.expected_max; - let remove_entire_generics = redundant_args_count >= self.gen_args.args.len(); + let span_hi_redundant_type_or_const_args = self.gen_args.args + [idx_first_redundant_type_or_const_args + num_redundant_type_or_const_args - 1] + .span() + .shrink_to_hi(); - let (span, msg) = if remove_entire_generics { + let span_redundant_type_or_const_args = + span_lo_redundant_type_or_const_args.to(span_hi_redundant_type_or_const_args); + + debug!("span_redundant_type_or_const_args: {:?}", span_redundant_type_or_const_args); + + let msg_types_or_consts = format!( + "remove {} {} argument{}", + if num_redundant_args == 1 { "this" } else { "these" }, + "generic", + pluralize!(num_redundant_type_or_const_args), + ); + + err.span_suggestion( + span_redundant_type_or_const_args, + &msg_types_or_consts, + String::new(), + Applicability::MaybeIncorrect, + ); + }; + + if remove_entire_generics { let sm = self.tcx.sess.source_map(); let span = self @@ -258,70 +710,16 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> { if self.gen_args.parenthesized { "parenthetical " } else { "" }, ); - (span, msg) + err.span_suggestion(span, &msg, String::new(), Applicability::MaybeIncorrect); + } else if redundant_lifetime_args && redundant_type_or_const_args { + remove_lifetime_args(err); + remove_type_or_const_args(err); + } else if redundant_lifetime_args { + remove_lifetime_args(err); } else { - // When it comes to removing particular argument(s) from the generics, there are two - // edge cases we have to consider: - // - // When the first redundant argument is at the beginning or in the middle of the - // generics, like so: - // - // ``` - // type Map = HashMap<String, String, String, String>; - // ^^^^^^^^^^^^^^^^ - // | span must start with the argument - // ``` - // - // When the last redundant argument is at the ending of the generics, like so: - // - // ``` - // type Map = HashMap<String, String, String, String>; - // ^^^^^^^^^^^^^^^^ - // | span must start with the comma - // ``` - - // Index of the first redundant argument - let from_idx = self.args_offset + self.expected_max; - - // Index of the last redundant argument - let to_idx = self.args_offset + self.provided - 1; - - assert!(from_idx <= to_idx); - - let (from, comma_eaten) = { - let first_argument_starts_generics = from_idx == 0; - let last_argument_ends_generics = to_idx + 1 == self.gen_args.args.len(); - - if !first_argument_starts_generics && last_argument_ends_generics { - (self.gen_args.args[from_idx - 1].span().hi(), true) - } else { - (self.gen_args.args[from_idx].span().lo(), false) - } - }; - - let to = { - let hi = self.gen_args.args[to_idx].span().hi(); - - if comma_eaten { - hi - } else { - self.gen_args.args.get(to_idx + 1).map(|arg| arg.span().lo()).unwrap_or(hi) - } - }; - - let span = Span::new(from, to, self.span.ctxt()); - - let msg = format!( - "remove {} {} argument{}", - if redundant_args_count == 1 { "this" } else { "these" }, - self.kind, - pluralize!(redundant_args_count), - ); - - (span, msg) - }; - - err.span_suggestion(span, &msg, String::new(), Applicability::MaybeIncorrect); + assert!(redundant_type_or_const_args); + remove_type_or_const_args(err); + } } /// Builds the `type defined here` message. @@ -334,7 +732,7 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> { let msg = { let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id); - let (quantifier, bound) = self.quantifier_and_bound(); + let (quantifier, bound) = self.get_quantifier_and_bound(); let params = if bound == 0 { String::new() @@ -362,7 +760,7 @@ impl<'tcx> WrongNumberOfGenericArgs<'_, 'tcx> { def_kind, quantifier, bound, - self.kind, + self.kind(), pluralize!(bound), params, ) |
