11195: Correctly pass through reference modifiers when extracting a variable r=Veykril a=Vannevelj

Fixes https://github.com/rust-analyzer/rust-analyzer/issues/10034

This will parse the field expression and look at whether it is marked `&` or `&mut` and include a modifier if appropriate. The original issue only mentions `&mut params` but I've found that this issue also occurs for `&mut locals` as well as `&params` and `&locals` so I've also added tests for them.

I'd definitely be interested in hearing where I can make my code more idiomatic for Rust.

11202: fix: Fix `apply_demorgan` assist hanging for certain binary expressions r=Veykril a=Veykril

Fixes https://github.com/rust-analyzer/rust-analyzer/issues/10963
bors r+

Co-authored-by: Jeroen Vannevel <jer_vannevel@outlook.com>
Co-authored-by: Lukas Wirth <lukastw97@gmail.com>
This commit is contained in:
bors[bot] 2022-01-05 21:30:50 +00:00 committed by GitHub
commit ada51f2ac4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 366 additions and 5 deletions

View File

@ -42,10 +42,11 @@ pub(crate) fn apply_demorgan(acc: &mut Assists, ctx: &AssistContext) -> Option<(
// Walk up the tree while we have the same binary operator
while let Some(parent_expr) = expr.syntax().parent().and_then(ast::BinExpr::cast) {
if let Some(parent_op) = expr.op_kind() {
if parent_op == op {
expr = parent_expr
match expr.op_kind() {
Some(parent_op) if parent_op == op => {
expr = parent_expr;
}
_ => break,
}
}
@ -220,4 +221,14 @@ fn f() { !(S <= S || S < S) }
cov_mark::check!(demorgan_double_parens);
check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }")
}
// https://github.com/rust-analyzer/rust-analyzer/issues/10963
#[test]
fn demorgan_doesnt_hang() {
check_assist(
apply_demorgan,
"fn f() { 1 || 3 &&$0 4 || 5 }",
"fn f() { !(!1 || !3 || !4) || 5 }",
)
}
}

View File

@ -52,6 +52,12 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
}
}
let reference_modifier = match get_receiver_type(&ctx, &to_extract) {
Some(receiver_type) if receiver_type.is_mutable_reference() => "&mut ",
Some(receiver_type) if receiver_type.is_reference() => "&",
_ => "",
};
let anchor = Anchor::from(&to_extract)?;
let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone();
let target = to_extract.syntax().text_range();
@ -79,9 +85,11 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
match anchor {
Anchor::Before(_) | Anchor::Replace(_) => {
format_to!(buf, "let {} = ", var_name)
format_to!(buf, "let {} = {}", var_name, reference_modifier)
}
Anchor::WrapInBlock(_) => {
format_to!(buf, "{{ let {} = {}", var_name, reference_modifier)
}
Anchor::WrapInBlock(_) => format_to!(buf, "{{ let {} = ", var_name),
};
format_to!(buf, "{}", to_extract.syntax());
@ -146,6 +154,22 @@ fn valid_target_expr(node: SyntaxNode) -> Option<ast::Expr> {
}
}
fn get_receiver_type(ctx: &AssistContext, expression: &ast::Expr) -> Option<hir::Type> {
let receiver = get_receiver(expression.clone())?;
Some(ctx.sema.type_of_expr(&receiver)?.original())
}
/// In the expression `a.b.c.x()`, find `a`
fn get_receiver(expression: ast::Expr) -> Option<ast::Expr> {
match expression {
ast::Expr::FieldExpr(field) if field.expr().is_some() => {
let nested_expression = &field.expr()?;
get_receiver(nested_expression.to_owned())
}
_ => Some(expression),
}
}
#[derive(Debug)]
enum Anchor {
Before(SyntaxNode),
@ -900,4 +924,330 @@ const X: usize = $0100$0;
",
);
}
#[test]
fn test_extract_var_mutable_reference_parameter() {
check_assist(
extract_variable,
r#"
struct S {
vec: Vec<u8>
}
fn foo(s: &mut S) {
$0s.vec$0.push(0);
}"#,
r#"
struct S {
vec: Vec<u8>
}
fn foo(s: &mut S) {
let $0var_name = &mut s.vec;
var_name.push(0);
}"#,
);
}
#[test]
fn test_extract_var_mutable_reference_parameter_deep_nesting() {
check_assist(
extract_variable,
r#"
struct Y {
field: X
}
struct X {
field: S
}
struct S {
vec: Vec<u8>
}
fn foo(f: &mut Y) {
$0f.field.field.vec$0.push(0);
}"#,
r#"
struct Y {
field: X
}
struct X {
field: S
}
struct S {
vec: Vec<u8>
}
fn foo(f: &mut Y) {
let $0var_name = &mut f.field.field.vec;
var_name.push(0);
}"#,
);
}
#[test]
fn test_extract_var_reference_parameter() {
check_assist(
extract_variable,
r#"
struct X;
impl X {
fn do_thing(&self) {
}
}
struct S {
sub: X
}
fn foo(s: &S) {
$0s.sub$0.do_thing();
}"#,
r#"
struct X;
impl X {
fn do_thing(&self) {
}
}
struct S {
sub: X
}
fn foo(s: &S) {
let $0x = &s.sub;
x.do_thing();
}"#,
);
}
#[test]
fn test_extract_var_reference_parameter_deep_nesting() {
check_assist(
extract_variable,
r#"
struct Z;
impl Z {
fn do_thing(&self) {
}
}
struct Y {
field: Z
}
struct X {
field: Y
}
struct S {
sub: X
}
fn foo(s: &S) {
$0s.sub.field.field$0.do_thing();
}"#,
r#"
struct Z;
impl Z {
fn do_thing(&self) {
}
}
struct Y {
field: Z
}
struct X {
field: Y
}
struct S {
sub: X
}
fn foo(s: &S) {
let $0z = &s.sub.field.field;
z.do_thing();
}"#,
);
}
#[test]
fn test_extract_var_regular_parameter() {
check_assist(
extract_variable,
r#"
struct X;
impl X {
fn do_thing(&self) {
}
}
struct S {
sub: X
}
fn foo(s: S) {
$0s.sub$0.do_thing();
}"#,
r#"
struct X;
impl X {
fn do_thing(&self) {
}
}
struct S {
sub: X
}
fn foo(s: S) {
let $0x = s.sub;
x.do_thing();
}"#,
);
}
#[test]
fn test_extract_var_mutable_reference_local() {
check_assist(
extract_variable,
r#"
struct X;
struct S {
sub: X
}
impl S {
fn new() -> S {
S {
sub: X::new()
}
}
}
impl X {
fn new() -> X {
X { }
}
fn do_thing(&self) {
}
}
fn foo() {
let local = &mut S::new();
$0local.sub$0.do_thing();
}"#,
r#"
struct X;
struct S {
sub: X
}
impl S {
fn new() -> S {
S {
sub: X::new()
}
}
}
impl X {
fn new() -> X {
X { }
}
fn do_thing(&self) {
}
}
fn foo() {
let local = &mut S::new();
let $0x = &mut local.sub;
x.do_thing();
}"#,
);
}
#[test]
fn test_extract_var_reference_local() {
check_assist(
extract_variable,
r#"
struct X;
struct S {
sub: X
}
impl S {
fn new() -> S {
S {
sub: X::new()
}
}
}
impl X {
fn new() -> X {
X { }
}
fn do_thing(&self) {
}
}
fn foo() {
let local = &S::new();
$0local.sub$0.do_thing();
}"#,
r#"
struct X;
struct S {
sub: X
}
impl S {
fn new() -> S {
S {
sub: X::new()
}
}
}
impl X {
fn new() -> X {
X { }
}
fn do_thing(&self) {
}
}
fn foo() {
let local = &S::new();
let $0x = &local.sub;
x.do_thing();
}"#,
);
}
}