rust-lang / rust-lang/rust-analyzer

Weird handling of Result around function extraction

Open
#18,467 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

A-assists C-bug
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:

  1. Since in the current scope, Result is actually syn::Result, as written the return type of fun_name is invalid. That's an understandable mistake, but ideally RA should notice and disambiguate.
  2. I can't work out why RA thinks the extracted function could ever return Err(Ok(...)) as the signature allows, presumably also the reason the syn::Error thrown when num_parameters is 0 has grown an extra layer of Err it previously didn't have.
  3. 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

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.