Assist: replace anonymous lifetime with a named one

(fixes #4523)
This commit is contained in:
Jess Balint 2020-05-20 18:37:09 -05:00
parent 90332ca219
commit 2ab79c6f4d
3 changed files with 149 additions and 0 deletions

View file

@ -0,0 +1,123 @@
use crate::{AssistContext, AssistId, Assists};
use ra_syntax::{ast, ast::{TypeParamsOwner}, AstNode, SyntaxKind};
/// Assist: change_lifetime_anon_to_named
///
/// Change an anonymous lifetime to a named lifetime.
///
/// ```
/// impl Cursor<'_<|>> {
/// fn node(self) -> &SyntaxNode {
/// match self {
/// Cursor::Replace(node) | Cursor::Before(node) => node,
/// }
/// }
/// }
/// ```
/// ->
/// ```
/// impl<'a> Cursor<'a> {
/// fn node(self) -> &SyntaxNode {
/// match self {
/// Cursor::Replace(node) | Cursor::Before(node) => node,
/// }
/// }
/// }
/// ```
// TODO : How can we handle renaming any one of multiple anonymous lifetimes?
pub(crate) fn change_lifetime_anon_to_named(acc: &mut Assists, ctx: &AssistContext) -> Option<()> {
let lifetime_token = ctx.find_token_at_offset(SyntaxKind::LIFETIME)?;
let lifetime_arg = ast::LifetimeArg::cast(lifetime_token.parent())?;
if lifetime_arg.syntax().text() != "'_" {
return None;
}
let next_token = lifetime_token.next_token()?;
if next_token.kind() != SyntaxKind::R_ANGLE {
// only allow naming the last anonymous lifetime
return None;
}
match lifetime_arg.syntax().ancestors().find_map(ast::ImplDef::cast) {
Some(impl_def) => {
// get the `impl` keyword so we know where to add the lifetime argument
let impl_kw = impl_def.syntax().first_child_or_token()?.into_token()?;
if impl_kw.kind() != SyntaxKind::IMPL_KW {
return None;
}
acc.add(
AssistId("change_lifetime_anon_to_named"),
"Give anonymous lifetime a name",
lifetime_arg.syntax().text_range(),
|builder| {
match impl_def.type_param_list() {
Some(type_params) => {
builder.insert(
(u32::from(type_params.syntax().text_range().end()) - 1).into(),
", 'a",
);
},
None => {
builder.insert(
impl_kw.text_range().end(),
"<'a>",
);
},
}
builder.replace(lifetime_arg.syntax().text_range(), "'a");
},
)
}
_ => None,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::tests::{check_assist, check_assist_not_applicable};
#[test]
fn test_example_case() {
check_assist(
change_lifetime_anon_to_named,
r#"impl Cursor<'_<|>> {
fn node(self) -> &SyntaxNode {
match self {
Cursor::Replace(node) | Cursor::Before(node) => node,
}
}
}"#,
r#"impl<'a> Cursor<'a> {
fn node(self) -> &SyntaxNode {
match self {
Cursor::Replace(node) | Cursor::Before(node) => node,
}
}
}"#,
);
}
#[test]
fn test_example_case_simplified() {
check_assist(
change_lifetime_anon_to_named,
r#"impl Cursor<'_<|>> {"#,
r#"impl<'a> Cursor<'a> {"#,
);
}
#[test]
fn test_not_applicable() {
check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<'_><|> {"#);
check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<|><'_> {"#);
check_assist_not_applicable(change_lifetime_anon_to_named, r#"impl Cursor<'a<|>> {"#);
}
#[test]
fn test_with_type_parameter() {
check_assist(
change_lifetime_anon_to_named,
r#"impl<T> Cursor<T, '_<|>>"#,
r#"impl<T, 'a> Cursor<T, 'a>"#,
);
}
}

View file

@ -112,6 +112,7 @@ mod handlers {
mod add_turbo_fish; mod add_turbo_fish;
mod apply_demorgan; mod apply_demorgan;
mod auto_import; mod auto_import;
mod change_lifetime_anon_to_named;
mod change_return_type_to_result; mod change_return_type_to_result;
mod change_visibility; mod change_visibility;
mod early_return; mod early_return;
@ -151,6 +152,7 @@ mod handlers {
add_turbo_fish::add_turbo_fish, add_turbo_fish::add_turbo_fish,
apply_demorgan::apply_demorgan, apply_demorgan::apply_demorgan,
auto_import::auto_import, auto_import::auto_import,
change_lifetime_anon_to_named::change_lifetime_anon_to_named,
change_return_type_to_result::change_return_type_to_result, change_return_type_to_result::change_return_type_to_result,
change_visibility::change_visibility, change_visibility::change_visibility,
early_return::convert_to_guarded_return, early_return::convert_to_guarded_return,

View file

@ -259,6 +259,30 @@ fn main() {
} }
``` ```
## `change_lifetime_anon_to_named`
Change an anonymous lifetime to a named lifetime.
```rust
// BEFORE
impl Cursor<'_<|>> {
fn node(self) -> &SyntaxNode {
match self {
Cursor::Replace(node) | Cursor::Before(node) => node,
}
}
}
// AFTER
impl<'a> Cursor<'a> {
fn node(self) -> &SyntaxNode {
match self {
Cursor::Replace(node) | Cursor::Before(node) => node,
}
}
}
```
## `change_return_type_to_result` ## `change_return_type_to_result`
Change the function's return type to Result. Change the function's return type to Result.