about summary refs log tree commit diff
path: root/compiler
diff options
context:
space:
mode:
authorb-naber <bn263@gmx.de>2021-02-18 21:01:44 +0100
committerb-naber <bn263@gmx.de>2021-05-11 14:09:46 +0200
commite4d9bc66f65fd3d206587c07e33c4877fda073f9 (patch)
tree1cbfff1dc8de7fd37b9c550e063d9e6256599676 /compiler
parentfe62c6e2958abfe54a9410a24a5750baf4c157e0 (diff)
downloadrust-e4d9bc66f65fd3d206587c07e33c4877fda073f9.tar.gz
rust-e4d9bc66f65fd3d206587c07e33c4877fda073f9.zip
improve diagnosts for GATs
Diffstat (limited to 'compiler')
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs5
-rw-r--r--compiler/rustc_middle/src/middle/resolve_lifetime.rs14
-rw-r--r--compiler/rustc_middle/src/query/mod.rs4
-rw-r--r--compiler/rustc_middle/src/ty/context.rs6
-rw-r--r--compiler/rustc_middle/src/ty/query/mod.rs4
-rw-r--r--compiler/rustc_resolve/src/late/lifetimes.rs132
-rw-r--r--compiler/rustc_typeck/src/astconv/generics.rs134
-rw-r--r--compiler/rustc_typeck/src/astconv/mod.rs11
-rw-r--r--compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs800
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,
             )