diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs index d280ff8815a..82718b2f82c 100644 --- a/crates/hir/src/lib.rs +++ b/crates/hir/src/lib.rs @@ -42,7 +42,7 @@ use hir_def::{ adt::VariantData, body::{BodyDiagnostic, SyntheticSyntax}, expr::{BindingAnnotation, ExprOrPatId, LabelId, Pat, PatId}, - generics::{ConstParamData, LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, + generics::{LifetimeParamData, TypeOrConstParamData, TypeParamProvenance}, item_tree::ItemTreeNode, lang_item::{LangItem, LangItemTarget}, layout::{Layout, LayoutError, ReprOptions}, @@ -1189,31 +1189,6 @@ impl Adt { .map(|arena| arena.1.clone()) } - /// Returns an iterator of all `const` generic paramaters - /// - /// This method is not well optimized, I could not statisfy the borrow - /// checker. I'm sure there are smarter ways to return the consts names - pub fn consts(&self, db: &dyn HirDatabase) -> impl Iterator { - let resolver = match self { - Adt::Struct(s) => s.id.resolver(db.upcast()), - Adt::Union(u) => u.id.resolver(db.upcast()), - Adt::Enum(e) => e.id.resolver(db.upcast()), - }; - resolver - .generic_params() - .map_or(vec![], |gp| { - gp.as_ref() - .type_or_consts - .iter() - .filter_map(|arena| match arena.1 { - TypeOrConstParamData::ConstParamData(consts) => Some(consts.clone()), - _ => None, - }) - .collect::>() - }) - .into_iter() - } - pub fn as_enum(&self) -> Option { if let Self::Enum(v) = self { Some(*v) @@ -3373,6 +3348,24 @@ impl Type { } } + /// Iterates its type arguments + /// + /// It iterates the actual type arguments when concrete types are used + /// and otherwise the generic names. + /// It does not include `const` arguments. + /// + /// For code, such as: + /// ```text + /// struct Foo + /// + /// impl Foo + /// ``` + /// + /// It iterates: + /// ```text + /// - "String" + /// - "U" + /// ``` pub fn type_arguments(&self) -> impl Iterator + '_ { self.ty .strip_references() @@ -3383,6 +3376,46 @@ impl Type { .map(move |ty| self.derived(ty)) } + /// Iterates its type and const arguments + /// + /// It iterates the actual type and const arguments when concrete types + /// are used and otherwise the generic names. + /// + /// For code, such as: + /// ```text + /// struct Foo + /// + /// impl Foo + /// ``` + /// + /// It iterates: + /// ```text + /// - "String" + /// - "U" + /// - "12" + /// ``` + pub fn type_and_const_arguments<'a>( + &'a self, + db: &'a dyn HirDatabase, + ) -> impl Iterator + 'a { + self.ty + .strip_references() + .as_adt() + .into_iter() + .flat_map(|(_, substs)| substs.iter(Interner)) + .filter_map(|arg| { + // arg can be either a `Ty` or `constant` + if let Some(ty) = arg.ty(Interner) { + Some(SmolStr::new(ty.display(db).to_string())) + // Some(ty) + } else if let Some(const_) = arg.constant(Interner) { + Some(SmolStr::new_inline(&const_.display(db).to_string())) + } else { + None + } + }) + } + /// Combines lifetime indicators, type and constant parameters into a single `Iterator` pub fn lifetime_type_const_paramaters<'a>( &'a self, @@ -3392,12 +3425,8 @@ impl Type { self.as_adt() .and_then(|a| a.lifetime(db).and_then(|lt| Some((<.name).to_smol_str()))) .into_iter() - // add the type paramaters - .chain(self.type_arguments().map(|ty| SmolStr::new(ty.display(db).to_string()))) - // add const paramameters - .chain(self.as_adt().map_or(vec![], |a| { - a.consts(db).map(|cs| cs.name.to_smol_str()).collect::>() - })) + // add the type and const paramaters + .chain(self.type_and_const_arguments(db)) } pub fn iterate_method_candidates_with_traits( diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 7fc396f023f..b4fa2f9b9f0 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs @@ -2272,6 +2272,60 @@ mod tests { ); } + #[test] + fn test_runnables_doc_test_in_impl_with_lifetime_type_const_value() { + check( + r#" +//- /lib.rs +$0 +fn main() {} + +struct Data<'a, A, const B: usize, C, const D: u32>; +impl Data<'a, A, 12, C, D> { + /// ``` + /// ``` + fn foo() {} +} +"#, + &[Bin, DocTest], + expect![[r#" + [ + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 1..13, + focus_range: 4..8, + name: "main", + kind: Function, + }, + kind: Bin, + cfg: None, + }, + Runnable { + use_name_in_title: false, + nav: NavigationTarget { + file_id: FileId( + 0, + ), + full_range: 121..156, + name: "foo", + }, + kind: DocTest { + test_id: Path( + "Data<'a,A,12,C,D>::foo", + ), + }, + cfg: None, + }, + ] + "#]], + ); + } + + #[test] fn doc_test_type_params() { check(