2 use ide_db::helpers::node_ext::walk_ty;
3 use itertools::Itertools;
5 ast::{self, edit::IndentLevel, AstNode, HasGenericParams, HasName},
9 use crate::{AssistContext, AssistId, AssistKind, Assists};
11 // Assist: extract_type_alias
13 // Extracts the selected type as a type alias.
17 // field: $0(u8, u8, u8)$0,
22 // type $0Type = (u8, u8, u8);
28 pub(crate) fn extract_type_alias(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
29 if ctx.frange.range.is_empty() {
33 let ty = ctx.find_node_at_range::<ast::Type>()?;
34 let item = ty.syntax().ancestors().find_map(ast::Item::cast)?;
35 let assoc_owner = item.syntax().ancestors().nth(2).and_then(|it| {
38 ast::Trait(tr) => Some(Either::Left(tr)),
39 ast::Impl(impl_) => Some(Either::Right(impl_)),
44 let node = assoc_owner.as_ref().map_or_else(
46 |impl_| impl_.as_ref().either(AstNode::syntax, AstNode::syntax),
48 let insert_pos = node.text_range().start();
49 let target = ty.syntax().text_range();
52 AssistId("extract_type_alias", AssistKind::RefactorExtract),
53 "Extract type as type alias",
56 let mut known_generics = match item.generic_param_list() {
57 Some(it) => it.generic_params().collect(),
60 if let Some(it) = assoc_owner.as_ref().and_then(|it| match it {
61 Either::Left(it) => it.generic_param_list(),
62 Either::Right(it) => it.generic_param_list(),
64 known_generics.extend(it.generic_params());
66 let generics = collect_used_generics(&ty, &known_generics);
68 let replacement = if !generics.is_empty() {
71 generics.iter().format_with(", ", |generic, f| {
73 ast::GenericParam::ConstParam(cp) => f(&cp.name().unwrap()),
74 ast::GenericParam::LifetimeParam(lp) => f(&lp.lifetime().unwrap()),
75 ast::GenericParam::TypeParam(tp) => f(&tp.name().unwrap()),
82 builder.replace(target, replacement);
84 let indent = IndentLevel::from_node(node);
85 let generics = if !generics.is_empty() {
86 format!("<{}>", generics.iter().format(", "))
90 match ctx.config.snippet_cap {
92 builder.insert_snippet(
95 format!("type $0Type{} = {};\n\n{}", generics, ty, indent),
101 format!("type Type{} = {};\n\n{}", generics, ty, indent),
109 fn collect_used_generics<'gp>(
111 known_generics: &'gp [ast::GenericParam],
112 ) -> Vec<&'gp ast::GenericParam> {
113 // can't use a closure -> closure here cause lifetime inference fails for that
114 fn find_lifetime(text: &str) -> impl Fn(&&ast::GenericParam) -> bool + '_ {
115 move |gp: &&ast::GenericParam| match gp {
116 ast::GenericParam::LifetimeParam(lp) => {
117 lp.lifetime().map_or(false, |lt| lt.text() == text)
123 let mut generics = Vec::new();
124 walk_ty(ty, &mut |ty| match ty {
125 ast::Type::PathType(ty) => {
126 if let Some(path) = ty.path() {
127 if let Some(name_ref) = path.as_single_name_ref() {
128 if let Some(param) = known_generics.iter().find(|gp| {
130 ast::GenericParam::ConstParam(cp) => cp.name(),
131 ast::GenericParam::TypeParam(tp) => tp.name(),
134 .map_or(false, |n| n.text() == name_ref.text())
136 generics.push(param);
141 .filter_map(|seg| seg.generic_arg_list())
142 .flat_map(|it| it.generic_args())
143 .filter_map(|it| match it {
144 ast::GenericArg::LifetimeArg(lt) => {
145 let lt = lt.lifetime()?;
146 known_generics.iter().find(find_lifetime(<.text()))
153 ast::Type::ImplTraitType(impl_ty) => {
154 if let Some(it) = impl_ty.type_bound_list() {
157 .filter_map(|it| it.lifetime())
158 .filter_map(|lt| known_generics.iter().find(find_lifetime(<.text()))),
162 ast::Type::DynTraitType(dyn_ty) => {
163 if let Some(it) = dyn_ty.type_bound_list() {
166 .filter_map(|it| it.lifetime())
167 .filter_map(|lt| known_generics.iter().find(find_lifetime(<.text()))),
171 ast::Type::RefType(ref_) => generics.extend(
172 ref_.lifetime().and_then(|lt| known_generics.iter().find(find_lifetime(<.text()))),
176 // stable resort to lifetime, type, const
177 generics.sort_by_key(|gp| match gp {
178 ast::GenericParam::ConstParam(_) => 2,
179 ast::GenericParam::LifetimeParam(_) => 0,
180 ast::GenericParam::TypeParam(_) => 1,
187 use crate::tests::{check_assist, check_assist_not_applicable};
192 fn test_not_applicable_without_selection() {
193 check_assist_not_applicable(
197 field: $0(u8, u8, u8),
204 fn test_simple_types() {
223 fn test_generic_type_arg() {
246 fn test_inner_type_arg() {
252 v: Vec<Vec<$0Vec<u8>$0>>,
257 type $0Type = Vec<u8>;
267 fn test_extract_inner_type() {
286 fn extract_from_impl_or_trait() {
287 // When invoked in an impl/trait, extracted type alias should be placed next to the
288 // impl/trait, not inside.
293 fn f() -> $0(u8, u8)$0 {}
297 type $0Type = (u8, u8);
308 fn f() -> $0(u8, u8)$0 {}
312 type $0Type = (u8, u8);
345 struct Struct<const C: usize>;
346 impl<'outer, Outer, const OUTER: usize> () {
347 fn func<'inner, Inner, const INNER: usize>(_: $0&(Struct<INNER>, Struct<OUTER>, Outer, &'inner (), Inner, &'outer ())$0) {}
351 struct Struct<const C: usize>;
352 type $0Type<'inner, 'outer, Outer, Inner, const INNER: usize, const OUTER: usize> = &(Struct<INNER>, Struct<OUTER>, Outer, &'inner (), Inner, &'outer ());
354 impl<'outer, Outer, const OUTER: usize> () {
355 fn func<'inner, Inner, const INNER: usize>(_: Type<'inner, 'outer, Outer, Inner, INNER, OUTER>) {}