8415: Fix faulty assertion when extracting function with macro call r=matklad a=brandondong

**Reproduction:**
```rust
fn main() {
    let n = 1;
    let k = n * n;
    dbg!(n);
}
```
1. Select the second and third lines of the main function. Use the "Extract into function" code assist.
2. Panic occurs in debug, error is logged in release: "[ERROR ide_assists::handlers::extract_function] assertion failed: matches!(path, ast :: Expr :: PathExpr(_))".
3. Function generates successfully on release where the panic was bypassed.
```rust
fn fun_name(n: i32) {
    let k = n * n;
    dbg!(n);
}
```

**Cause:**
- The generated function will take `n` as a parameter. The extraction logic needs to search the usages of `n` to determine whether it is used mutably or not. The helper `path_element_of_reference` is called for each usage but the second usage is a macro call and fails the `Expr::PathExpr(_)` match assertion.
- The caller of `path_element_of_reference` does implicitly assume it to be a `Expr::PathExpr(_)` in how it looks at its parent node for determining whether it is used mutably. This logic will not work for macros.
- I'm not sure if there are any other cases besides macros where it could be something other than a `Expr::PathExpr(_)`. I tried various examples and could not find any.

**Fix:**
- Update assertion to include the macro case.
- Add a FIXME to properly handle checking if a macro usage requires mutable access. For now, return false instead of running the existing logic that is tailored for `Expr::PathExpr(_)`'s.

Co-authored-by: Brandon <brandondong604@hotmail.com>
This commit is contained in:
bors[bot] 2021-04-13 11:39:03 +00:00 committed by GitHub
commit e6728a8cd3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -736,6 +736,14 @@ fn reference_is_exclusive(
/// checks if this expr requires `&mut` access, recurses on field access
fn expr_require_exclusive_access(ctx: &AssistContext, expr: &ast::Expr) -> Option<bool> {
match expr {
ast::Expr::MacroCall(_) => {
// FIXME: expand macro and check output for mutable usages of the variable?
return None;
}
_ => (),
}
let parent = expr.syntax().parent()?;
if let Some(bin_expr) = ast::BinExpr::cast(parent.clone()) {
@ -794,7 +802,7 @@ impl HasTokenAtOffset for SyntaxNode {
}
}
/// find relevant `ast::PathExpr` for reference
/// find relevant `ast::Expr` for reference
///
/// # Preconditions
///
@ -811,7 +819,11 @@ fn path_element_of_reference(
stdx::never!(false, "cannot find path parent of variable usage: {:?}", token);
None
})?;
stdx::always!(matches!(path, ast::Expr::PathExpr(_)));
stdx::always!(
matches!(path, ast::Expr::PathExpr(_) | ast::Expr::MacroCall(_)),
"unexpected expression type for variable usage: {:?}",
path
);
Some(path)
}
@ -3462,4 +3474,36 @@ fn foo() -> Result<(), i64> {
}"##,
);
}
#[test]
fn param_usage_in_macro() {
check_assist(
extract_function,
r"
macro_rules! m {
($val:expr) => { $val };
}
fn foo() {
let n = 1;
$0let k = n * m!(n);$0
let m = k + 1;
}",
r"
macro_rules! m {
($val:expr) => { $val };
}
fn foo() {
let n = 1;
let k = fun_name(n);
let m = k + 1;
}
fn $0fun_name(n: i32) -> i32 {
let k = n * m!(n);
k
}",
);
}
}