about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEsteban Küber <esteban@kuber.com.ar>2020-02-13 22:45:48 -0800
committerEsteban Küber <esteban@kuber.com.ar>2020-02-16 18:41:02 -0800
commit8bafe883b6bb273b7a256d7720e638b267c2e574 (patch)
tree98aa52fddb1fbdfb3ea3f8dfeca993a538f94994 /src
parenta643ee8d693b8100e6f54f2a01ff7cde05eb65c5 (diff)
downloadrust-8bafe883b6bb273b7a256d7720e638b267c2e574.tar.gz
rust-8bafe883b6bb273b7a256d7720e638b267c2e574.zip
Select an appropriate unused lifetime name in suggestion
Diffstat (limited to 'src')
-rw-r--r--src/librustc_typeck/collect.rs50
-rw-r--r--src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs11
-rw-r--r--src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr23
3 files changed, 71 insertions, 13 deletions
diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs
index 80d914d8d0a..ca3bcac1206 100644
--- a/src/librustc_typeck/collect.rs
+++ b/src/librustc_typeck/collect.rs
@@ -33,7 +33,7 @@ use rustc::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt};
 use rustc::ty::{ReprOptions, ToPredicate, WithConstness};
 use rustc_attr::{list_contains_name, mark_used, InlineAttr, OptimizeAttr};
 use rustc_data_structures::captures::Captures;
-use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
 use rustc_errors::{struct_span_err, Applicability};
 use rustc_hir as hir;
 use rustc_hir::def::{CtorKind, DefKind, Res};
@@ -369,10 +369,12 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
                         hir::ItemKind::Enum(_, generics)
                         | hir::ItemKind::Struct(_, generics)
                         | hir::ItemKind::Union(_, generics) => {
-                            // FIXME: look for an appropriate lt name if `'a` is already used
+                            let lt_name = get_new_lifetime_name(self.tcx, poly_trait_ref, generics);
                             let (lt_sp, sugg) = match &generics.params[..] {
-                                [] => (generics.span, "<'a>".to_string()),
-                                [bound, ..] => (bound.span.shrink_to_lo(), "'a, ".to_string()),
+                                [] => (generics.span, format!("<{}>", lt_name)),
+                                [bound, ..] => {
+                                    (bound.span.shrink_to_lo(), format!("{}, ", lt_name))
+                                }
                             };
                             let suggestions = vec![
                                 (lt_sp, sugg),
@@ -387,7 +389,7 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
                                                     ty::EarlyBoundRegion {
                                                         def_id: item_def_id,
                                                         index: 0,
-                                                        name: Symbol::intern("'a"),
+                                                        name: Symbol::intern(&lt_name),
                                                     },
                                                 ))
                                             })
@@ -445,6 +447,43 @@ impl AstConv<'tcx> for ItemCtxt<'tcx> {
     }
 }
 
+/// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present.
+fn get_new_lifetime_name<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    poly_trait_ref: ty::PolyTraitRef<'tcx>,
+    generics: &hir::Generics<'tcx>,
+) -> String {
+    let existing_lifetimes = tcx
+        .collect_referenced_late_bound_regions(&poly_trait_ref)
+        .into_iter()
+        .filter_map(|lt| {
+            if let ty::BoundRegion::BrNamed(_, name) = lt {
+                Some(name.as_str().to_string())
+            } else {
+                None
+            }
+        })
+        .chain(generics.params.iter().filter_map(|param| {
+            if let hir::GenericParamKind::Lifetime { .. } = &param.kind {
+                Some(param.name.ident().as_str().to_string())
+            } else {
+                None
+            }
+        }))
+        .collect::<FxHashSet<String>>();
+
+    let a_to_z_repeat_n = |n| {
+        (b'a'..=b'z').map(move |c| {
+            let mut s = format!("'");
+            s.extend(std::iter::repeat(char::from(c)).take(n));
+            s
+        })
+    };
+
+    // If all single char lifetime names are present, we wrap around and double the chars.
+    (1..).flat_map(a_to_z_repeat_n).find(|lt| !existing_lifetimes.contains(lt.as_str())).unwrap()
+}
+
 /// Returns the predicates defined on `item_def_id` of the form
 /// `X: Foo` where `X` is the type parameter `def_id`.
 fn type_param_predicates(
@@ -1588,7 +1627,6 @@ fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
 /// Returns a list of user-specified type predicates for the definition with ID `def_id`.
 /// N.B., this does not include any implied/inferred constraints.
 fn explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
-    use rustc_data_structures::fx::FxHashSet;
     use rustc_hir::*;
 
     debug!("explicit_predicates_of(def_id={:?})", def_id);
diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs
index 8a5777d4d7c..58f186d7775 100644
--- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs
+++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.rs
@@ -12,11 +12,12 @@ struct SomeStruct<I: for<'x> Foo<&'x isize>> {
     //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
 }
 
-enum SomeEnum<I: for<'x> Foo<&'x isize>> {
+enum SomeEnum<'b, I: for<'a> Foo<&'a isize>> {
     TupleVariant(I::A),
     //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
     StructVariant { field: I::A },
     //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
+    OkVariant(&'b usize),
 }
 
 // FIXME(eddyb) This one doesn't even compile because of the unsupported syntax.
@@ -26,7 +27,13 @@ enum SomeEnum<I: for<'x> Foo<&'x isize>> {
 // }
 
 struct YetAnotherStruct<'a, I: for<'x> Foo<&'x isize>> {
-    field: <I as Foo<&'a isize>>::A
+    field: <I as Foo<&'a isize>>::A,
+}
+
+struct Why<'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'n, 'o, 'p, 'q, 'r, 's, 't, 'u, 'v, 'w, 'x,
+    'y, 'z, 'aa, I: for<'l, 'm> Foo<&'l &'m isize>> {
+    field: I::A,
+    //~^ ERROR cannot extract an associated type from a higher-ranked trait bound in this context
 }
 
 pub fn main() {}
diff --git a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr
index c71bc70ea6c..e3fd2860ebc 100644
--- a/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr
+++ b/src/test/ui/associated-types/associated-types-project-from-hrtb-in-struct.stderr
@@ -18,8 +18,8 @@ LL |     TupleVariant(I::A),
    |
 help: use a fully qualified path with explicit lifetimes
    |
-LL | enum SomeEnum<'a, I: for<'x> Foo<&'x isize>> {
-LL |     TupleVariant(<I as Foo<&'a isize>>::A),
+LL | enum SomeEnum<'c, 'b, I: for<'a> Foo<&'a isize>> {
+LL |     TupleVariant(<I as Foo<&'c isize>>::A),
    |
 
 error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context
@@ -30,11 +30,24 @@ LL |     StructVariant { field: I::A },
    |
 help: use a fully qualified path with explicit lifetimes
    |
-LL | enum SomeEnum<'a, I: for<'x> Foo<&'x isize>> {
+LL | enum SomeEnum<'c, 'b, I: for<'a> Foo<&'a isize>> {
 LL |     TupleVariant(I::A),
 LL |
-LL |     StructVariant { field: <I as Foo<&'a isize>>::A },
+LL |     StructVariant { field: <I as Foo<&'c isize>>::A },
    |
 
-error: aborting due to 3 previous errors
+error[E0212]: cannot extract an associated type from a higher-ranked trait bound in this context
+  --> $DIR/associated-types-project-from-hrtb-in-struct.rs:35:12
+   |
+LL |     field: I::A,
+   |            ^^^^
+   |
+help: use a fully qualified path with explicit lifetimes
+   |
+LL | struct Why<'bb, 'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'n, 'o, 'p, 'q, 'r, 's, 't, 'u, 'v, 'w, 'x,
+LL |     'y, 'z, 'aa, I: for<'l, 'm> Foo<&'l &'m isize>> {
+LL |     field: <I as Foo<&'bb &'bb isize>>::A,
+   |
+
+error: aborting due to 4 previous errors