1 use syntax::ast::{self, AstNode, HasGenericParams, HasName};
3 use crate::{AssistContext, AssistId, AssistKind, Assists};
5 // Assist: add_lifetime_to_type
7 // Adds a new lifetime to a struct, enum or union.
22 pub(crate) fn add_lifetime_to_type(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<()> {
23 let ref_type_focused = ctx.find_node_at_offset::<ast::RefType>()?;
24 if ref_type_focused.lifetime().is_some() {
28 let node = ctx.find_node_at_offset::<ast::Adt>()?;
29 let has_lifetime = node
31 .map_or(false, |gen_list| gen_list.lifetime_params().next().is_some());
37 let ref_types = fetch_borrowed_types(&node)?;
38 let target = node.syntax().text_range();
41 AssistId("add_lifetime_to_type", AssistKind::Generate),
45 match node.generic_param_list() {
47 if let Some(left_angle) = gen_param.l_angle_token() {
48 builder.insert(left_angle.text_range().end(), "'a, ");
52 if let Some(name) = node.name() {
53 builder.insert(name.syntax().text_range().end(), "<'a>");
58 for ref_type in ref_types {
59 if let Some(amp_token) = ref_type.amp_token() {
60 builder.insert(amp_token.text_range().end(), "'a ");
67 fn fetch_borrowed_types(node: &ast::Adt) -> Option<Vec<ast::RefType>> {
68 let ref_types: Vec<ast::RefType> = match node {
69 ast::Adt::Enum(enum_) => {
70 let variant_list = enum_.variant_list()?;
73 .filter_map(|variant| {
74 let field_list = variant.field_list()?;
76 find_ref_types_from_field_list(&field_list)
81 ast::Adt::Struct(strukt) => {
82 let field_list = strukt.field_list()?;
83 find_ref_types_from_field_list(&field_list)?
85 ast::Adt::Union(un) => {
86 let record_field_list = un.record_field_list()?;
89 .filter_map(|r_field| {
90 if let ast::Type::RefType(ref_type) = r_field.ty()? {
91 if ref_type.lifetime().is_none() {
92 return Some(ref_type);
102 if ref_types.is_empty() {
109 fn find_ref_types_from_field_list(field_list: &ast::FieldList) -> Option<Vec<ast::RefType>> {
110 let ref_types: Vec<ast::RefType> = match field_list {
111 ast::FieldList::RecordFieldList(record_list) => record_list
114 if let ast::Type::RefType(ref_type) = f.ty()? {
115 if ref_type.lifetime().is_none() {
116 return Some(ref_type);
123 ast::FieldList::TupleFieldList(tuple_field_list) => tuple_field_list
126 if let ast::Type::RefType(ref_type) = f.ty()? {
127 if ref_type.lifetime().is_none() {
128 return Some(ref_type);
137 if ref_types.is_empty() {
146 use crate::tests::{check_assist, check_assist_not_applicable};
151 fn add_lifetime_to_struct() {
153 add_lifetime_to_type,
154 r#"struct Foo { a: &$0i32 }"#,
155 r#"struct Foo<'a> { a: &'a i32 }"#,
159 add_lifetime_to_type,
160 r#"struct Foo { a: &$0i32, b: &usize }"#,
161 r#"struct Foo<'a> { a: &'a i32, b: &'a usize }"#,
165 add_lifetime_to_type,
166 r#"struct Foo { a: &$0i32, b: usize }"#,
167 r#"struct Foo<'a> { a: &'a i32, b: usize }"#,
171 add_lifetime_to_type,
172 r#"struct Foo<T> { a: &$0T, b: usize }"#,
173 r#"struct Foo<'a, T> { a: &'a T, b: usize }"#,
176 check_assist_not_applicable(add_lifetime_to_type, r#"struct Foo<'a> { a: &$0'a i32 }"#);
177 check_assist_not_applicable(add_lifetime_to_type, r#"struct Foo { a: &'a$0 i32 }"#);
181 fn add_lifetime_to_enum() {
183 add_lifetime_to_type,
184 r#"enum Foo { Bar { a: i32 }, Other, Tuple(u32, &$0u32)}"#,
185 r#"enum Foo<'a> { Bar { a: i32 }, Other, Tuple(u32, &'a u32)}"#,
189 add_lifetime_to_type,
190 r#"enum Foo { Bar { a: &$0i32 }}"#,
191 r#"enum Foo<'a> { Bar { a: &'a i32 }}"#,
195 add_lifetime_to_type,
196 r#"enum Foo<T> { Bar { a: &$0i32, b: &T }}"#,
197 r#"enum Foo<'a, T> { Bar { a: &'a i32, b: &'a T }}"#,
200 check_assist_not_applicable(
201 add_lifetime_to_type,
202 r#"enum Foo<'a> { Bar { a: &$0'a i32 }}"#,
204 check_assist_not_applicable(add_lifetime_to_type, r#"enum Foo { Bar, $0Misc }"#);
208 fn add_lifetime_to_union() {
210 add_lifetime_to_type,
211 r#"union Foo { a: &$0i32 }"#,
212 r#"union Foo<'a> { a: &'a i32 }"#,
216 add_lifetime_to_type,
217 r#"union Foo { a: &$0i32, b: &usize }"#,
218 r#"union Foo<'a> { a: &'a i32, b: &'a usize }"#,
222 add_lifetime_to_type,
223 r#"union Foo<T> { a: &$0T, b: usize }"#,
224 r#"union Foo<'a, T> { a: &'a T, b: usize }"#,
227 check_assist_not_applicable(add_lifetime_to_type, r#"struct Foo<'a> { a: &'a $0i32 }"#);