rust-lang / rust-lang/rust-analyzer
Weird handling of Result around function extraction
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 16.9k
- Forks
- 2.2k
- Avg merge
- 1d 12h
- Merged PRs (30d)
- 72
Description
rust-analyzer version: 0.4.2163-standalone (cf8f950ba 2024-10-28)
rustc version: rustc 1.83.0-nightly (52fd99839 2024-10-10)
editor or extension: VSCode v0.4.2165
relevant settings: None?
repository link (if public, optional): shakespeare but this issue is entirely about syntax so that's not strictly neccesary
code snippet to reproduce:
Given the following function
use syn::{Arm, Attribute, Expr, Ident, ItemImpl, Path, Result};
fn dispatch_case(role_name: &RoleName, payload_type: &Path, fun: &syn::ImplItemFn) -> Result<Arm> {
let num_parameters = fun.sig.number_of_payload_parameters();
if num_parameters == 0 {
return Err(syn::Error::new(
fun.span(),
"Performance method cannot have no receiver",
));
}
let names = (0..num_parameters - 1).map(|n| format_ident!("_{n}"));
let call_params = if fun.sig.has_context_input() {
Either::Left(std::iter::once(format_ident!("context")).chain(names.clone()))
} else {
Either::Right(names.clone())
};
let variant_name = fun.sig.enum_variant_name();
// extra stuff
}
Asking to extract the start of the function down to the newline just before variant_name is defined results in:
fn dispatch_case(role_name: &RoleName, payload_type: &Path, fun: &syn::ImplItemFn) -> Result<Arm> {
let (names, call_params) = match fun_name(fun) {
Ok(value) => value,
Err(value) => return value,
};
// Other stuff
}
fn fun_name(
fun: &syn::ImplItemFn,
) -> Result<
(
std::iter::Map<std::ops::Range<usize>, impl FnMut(usize) -> Ident>,
Either<
std::iter::Chain<
std::iter::Once<Ident>,
std::iter::Map<std::ops::Range<usize>, impl FnMut(usize) -> Ident>,
>,
std::iter::Map<std::ops::Range<usize>, impl FnMut(usize) -> Ident>,
>,
),
std::result::Result<Arm, syn::Error>,
> {
let num_parameters = fun.sig.number_of_payload_parameters();
if num_parameters == 0 {
return Err(Err(syn::Error::new(
fun.span(),
"Performance method cannot have no receiver",
)));
}
let names = (0..num_parameters - 1).map(|n| format_ident!("_{n}"));
let call_params = if fun.sig.has_context_input() {
Either::Left(std::iter::once(format_ident!("context")).chain(names.clone()))
} else {
Either::Right(names.clone())
};
Ok((names, call_params))
}
There's a number of things that don't make sense to me about the transformed code:
- Since in the current scope,
Resultis actuallysyn::Result, as written the return type offun_nameis invalid. That's an understandable mistake, but ideally RA should notice and disambiguate. - I can't work out why RA thinks the extracted function could ever return
Err(Ok(...))as the signature allows, presumably also the reason thesyn::Errorthrown whennum_parametersis 0 has grown an extra layer ofErrit previously didn't have. - It took me a long time to understand why the match statement it left behind was necessary and as far as I can tell it's completely equivalent to
fun_name(fun)?, which would more idiomatic. Is it possible for RA to recognise that pattern?
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Reproduce the issue with the provided Rust dispatch_case example and the function-extraction action. Inspect the extraction logic responsible for inferred return types, error wrapping, and propagation in the generated function. Done means extraction produces valid Result types and preserves equivalent, idiomatic error handling without the extra Err layer.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- devtools
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 42/100