mirror of
https://github.com/rust-lang/rust.git
synced 2025-05-03 13:37:37 +00:00
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 `¶ms` 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:
commit
ada51f2ac4
@ -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
|
// Walk up the tree while we have the same binary operator
|
||||||
while let Some(parent_expr) = expr.syntax().parent().and_then(ast::BinExpr::cast) {
|
while let Some(parent_expr) = expr.syntax().parent().and_then(ast::BinExpr::cast) {
|
||||||
if let Some(parent_op) = expr.op_kind() {
|
match expr.op_kind() {
|
||||||
if parent_op == op {
|
Some(parent_op) if parent_op == op => {
|
||||||
expr = parent_expr
|
expr = parent_expr;
|
||||||
}
|
}
|
||||||
|
_ => break,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -220,4 +221,14 @@ fn f() { !(S <= S || S < S) }
|
|||||||
cov_mark::check!(demorgan_double_parens);
|
cov_mark::check!(demorgan_double_parens);
|
||||||
check_assist(apply_demorgan, "fn f() { (x ||$0 x) }", "fn f() { !(!x && !x) }")
|
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 }",
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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 anchor = Anchor::from(&to_extract)?;
|
||||||
let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone();
|
let indent = anchor.syntax().prev_sibling_or_token()?.as_token()?.clone();
|
||||||
let target = to_extract.syntax().text_range();
|
let target = to_extract.syntax().text_range();
|
||||||
@ -79,9 +85,11 @@ pub(crate) fn extract_variable(acc: &mut Assists, ctx: &AssistContext) -> Option
|
|||||||
|
|
||||||
match anchor {
|
match anchor {
|
||||||
Anchor::Before(_) | Anchor::Replace(_) => {
|
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());
|
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)]
|
#[derive(Debug)]
|
||||||
enum Anchor {
|
enum Anchor {
|
||||||
Before(SyntaxNode),
|
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();
|
||||||
|
}"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user