Fix crash for super trait cycles

This commit is contained in:
Florian Diebold 2019-09-07 16:39:05 +02:00
parent 9db34eec20
commit 8fb3cab76c
4 changed files with 39 additions and 20 deletions

1
Cargo.lock generated
View file

@ -1007,7 +1007,6 @@ dependencies = [
"chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git)", "chalk-solve 0.1.0 (git+https://github.com/rust-lang/chalk.git)",
"ena 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)", "ena 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)",
"insta 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "insta 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
"itertools 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)",
"lalrpop-intern 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)", "lalrpop-intern 0.15.1 (registry+https://github.com/rust-lang/crates.io-index)",
"log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.8 (registry+https://github.com/rust-lang/crates.io-index)",
"once_cell 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "once_cell 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)",

View file

@ -12,7 +12,6 @@ rustc-hash = "1.0"
parking_lot = "0.9.0" parking_lot = "0.9.0"
ena = "0.13" ena = "0.13"
once_cell = "1.0.1" once_cell = "1.0.1"
itertools = "0.8.0"
ra_syntax = { path = "../ra_syntax" } ra_syntax = { path = "../ra_syntax" }
ra_arena = { path = "../ra_arena" } ra_arena = { path = "../ra_arena" }

View file

@ -1,11 +1,8 @@
pub(crate) mod src; pub(crate) mod src;
pub(crate) mod docs; pub(crate) mod docs;
use std::iter;
use std::sync::Arc; use std::sync::Arc;
use itertools::Itertools;
use ra_db::{CrateId, Edition, FileId, SourceRootId}; use ra_db::{CrateId, Edition, FileId, SourceRootId};
use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner}; use ra_syntax::ast::{self, NameOwner, TypeAscriptionOwner};
@ -849,20 +846,23 @@ impl Trait {
/// Returns an iterator over the whole super trait hierarchy (including the /// Returns an iterator over the whole super trait hierarchy (including the
/// trait itself). /// trait itself).
pub fn all_super_traits<'a>( pub fn all_super_traits(self, db: &impl HirDatabase) -> Vec<Trait> {
self, // we need to take care a bit here to avoid infinite loops in case of cycles
db: &'a impl HirDatabase, // (i.e. if we have `trait A: B; trait B: A;`)
) -> impl Iterator<Item = Trait> + 'a { let mut result = vec![self];
self.all_super_traits_inner(db).unique() let mut i = 0;
} while i < result.len() {
let t = result[i];
fn all_super_traits_inner<'a>( // yeah this is quadratic, but trait hierarchies should be flat
self, // enough that this doesn't matter
db: &'a impl HirDatabase, for tt in t.direct_super_traits(db) {
) -> impl Iterator<Item = Trait> + 'a { if !result.contains(&tt) {
iter::once(self).chain(self.direct_super_traits(db).into_iter().flat_map(move |t| { result.push(tt);
iter::once(t).chain(Box::new(t.all_super_traits(db)) as Box<dyn Iterator<Item = Trait>>) }
})) }
i += 1;
}
result
} }
pub fn associated_type_by_name(self, db: &impl DefDatabase, name: &Name) -> Option<TypeAlias> { pub fn associated_type_by_name(self, db: &impl DefDatabase, name: &Name) -> Option<TypeAlias> {
@ -882,7 +882,7 @@ impl Trait {
db: &impl HirDatabase, db: &impl HirDatabase,
name: &Name, name: &Name,
) -> Option<TypeAlias> { ) -> Option<TypeAlias> {
self.all_super_traits(db).find_map(|t| t.associated_type_by_name(db, name)) self.all_super_traits(db).into_iter().find_map(|t| t.associated_type_by_name(db, name))
} }
pub(crate) fn trait_data(self, db: &impl DefDatabase) -> Arc<TraitData> { pub(crate) fn trait_data(self, db: &impl DefDatabase) -> Arc<TraitData> {

View file

@ -3694,6 +3694,27 @@ fn test<T: Trait1, U: Trait2>(x: T, y: U) {
); );
} }
#[test]
fn super_trait_cycle() {
// This just needs to not crash
assert_snapshot!(
infer(r#"
trait A: B {}
trait B: A {}
fn test<T: A>(x: T) {
x.foo();
}
"#),
@r###"
[44; 45) 'x': T
[50; 66) '{ ...o(); }': ()
[56; 57) 'x': T
[56; 63) 'x.foo()': {unknown}
"###
);
}
#[test] #[test]
fn super_trait_assoc_type_bounds() { fn super_trait_assoc_type_bounds() {
assert_snapshot!( assert_snapshot!(