mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-12-27 16:07:46 +00:00
Merge pull request #20996 from A4-Tacks/explicit-dot-call-deref
Add ide-assist: add_explicit_method_call_deref
This commit is contained in:
commit
3aecf081f6
214
crates/ide-assists/src/handlers/add_explicit_dot_deref.rs
Normal file
214
crates/ide-assists/src/handlers/add_explicit_dot_deref.rs
Normal file
@ -0,0 +1,214 @@
|
||||
use hir::{Adjust, Mutability};
|
||||
use ide_db::assists::AssistId;
|
||||
use itertools::Itertools;
|
||||
use syntax::{
|
||||
AstNode, T,
|
||||
ast::{self, syntax_factory::SyntaxFactory},
|
||||
};
|
||||
|
||||
use crate::{AssistContext, Assists};
|
||||
|
||||
// Assist: add_explicit_method_call_deref
|
||||
//
|
||||
// Insert explicit method call reference and dereferences.
|
||||
//
|
||||
// ```
|
||||
// struct Foo;
|
||||
// impl Foo { fn foo(&self) {} }
|
||||
// fn test() {
|
||||
// Foo$0.$0foo();
|
||||
// }
|
||||
// ```
|
||||
// ->
|
||||
// ```
|
||||
// struct Foo;
|
||||
// impl Foo { fn foo(&self) {} }
|
||||
// fn test() {
|
||||
// (&Foo).foo();
|
||||
// }
|
||||
// ```
|
||||
pub(crate) fn add_explicit_method_call_deref(
|
||||
acc: &mut Assists,
|
||||
ctx: &AssistContext<'_>,
|
||||
) -> Option<()> {
|
||||
if ctx.has_empty_selection() {
|
||||
return None;
|
||||
}
|
||||
let dot_token = ctx.find_token_syntax_at_offset(T![.])?;
|
||||
if ctx.selection_trimmed() != dot_token.text_range() {
|
||||
return None;
|
||||
}
|
||||
let method_call_expr = dot_token.parent().and_then(ast::MethodCallExpr::cast)?;
|
||||
let receiver = method_call_expr.receiver()?;
|
||||
|
||||
let adjustments = ctx.sema.expr_adjustments(&receiver)?;
|
||||
let adjustments =
|
||||
adjustments.into_iter().filter_map(|adjust| simple_adjust_kind(adjust.kind)).collect_vec();
|
||||
if adjustments.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
acc.add(
|
||||
AssistId::refactor_rewrite("add_explicit_method_call_deref"),
|
||||
"Insert explicit method call derefs",
|
||||
dot_token.text_range(),
|
||||
|builder| {
|
||||
let mut edit = builder.make_editor(method_call_expr.syntax());
|
||||
let make = SyntaxFactory::without_mappings();
|
||||
let mut expr = receiver.clone();
|
||||
|
||||
for adjust_kind in adjustments {
|
||||
expr = adjust_kind.wrap_expr(expr, &make);
|
||||
}
|
||||
|
||||
expr = make.expr_paren(expr).into();
|
||||
edit.replace(receiver.syntax(), expr.syntax());
|
||||
|
||||
builder.add_file_edits(ctx.vfs_file_id(), edit);
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
fn simple_adjust_kind(adjust: Adjust) -> Option<AdjustKind> {
|
||||
match adjust {
|
||||
Adjust::NeverToAny | Adjust::Pointer(_) => None,
|
||||
Adjust::Deref(_) => Some(AdjustKind::Deref),
|
||||
Adjust::Borrow(hir::AutoBorrow::Ref(mutability)) => Some(AdjustKind::Ref(mutability)),
|
||||
Adjust::Borrow(hir::AutoBorrow::RawPtr(mutability)) => Some(AdjustKind::RefRaw(mutability)),
|
||||
}
|
||||
}
|
||||
|
||||
enum AdjustKind {
|
||||
Deref,
|
||||
Ref(Mutability),
|
||||
RefRaw(Mutability),
|
||||
}
|
||||
|
||||
impl AdjustKind {
|
||||
fn wrap_expr(self, expr: ast::Expr, make: &SyntaxFactory) -> ast::Expr {
|
||||
match self {
|
||||
AdjustKind::Deref => make.expr_prefix(T![*], expr).into(),
|
||||
AdjustKind::Ref(mutability) => make.expr_ref(expr, mutability.is_mut()),
|
||||
AdjustKind::RefRaw(mutability) => make.expr_raw_ref(expr, mutability.is_mut()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::tests::check_assist;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn works_ref() {
|
||||
check_assist(
|
||||
add_explicit_method_call_deref,
|
||||
r#"
|
||||
struct Foo;
|
||||
impl Foo { fn foo(&self) {} }
|
||||
fn test() {
|
||||
Foo$0.$0foo();
|
||||
}"#,
|
||||
r#"
|
||||
struct Foo;
|
||||
impl Foo { fn foo(&self) {} }
|
||||
fn test() {
|
||||
(&Foo).foo();
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_ref_mut() {
|
||||
check_assist(
|
||||
add_explicit_method_call_deref,
|
||||
r#"
|
||||
struct Foo;
|
||||
impl Foo { fn foo(&mut self) {} }
|
||||
fn test() {
|
||||
Foo$0.$0foo();
|
||||
}"#,
|
||||
r#"
|
||||
struct Foo;
|
||||
impl Foo { fn foo(&mut self) {} }
|
||||
fn test() {
|
||||
(&mut Foo).foo();
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_deref() {
|
||||
check_assist(
|
||||
add_explicit_method_call_deref,
|
||||
r#"
|
||||
struct Foo;
|
||||
impl Foo { fn foo(self) {} }
|
||||
fn test() {
|
||||
let foo = &Foo;
|
||||
foo$0.$0foo();
|
||||
}"#,
|
||||
r#"
|
||||
struct Foo;
|
||||
impl Foo { fn foo(self) {} }
|
||||
fn test() {
|
||||
let foo = &Foo;
|
||||
(*foo).foo();
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_reborrow() {
|
||||
check_assist(
|
||||
add_explicit_method_call_deref,
|
||||
r#"
|
||||
struct Foo;
|
||||
impl Foo { fn foo(&self) {} }
|
||||
fn test() {
|
||||
let foo = &mut Foo;
|
||||
foo$0.$0foo();
|
||||
}"#,
|
||||
r#"
|
||||
struct Foo;
|
||||
impl Foo { fn foo(&self) {} }
|
||||
fn test() {
|
||||
let foo = &mut Foo;
|
||||
(&*foo).foo();
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn works_deref_reborrow() {
|
||||
check_assist(
|
||||
add_explicit_method_call_deref,
|
||||
r#"
|
||||
//- minicore: deref
|
||||
struct Foo;
|
||||
struct Bar;
|
||||
impl core::ops::Deref for Foo {
|
||||
type Target = Bar;
|
||||
fn deref(&self) -> &Self::Target {}
|
||||
}
|
||||
impl Bar { fn bar(&self) {} }
|
||||
fn test() {
|
||||
let foo = &mut Foo;
|
||||
foo$0.$0bar();
|
||||
}"#,
|
||||
r#"
|
||||
struct Foo;
|
||||
struct Bar;
|
||||
impl core::ops::Deref for Foo {
|
||||
type Target = Bar;
|
||||
fn deref(&self) -> &Self::Target {}
|
||||
}
|
||||
impl Bar { fn bar(&self) {} }
|
||||
fn test() {
|
||||
let foo = &mut Foo;
|
||||
(&**foo).bar();
|
||||
}"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
@ -105,6 +105,7 @@ mod handlers {
|
||||
pub(crate) type Handler = fn(&mut Assists, &AssistContext<'_>) -> Option<()>;
|
||||
|
||||
mod add_braces;
|
||||
mod add_explicit_dot_deref;
|
||||
mod add_explicit_enum_discriminant;
|
||||
mod add_explicit_type;
|
||||
mod add_label_to_loop;
|
||||
@ -242,6 +243,7 @@ mod handlers {
|
||||
&[
|
||||
// These are alphabetic for the foolish consistency
|
||||
add_braces::add_braces,
|
||||
add_explicit_dot_deref::add_explicit_method_call_deref,
|
||||
add_explicit_enum_discriminant::add_explicit_enum_discriminant,
|
||||
add_explicit_type::add_explicit_type,
|
||||
add_label_to_loop::add_label_to_loop,
|
||||
|
||||
@ -69,6 +69,27 @@ enum TheEnum {
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_add_explicit_method_call_deref() {
|
||||
check_doc_test(
|
||||
"add_explicit_method_call_deref",
|
||||
r#####"
|
||||
struct Foo;
|
||||
impl Foo { fn foo(&self) {} }
|
||||
fn test() {
|
||||
Foo$0.$0foo();
|
||||
}
|
||||
"#####,
|
||||
r#####"
|
||||
struct Foo;
|
||||
impl Foo { fn foo(&self) {} }
|
||||
fn test() {
|
||||
(&Foo).foo();
|
||||
}
|
||||
"#####,
|
||||
)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn doctest_add_explicit_type() {
|
||||
check_doc_test(
|
||||
|
||||
@ -690,6 +690,13 @@ pub fn expr_macro(path: ast::Path, tt: ast::TokenTree) -> ast::MacroExpr {
|
||||
pub fn expr_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr {
|
||||
expr_from_text(&if exclusive { format!("&mut {expr}") } else { format!("&{expr}") })
|
||||
}
|
||||
pub fn expr_raw_ref(expr: ast::Expr, exclusive: bool) -> ast::Expr {
|
||||
expr_from_text(&if exclusive {
|
||||
format!("&raw mut {expr}")
|
||||
} else {
|
||||
format!("&raw const {expr}")
|
||||
})
|
||||
}
|
||||
pub fn expr_reborrow(expr: ast::Expr) -> ast::Expr {
|
||||
expr_from_text(&format!("&mut *{expr}"))
|
||||
}
|
||||
|
||||
@ -748,6 +748,22 @@ impl SyntaxFactory {
|
||||
ast.into()
|
||||
}
|
||||
|
||||
pub fn expr_raw_ref(&self, expr: ast::Expr, exclusive: bool) -> ast::Expr {
|
||||
let ast::Expr::RefExpr(ast) =
|
||||
make::expr_raw_ref(expr.clone(), exclusive).clone_for_update()
|
||||
else {
|
||||
unreachable!()
|
||||
};
|
||||
|
||||
if let Some(mut mapping) = self.mappings() {
|
||||
let mut builder = SyntaxMappingBuilder::new(ast.syntax().clone());
|
||||
builder.map_node(expr.syntax().clone(), ast.expr().unwrap().syntax().clone());
|
||||
builder.finish(&mut mapping);
|
||||
}
|
||||
|
||||
ast.into()
|
||||
}
|
||||
|
||||
pub fn expr_closure(
|
||||
&self,
|
||||
pats: impl IntoIterator<Item = ast::Param>,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user