Skip to main content

ide_assists/handlers/
inline_type_alias.rs

1// Some ideas for future improvements:
2// - Support replacing aliases which are used in expressions, e.g. `A::new()`.
3// - Remove unused aliases if there are no longer any users, see inline_call.rs.
4
5use hir::{HasSource, PathResolution};
6use ide_db::FxHashMap;
7use ide_db::{
8    defs::Definition, imports::insert_use::remove_use_tree_if_simple, search::FileReference,
9};
10use itertools::Itertools;
11use syntax::ast::syntax_factory::SyntaxFactory;
12use syntax::syntax_editor::SyntaxEditor;
13use syntax::{
14    AstNode, NodeOrToken, SyntaxKind, SyntaxNode, T,
15    ast::{self, HasGenericParams, HasName},
16};
17
18use crate::{
19    AssistId,
20    assist_context::{AssistContext, Assists},
21};
22
23use super::inline_call::split_refs_and_uses;
24
25// Assist: inline_type_alias_uses
26//
27// Inline a type alias into all of its uses where possible.
28//
29// ```
30// type $0A = i32;
31// fn id(x: A) -> A {
32//     x
33// };
34// fn foo() {
35//     let _: A = 3;
36// }
37// ```
38// ->
39// ```
40//
41// fn id(x: i32) -> i32 {
42//     x
43// };
44// fn foo() {
45//     let _: i32 = 3;
46// }
47// ```
48pub(crate) fn inline_type_alias_uses(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
49    let name = ctx.find_node_at_offset::<ast::Name>()?;
50    let ast_alias = name.syntax().parent().and_then(ast::TypeAlias::cast)?;
51
52    let hir_alias = ctx.sema.to_def(&ast_alias)?;
53    let concrete_type = ast_alias.ty()?;
54
55    let usages = Definition::TypeAlias(hir_alias).usages(&ctx.sema);
56    if !usages.at_least_one() {
57        return None;
58    }
59
60    // until this is ok
61
62    acc.add(
63        AssistId::refactor_inline("inline_type_alias_uses"),
64        "Inline type alias into all uses",
65        name.syntax().text_range(),
66        |builder| {
67            let usages = usages.all();
68            let mut definition_deleted = false;
69
70            let mut inline_refs_for_file = |file_id, refs: Vec<FileReference>| {
71                let source = ctx.sema.parse(file_id);
72                let editor = builder.make_editor(source.syntax());
73
74                let (path_types, path_type_uses) = split_refs_and_uses(refs, |path_type| {
75                    path_type.syntax().ancestors().nth(3).and_then(ast::PathType::cast)
76                });
77                path_type_uses
78                    .iter()
79                    .for_each(|use_tree| remove_use_tree_if_simple(use_tree, &editor));
80
81                for (target, replacement) in path_types.into_iter().filter_map(|path_type| {
82                    let replacement =
83                        inline(&ast_alias, &path_type)?.replace_generic(&concrete_type);
84                    let target = path_type.syntax().clone();
85                    Some((target, replacement))
86                }) {
87                    editor.replace(target, replacement);
88                }
89
90                if file_id.file_id(ctx.db()) == ctx.vfs_file_id() {
91                    editor.delete(ast_alias.syntax());
92                    definition_deleted = true;
93                }
94                builder.add_file_edits(file_id.file_id(ctx.db()), editor);
95            };
96
97            for (file_id, refs) in usages.into_iter() {
98                inline_refs_for_file(file_id, refs);
99            }
100            if !definition_deleted {
101                let editor = builder.make_editor(ast_alias.syntax());
102                editor.delete(ast_alias.syntax());
103                builder.add_file_edits(ctx.vfs_file_id(), editor)
104            }
105        },
106    )
107}
108
109// Assist: inline_type_alias
110//
111// Replace a type alias with its concrete type.
112//
113// ```
114// type A<T = u32> = Vec<T>;
115//
116// fn main() {
117//     let a: $0A;
118// }
119// ```
120// ->
121// ```
122// type A<T = u32> = Vec<T>;
123//
124// fn main() {
125//     let a: Vec<u32>;
126// }
127// ```
128pub(crate) fn inline_type_alias(acc: &mut Assists, ctx: &AssistContext<'_, '_>) -> Option<()> {
129    let alias_instance = ctx.find_node_at_offset::<ast::PathType>()?;
130    let concrete_type;
131    let replacement;
132    match alias_instance.path()?.as_single_name_ref() {
133        Some(nameref) if nameref.Self_token().is_some() => {
134            match ctx.sema.resolve_path(&alias_instance.path()?)? {
135                PathResolution::SelfType(imp) => {
136                    concrete_type = imp.source(ctx.db())?.value.self_ty()?;
137                }
138                // FIXME: should also work in ADT definitions
139                _ => return None,
140            }
141
142            replacement = Replacement::Plain;
143        }
144        _ => {
145            let alias = get_type_alias(ctx, &alias_instance)?;
146            concrete_type = alias.ty()?;
147            replacement = inline(&alias, &alias_instance)?;
148        }
149    }
150
151    acc.add(
152        AssistId::refactor_inline("inline_type_alias"),
153        "Inline type alias",
154        alias_instance.syntax().text_range(),
155        |builder| {
156            let editor = builder.make_editor(alias_instance.syntax());
157            let replace = replacement.replace_generic(&concrete_type);
158            editor.replace(alias_instance.syntax(), replace);
159            builder.add_file_edits(ctx.vfs_file_id(), editor);
160        },
161    )
162}
163
164impl Replacement {
165    fn replace_generic(&self, concrete_type: &ast::Type) -> SyntaxNode {
166        match self {
167            Replacement::Generic { lifetime_map, const_and_type_map } => {
168                create_replacement(lifetime_map, const_and_type_map, concrete_type)
169            }
170            Replacement::Plain => concrete_type.syntax().clone(),
171        }
172    }
173}
174
175enum Replacement {
176    Generic { lifetime_map: LifetimeMap, const_and_type_map: ConstAndTypeMap },
177    Plain,
178}
179
180fn inline(alias_def: &ast::TypeAlias, alias_instance: &ast::PathType) -> Option<Replacement> {
181    let repl = if let Some(alias_generics) = alias_def.generic_param_list() {
182        if alias_generics.generic_params().next().is_none() {
183            cov_mark::hit!(no_generics_params);
184            return None;
185        }
186        let instance_args =
187            alias_instance.syntax().descendants().find_map(ast::GenericArgList::cast);
188
189        Replacement::Generic {
190            lifetime_map: LifetimeMap::new(&instance_args, &alias_generics)?,
191            const_and_type_map: ConstAndTypeMap::new(&instance_args, &alias_generics)?,
192        }
193    } else {
194        Replacement::Plain
195    };
196    Some(repl)
197}
198
199struct LifetimeMap(FxHashMap<String, ast::Lifetime>);
200
201impl LifetimeMap {
202    fn new(
203        instance_args: &Option<ast::GenericArgList>,
204        alias_generics: &ast::GenericParamList,
205    ) -> Option<Self> {
206        let mut inner = FxHashMap::default();
207        let make = SyntaxFactory::without_mappings();
208        let wildcard_lifetime = make.lifetime("'_");
209        let lifetimes = alias_generics
210            .lifetime_params()
211            .filter_map(|lp| lp.lifetime())
212            .map(|l| l.to_string())
213            .collect_vec();
214
215        for lifetime in &lifetimes {
216            inner.insert(lifetime.to_string(), wildcard_lifetime.clone());
217        }
218
219        if let Some(instance_generic_args_list) = &instance_args {
220            for (index, lifetime) in instance_generic_args_list
221                .lifetime_args()
222                .filter_map(|arg| arg.lifetime())
223                .enumerate()
224            {
225                let key = match lifetimes.get(index) {
226                    Some(key) => key,
227                    None => {
228                        cov_mark::hit!(too_many_lifetimes);
229                        return None;
230                    }
231                };
232
233                inner.insert(key.clone(), lifetime);
234            }
235        }
236
237        Some(Self(inner))
238    }
239}
240
241struct ConstAndTypeMap(FxHashMap<String, SyntaxNode>);
242
243impl ConstAndTypeMap {
244    fn new(
245        instance_args: &Option<ast::GenericArgList>,
246        alias_generics: &ast::GenericParamList,
247    ) -> Option<Self> {
248        let mut inner = FxHashMap::default();
249        let instance_generics = generic_args_to_const_and_type_generics(instance_args);
250        let alias_generics = generic_param_list_to_const_and_type_generics(alias_generics);
251
252        if instance_generics.len() > alias_generics.len() {
253            cov_mark::hit!(too_many_generic_args);
254            return None;
255        }
256
257        // Any declaration generics that don't have a default value must have one
258        // provided by the instance.
259        for (i, declaration_generic) in alias_generics.iter().enumerate() {
260            let key = declaration_generic.replacement_key()?;
261
262            if let Some(instance_generic) = instance_generics.get(i) {
263                inner.insert(key, instance_generic.replacement_value()?);
264            } else if let Some(value) = declaration_generic.replacement_value() {
265                inner.insert(key, value);
266            } else {
267                cov_mark::hit!(missing_replacement_param);
268                return None;
269            }
270        }
271
272        Some(Self(inner))
273    }
274}
275
276/// This doesn't attempt to ensure specified generics are compatible with those
277/// required by the type alias, other than lifetimes which must either all be
278/// specified or all omitted. It will replace TypeArgs with ConstArgs and vice
279/// versa if they're in the wrong position. It supports partially specified
280/// generics.
281///
282/// 1. Map the provided instance's generic args to the type alias's generic
283///    params:
284///
285///    ```ignore
286///    type A<'a, const N: usize, T = u64> = &'a [T; N];
287///          ^ alias generic params
288///    let a: A<100>;
289///            ^ instance generic args
290///
291///    generic['a] = '_ due to omission
292///    generic[N] = 100 due to the instance arg
293///    generic[T] = u64 due to the default param
294///    ```
295///
296/// 2. Copy the concrete type and substitute in each found mapping:
297///
298///    ```ignore
299///    &'_ [u64; 100]
300///    ```
301///
302/// 3. Remove wildcard lifetimes entirely:
303///
304///    ```ignore
305///    &[u64; 100]
306///    ```
307fn create_replacement(
308    lifetime_map: &LifetimeMap,
309    const_and_type_map: &ConstAndTypeMap,
310    concrete_type: &ast::Type,
311) -> SyntaxNode {
312    let (editor, updated_concrete_type) = SyntaxEditor::new(concrete_type.syntax().clone());
313    let make = editor.make();
314    let mut replacements: Vec<(SyntaxNode, SyntaxNode)> = Vec::new();
315    let mut removals: Vec<NodeOrToken<SyntaxNode, _>> = Vec::new();
316
317    for syntax in updated_concrete_type.descendants() {
318        if let Some(old_lifetime) = ast::Lifetime::cast(syntax.clone()) {
319            if let Some(new_lifetime) = lifetime_map.0.get(&old_lifetime.to_string()) {
320                if new_lifetime.text() == "'_" {
321                    // Check if this lifetime is inside a LifetimeArg (in angle brackets)
322                    if let Some(lifetime_arg) =
323                        old_lifetime.syntax().parent().and_then(ast::LifetimeArg::cast)
324                    {
325                        // Remove LifetimeArg and associated comma/whitespace
326                        let lifetime_arg_syntax = lifetime_arg.syntax();
327                        removals.push(NodeOrToken::Node(lifetime_arg_syntax.clone()));
328
329                        // Remove comma and whitespace (look forward then backward)
330                        let comma_and_ws: Vec<_> = lifetime_arg_syntax
331                            .siblings_with_tokens(syntax::Direction::Next)
332                            .skip(1)
333                            .take_while(|it| it.as_token().is_some())
334                            .take_while_inclusive(|it| it.kind() == T![,])
335                            .collect();
336
337                        if comma_and_ws.iter().any(|it| it.kind() == T![,]) {
338                            removals.extend(comma_and_ws);
339                        } else {
340                            // No comma after, try before
341                            let comma_and_ws: Vec<_> = lifetime_arg_syntax
342                                .siblings_with_tokens(syntax::Direction::Prev)
343                                .skip(1)
344                                .take_while(|it| it.as_token().is_some())
345                                .take_while_inclusive(|it| it.kind() == T![,])
346                                .collect();
347                            removals.extend(comma_and_ws);
348                        }
349                        continue;
350                    }
351                    removals.push(NodeOrToken::Node(syntax.clone()));
352                    if let Some(ws) = syntax.next_sibling_or_token()
353                        && ws.kind() == SyntaxKind::WHITESPACE
354                    {
355                        removals.push(ws);
356                    }
357                    continue;
358                }
359
360                replacements.push((syntax.clone(), new_lifetime.syntax().clone()));
361            }
362        } else if let Some(name_ref) = ast::NameRef::cast(syntax.clone()) {
363            let Some(replacement_syntax) = const_and_type_map.0.get(&name_ref.to_string()) else {
364                continue;
365            };
366            let new_string = replacement_syntax.to_string();
367            let new = if new_string == "_" {
368                make.wildcard_pat().syntax().clone()
369            } else {
370                replacement_syntax.clone()
371            };
372
373            replacements.push((syntax.clone(), new));
374        }
375    }
376
377    // Deduplicate removals to avoid intersecting changes
378    removals.sort_by_key(|n| n.text_range().start());
379    removals.dedup();
380
381    // Remove GenericArgList entirely if all its args are being removed (avoids empty angle brackets)
382    let generic_arg_lists_to_check: Vec<_> =
383        updated_concrete_type.descendants().filter_map(ast::GenericArgList::cast).collect();
384
385    for generic_arg_list in generic_arg_lists_to_check {
386        let will_be_empty = generic_arg_list.generic_args().all(|arg| match arg {
387            ast::GenericArg::LifetimeArg(lt_arg) => removals.iter().any(|removal| {
388                if let NodeOrToken::Node(node) = removal { node == lt_arg.syntax() } else { false }
389            }),
390            _ => false,
391        });
392
393        if will_be_empty && generic_arg_list.generic_args().next().is_some() {
394            removals.retain(|removal| {
395                if let NodeOrToken::Node(node) = removal {
396                    !node.ancestors().any(|anc| anc == *generic_arg_list.syntax())
397                } else {
398                    true
399                }
400            });
401            removals.push(NodeOrToken::Node(generic_arg_list.syntax().clone()));
402        }
403    }
404
405    for (old, new) in replacements {
406        editor.replace(old, new);
407    }
408
409    for syntax in removals {
410        editor.delete(syntax);
411    }
412    editor.finish().new_root().clone()
413}
414
415fn get_type_alias(ctx: &AssistContext<'_, '_>, path: &ast::PathType) -> Option<ast::TypeAlias> {
416    let resolved_path = ctx.sema.resolve_path(&path.path()?)?;
417
418    // We need the generics in the correct order to be able to map any provided
419    // instance generics to declaration generics. The `hir::TypeAlias` doesn't
420    // keep the order, so we must get the `ast::TypeAlias` from the hir
421    // definition.
422    if let PathResolution::Def(hir::ModuleDef::TypeAlias(ta)) = resolved_path {
423        Some(ctx.sema.source(ta)?.value)
424    } else {
425        None
426    }
427}
428
429enum ConstOrTypeGeneric {
430    ConstArg(ast::ConstArg),
431    TypeArg(ast::TypeArg),
432    ConstParam(ast::ConstParam),
433    TypeParam(ast::TypeParam),
434}
435
436impl ConstOrTypeGeneric {
437    fn replacement_key(&self) -> Option<String> {
438        // Only params are used as replacement keys.
439        match self {
440            ConstOrTypeGeneric::ConstParam(cp) => Some(cp.name()?.to_string()),
441            ConstOrTypeGeneric::TypeParam(tp) => Some(tp.name()?.to_string()),
442            _ => None,
443        }
444    }
445
446    fn replacement_value(&self) -> Option<SyntaxNode> {
447        Some(match self {
448            ConstOrTypeGeneric::ConstArg(ca) => ca.expr()?.syntax().clone(),
449            ConstOrTypeGeneric::TypeArg(ta) => ta.syntax().clone(),
450            ConstOrTypeGeneric::ConstParam(cp) => cp.default_val()?.syntax().clone(),
451            ConstOrTypeGeneric::TypeParam(tp) => tp.default_type()?.syntax().clone(),
452        })
453    }
454}
455
456fn generic_param_list_to_const_and_type_generics(
457    generics: &ast::GenericParamList,
458) -> Vec<ConstOrTypeGeneric> {
459    let mut others = Vec::new();
460
461    for param in generics.generic_params() {
462        match param {
463            ast::GenericParam::LifetimeParam(_) => {}
464            ast::GenericParam::ConstParam(cp) => {
465                others.push(ConstOrTypeGeneric::ConstParam(cp));
466            }
467            ast::GenericParam::TypeParam(tp) => others.push(ConstOrTypeGeneric::TypeParam(tp)),
468        }
469    }
470
471    others
472}
473
474fn generic_args_to_const_and_type_generics(
475    generics: &Option<ast::GenericArgList>,
476) -> Vec<ConstOrTypeGeneric> {
477    let mut others = Vec::new();
478
479    // It's fine for there to be no instance generics because the declaration
480    // might have default values or they might be inferred.
481    if let Some(generics) = generics {
482        for arg in generics.generic_args() {
483            match arg {
484                ast::GenericArg::TypeArg(ta) => {
485                    others.push(ConstOrTypeGeneric::TypeArg(ta));
486                }
487                ast::GenericArg::ConstArg(ca) => {
488                    others.push(ConstOrTypeGeneric::ConstArg(ca));
489                }
490                _ => {}
491            }
492        }
493    }
494
495    others
496}
497
498#[cfg(test)]
499mod test {
500    use super::*;
501    use crate::tests::{check_assist, check_assist_not_applicable};
502
503    #[test]
504    fn empty_generic_params() {
505        cov_mark::check!(no_generics_params);
506        check_assist_not_applicable(
507            inline_type_alias,
508            r#"
509type A<> = T;
510fn main() {
511    let a: $0A<u32>;
512}
513            "#,
514        );
515    }
516
517    #[test]
518    fn too_many_generic_args() {
519        cov_mark::check!(too_many_generic_args);
520        check_assist_not_applicable(
521            inline_type_alias,
522            r#"
523type A<T> = T;
524fn main() {
525    let a: $0A<u32, u64>;
526}
527            "#,
528        );
529    }
530
531    #[test]
532    fn too_many_lifetimes() {
533        cov_mark::check!(too_many_lifetimes);
534        check_assist_not_applicable(
535            inline_type_alias,
536            r#"
537type A<'a> = &'a &'b u32;
538fn f<'a>() {
539    let a: $0A<'a, 'b> = 0;
540}
541"#,
542        );
543    }
544
545    // This must be supported in order to support "inline_alias_to_users" or
546    // whatever it will be called.
547    #[test]
548    fn alias_as_expression_ignored() {
549        check_assist_not_applicable(
550            inline_type_alias,
551            r#"
552type A = Vec<u32>;
553fn main() {
554    let a: A = $0A::new();
555}
556"#,
557        );
558    }
559
560    #[test]
561    fn primitive_arg() {
562        check_assist(
563            inline_type_alias,
564            r#"
565type A<T> = T;
566fn main() {
567    let a: $0A<u32> = 0;
568}
569"#,
570            r#"
571type A<T> = T;
572fn main() {
573    let a: u32 = 0;
574}
575"#,
576        );
577    }
578
579    #[test]
580    fn no_generic_replacements() {
581        check_assist(
582            inline_type_alias,
583            r#"
584type A = Vec<u32>;
585fn main() {
586    let a: $0A;
587}
588"#,
589            r#"
590type A = Vec<u32>;
591fn main() {
592    let a: Vec<u32>;
593}
594"#,
595        );
596    }
597
598    #[test]
599    fn param_expression() {
600        check_assist(
601            inline_type_alias,
602            r#"
603type A<const N: usize = { 1 }> = [u32; N];
604fn main() {
605    let a: $0A;
606}
607"#,
608            r#"
609type A<const N: usize = { 1 }> = [u32; N];
610fn main() {
611    let a: [u32; { 1 }];
612}
613"#,
614        );
615    }
616
617    #[test]
618    fn param_default_value() {
619        check_assist(
620            inline_type_alias,
621            r#"
622type A<const N: usize = 1> = [u32; N];
623fn main() {
624    let a: $0A;
625}
626"#,
627            r#"
628type A<const N: usize = 1> = [u32; N];
629fn main() {
630    let a: [u32; 1];
631}
632"#,
633        );
634    }
635
636    #[test]
637    fn all_param_types() {
638        check_assist(
639            inline_type_alias,
640            r#"
641struct Struct<const C: usize>;
642type A<'inner1, 'outer1, Outer1, const INNER1: usize, Inner1: Clone, const OUTER1: usize> = (Struct<INNER1>, Struct<OUTER1>, Outer1, &'inner1 (), Inner1, &'outer1 ());
643fn foo<'inner2, 'outer2, Outer2, const INNER2: usize, Inner2, const OUTER2: usize>() {
644    let a: $0A<'inner2, 'outer2, Outer2, INNER2, Inner2, OUTER2>;
645}
646"#,
647            r#"
648struct Struct<const C: usize>;
649type A<'inner1, 'outer1, Outer1, const INNER1: usize, Inner1: Clone, const OUTER1: usize> = (Struct<INNER1>, Struct<OUTER1>, Outer1, &'inner1 (), Inner1, &'outer1 ());
650fn foo<'inner2, 'outer2, Outer2, const INNER2: usize, Inner2, const OUTER2: usize>() {
651    let a: (Struct<INNER2>, Struct<OUTER2>, Outer2, &'inner2 (), Inner2, &'outer2 ());
652}
653"#,
654        );
655    }
656
657    #[test]
658    fn omitted_lifetimes() {
659        check_assist(
660            inline_type_alias,
661            r#"
662type A<'l, 'r> = &'l &'r u32;
663fn main() {
664    let a: $0A;
665}
666"#,
667            r#"
668type A<'l, 'r> = &'l &'r u32;
669fn main() {
670    let a: &&u32;
671}
672"#,
673        );
674    }
675
676    #[test]
677    fn omitted_type() {
678        check_assist(
679            inline_type_alias,
680            r#"
681type A<'r, 'l, T = u32> = &'l std::collections::HashMap<&'r str, T>;
682fn main() {
683    let a: $0A<'_, '_>;
684}
685"#,
686            r#"
687type A<'r, 'l, T = u32> = &'l std::collections::HashMap<&'r str, T>;
688fn main() {
689    let a: &std::collections::HashMap<&str, u32>;
690}
691"#,
692        );
693    }
694
695    #[test]
696    fn omitted_everything() {
697        check_assist(
698            inline_type_alias,
699            r#"
700type A<'r, 'l, T = u32> = &'l std::collections::HashMap<&'r str, T>;
701fn main() {
702    let v = std::collections::HashMap<&str, u32>;
703    let a: $0A = &v;
704}
705"#,
706            r#"
707type A<'r, 'l, T = u32> = &'l std::collections::HashMap<&'r str, T>;
708fn main() {
709    let v = std::collections::HashMap<&str, u32>;
710    let a: &std::collections::HashMap<&str, u32> = &v;
711}
712"#,
713        );
714    }
715
716    // This doesn't actually cause the GenericArgsList to contain a AssocTypeArg.
717    #[test]
718    fn arg_associated_type() {
719        check_assist(
720            inline_type_alias,
721            r#"
722trait Tra { type Assoc; fn a(); }
723struct Str {}
724impl Tra for Str {
725    type Assoc = u32;
726    fn a() {
727        type A<T> = Vec<T>;
728        let a: $0A<Self::Assoc>;
729    }
730}
731"#,
732            r#"
733trait Tra { type Assoc; fn a(); }
734struct Str {}
735impl Tra for Str {
736    type Assoc = u32;
737    fn a() {
738        type A<T> = Vec<T>;
739        let a: Vec<Self::Assoc>;
740    }
741}
742"#,
743        );
744    }
745
746    #[test]
747    fn param_default_associated_type() {
748        check_assist(
749            inline_type_alias,
750            r#"
751trait Tra { type Assoc; fn a() }
752struct Str {}
753impl Tra for Str {
754    type Assoc = u32;
755    fn a() {
756        type A<T = Self::Assoc> = Vec<T>;
757        let a: $0A;
758    }
759}
760"#,
761            r#"
762trait Tra { type Assoc; fn a() }
763struct Str {}
764impl Tra for Str {
765    type Assoc = u32;
766    fn a() {
767        type A<T = Self::Assoc> = Vec<T>;
768        let a: Vec<Self::Assoc>;
769    }
770}
771"#,
772        );
773    }
774
775    #[test]
776    fn function_pointer() {
777        check_assist(
778            inline_type_alias,
779            r#"
780type A = fn(u32);
781fn foo(a: u32) {}
782fn main() {
783    let a: $0A = foo;
784}
785"#,
786            r#"
787type A = fn(u32);
788fn foo(a: u32) {}
789fn main() {
790    let a: fn(u32) = foo;
791}
792"#,
793        );
794    }
795
796    #[test]
797    fn closure() {
798        check_assist(
799            inline_type_alias,
800            r#"
801type A = Box<dyn FnOnce(u32) -> u32>;
802fn main() {
803    let a: $0A = Box::new(|_| 0);
804}
805"#,
806            r#"
807type A = Box<dyn FnOnce(u32) -> u32>;
808fn main() {
809    let a: Box<dyn FnOnce(u32) -> u32> = Box::new(|_| 0);
810}
811"#,
812        );
813    }
814
815    // Type aliases can't be used in traits, but someone might use the assist to
816    // fix the error.
817    #[test]
818    fn bounds() {
819        check_assist(
820            inline_type_alias,
821            r#"type A = std::io::Write; fn f<T>() where T: $0A {}"#,
822            r#"type A = std::io::Write; fn f<T>() where T: std::io::Write {}"#,
823        );
824    }
825
826    #[test]
827    fn function_parameter() {
828        check_assist(
829            inline_type_alias,
830            r#"
831type A = std::io::Write;
832fn f(a: impl $0A) {}
833"#,
834            r#"
835type A = std::io::Write;
836fn f(a: impl std::io::Write) {}
837"#,
838        );
839    }
840
841    #[test]
842    fn arg_expression() {
843        check_assist(
844            inline_type_alias,
845            r#"
846type A<const N: usize> = [u32; N];
847fn main() {
848    let a: $0A<{ 1 + 1 }>;
849}
850"#,
851            r#"
852type A<const N: usize> = [u32; N];
853fn main() {
854    let a: [u32; { 1 + 1 }];
855}
856"#,
857        )
858    }
859
860    #[test]
861    fn alias_instance_generic_path() {
862        check_assist(
863            inline_type_alias,
864            r#"
865type A<const N: usize> = [u32; N];
866fn main() {
867    let a: $0A<u32::MAX>;
868}
869"#,
870            r#"
871type A<const N: usize> = [u32; N];
872fn main() {
873    let a: [u32; u32::MAX];
874}
875"#,
876        )
877    }
878
879    #[test]
880    fn generic_type() {
881        check_assist(
882            inline_type_alias,
883            r#"
884type A = String;
885fn f(a: Vec<$0A>) {}
886"#,
887            r#"
888type A = String;
889fn f(a: Vec<String>) {}
890"#,
891        );
892    }
893
894    #[test]
895    fn missing_replacement_param() {
896        cov_mark::check!(missing_replacement_param);
897        check_assist_not_applicable(
898            inline_type_alias,
899            r#"
900type A<U> = Vec<T>;
901fn main() {
902    let a: $0A;
903}
904"#,
905        );
906    }
907
908    #[test]
909    fn full_path_type_is_replaced() {
910        check_assist(
911            inline_type_alias,
912            r#"
913mod foo {
914    pub type A = String;
915}
916fn main() {
917    let a: foo::$0A;
918}
919"#,
920            r#"
921mod foo {
922    pub type A = String;
923}
924fn main() {
925    let a: String;
926}
927"#,
928        );
929    }
930
931    #[test]
932    fn inline_self_type() {
933        check_assist(
934            inline_type_alias,
935            r#"
936struct Strukt;
937
938impl Strukt {
939    fn new() -> Self$0 {}
940}
941"#,
942            r#"
943struct Strukt;
944
945impl Strukt {
946    fn new() -> Strukt {}
947}
948"#,
949        );
950        check_assist(
951            inline_type_alias,
952            r#"
953struct Strukt<'a, T, const C: usize>(&'a [T; C]);
954
955impl<T, const C: usize> Strukt<'_, T, C> {
956    fn new() -> Self$0 {}
957}
958"#,
959            r#"
960struct Strukt<'a, T, const C: usize>(&'a [T; C]);
961
962impl<T, const C: usize> Strukt<'_, T, C> {
963    fn new() -> Strukt<'_, T, C> {}
964}
965"#,
966        );
967        check_assist(
968            inline_type_alias,
969            r#"
970struct Strukt<'a, T, const C: usize>(&'a [T; C]);
971
972trait Tr<'b, T> {}
973
974impl<T, const C: usize> Tr<'static, u8> for Strukt<'_, T, C> {
975    fn new() -> Self$0 {}
976}
977"#,
978            r#"
979struct Strukt<'a, T, const C: usize>(&'a [T; C]);
980
981trait Tr<'b, T> {}
982
983impl<T, const C: usize> Tr<'static, u8> for Strukt<'_, T, C> {
984    fn new() -> Strukt<'_, T, C> {}
985}
986"#,
987        );
988
989        check_assist_not_applicable(
990            inline_type_alias,
991            r#"
992trait Tr {
993    fn new() -> Self$0;
994}
995"#,
996        );
997    }
998
999    #[test]
1000    fn inline_types_with_lifetime() {
1001        check_assist(
1002            inline_type_alias_uses,
1003            r#"
1004struct A<'a, 'b>(pub &'a mut &'b mut ());
1005
1006type $0T<'a, 'b> = A<'a, 'b>;
1007
1008fn foo(_: T) {}
1009"#,
1010            r#"
1011struct A<'a, 'b>(pub &'a mut &'b mut ());
1012
1013
1014
1015fn foo(_: A) {}
1016"#,
1017        );
1018    }
1019
1020    #[test]
1021    fn mixed_lifetime_and_type_args() {
1022        check_assist(
1023            inline_type_alias,
1024            r#"
1025type Foo<'a, T> = Bar<'a, T>;
1026struct Bar<'a, T>(&'a T);
1027fn main() {
1028    let a: $0Foo<u32>;
1029}
1030"#,
1031            r#"
1032type Foo<'a, T> = Bar<'a, T>;
1033struct Bar<'a, T>(&'a T);
1034fn main() {
1035    let a: Bar<u32>;
1036}
1037"#,
1038        );
1039    }
1040
1041    mod inline_type_alias_uses {
1042        use crate::{handlers::inline_type_alias::inline_type_alias_uses, tests::check_assist};
1043
1044        #[test]
1045        fn inline_uses() {
1046            check_assist(
1047                inline_type_alias_uses,
1048                r#"
1049type $0A = u32;
1050
1051fn foo() {
1052    let _: A = 3;
1053    let _: A = 4;
1054}
1055"#,
1056                r#"
1057
1058
1059fn foo() {
1060    let _: u32 = 3;
1061    let _: u32 = 4;
1062}
1063"#,
1064            );
1065        }
1066
1067        #[test]
1068        fn inline_uses_across_files() {
1069            check_assist(
1070                inline_type_alias_uses,
1071                r#"
1072//- /lib.rs
1073mod foo;
1074type $0T<E> = Vec<E>;
1075fn f() -> T<&str> {
1076    vec!["hello"]
1077}
1078
1079//- /foo.rs
1080use super::T;
1081fn foo() {
1082    let _: T<i8> = Vec::new();
1083}
1084"#,
1085                r#"
1086//- /lib.rs
1087mod foo;
1088
1089fn f() -> Vec<&str> {
1090    vec!["hello"]
1091}
1092
1093//- /foo.rs
1094fn foo() {
1095    let _: Vec<i8> = Vec::new();
1096}
1097"#,
1098            );
1099        }
1100
1101        #[test]
1102        fn inline_uses_across_files_2() {
1103            check_assist(
1104                inline_type_alias_uses,
1105                r#"
1106//- /lib.rs
1107mod foo;
1108type $0I = i32;
1109
1110//- /foo.rs
1111use super::I;
1112fn foo() {
1113    let _: I = 0;
1114}
1115"#,
1116                r#"
1117//- /lib.rs
1118mod foo;
1119
1120
1121//- /foo.rs
1122fn foo() {
1123    let _: i32 = 0;
1124}
1125"#,
1126            );
1127        }
1128    }
1129}