Skip to content

Commit

Permalink
Auto merge of #15696 - rmehri01:14293_tuple_return_type_to_struct, r=…
Browse files Browse the repository at this point in the history
…Veykril

feat: implement tuple return type to tuple struct assist

This PR implements the `convert_tuple_return_type_to_struct` assist, for converting the return type of a function or method from a tuple to a tuple struct. Additionally, it moves the `to_camel_case` and `char_has_case` functions from `case_conv` to `stdx` so that they can be used similar to `to_lower_snake_case`.

[tuple_return_type_to_tuple_struct.webm](https://github.com/rust-lang/rust-analyzer/assets/52933714/2803ff58-fde3-4144-9495-7c7c7e139075)

Currently, the assist puts the struct definition above the function, or above the nearest `impl` or `trait` if applicable and only rewrites literal tuples that are returned in the body of the function. Additionally, it only attempts to rewrite simple tuple pattern usages with the corresponding tuple struct pattern but does so across files and modules.

I think that this is sufficient for the majority of use cases but I could be wrong. One thing I'm still not sure how to approach is handling `Self` and generics/lifetimes in the tuple type to be extracted. I was thinking of either manually figuring out what lifetimes and generics are in scope and using them (sort of similar to the `generate_function` assist) or maybe using `ctx.sema.resolve_type` and `generic_params` on `hir::Type` but this seems to not deal with lifetimes.

Closes #14293
  • Loading branch information
bors committed Oct 9, 2023
2 parents dca63d1 + 9ba8dbc commit ab62c01
Show file tree
Hide file tree
Showing 5 changed files with 970 additions and 50 deletions.
54 changes: 4 additions & 50 deletions crates/hir-ty/src/diagnostics/decl_check/case_conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,50 +11,7 @@ pub(crate) fn to_camel_case(ident: &str) -> Option<String> {
return None;
}

// Taken from rustc.
let ret = ident
.trim_matches('_')
.split('_')
.filter(|component| !component.is_empty())
.map(|component| {
let mut camel_cased_component = String::with_capacity(component.len());

let mut new_word = true;
let mut prev_is_lower_case = true;

for c in component.chars() {
// Preserve the case if an uppercase letter follows a lowercase letter, so that
// `camelCase` is converted to `CamelCase`.
if prev_is_lower_case && c.is_uppercase() {
new_word = true;
}

if new_word {
camel_cased_component.extend(c.to_uppercase());
} else {
camel_cased_component.extend(c.to_lowercase());
}

prev_is_lower_case = c.is_lowercase();
new_word = false;
}

camel_cased_component
})
.fold((String::new(), None), |(acc, prev): (_, Option<String>), next| {
// separate two components with an underscore if their boundary cannot
// be distinguished using an uppercase/lowercase case distinction
let join = prev
.and_then(|prev| {
let f = next.chars().next()?;
let l = prev.chars().last()?;
Some(!char_has_case(l) && !char_has_case(f))
})
.unwrap_or(false);
(acc + if join { "_" } else { "" } + &next, Some(next))
})
.0;
Some(ret)
Some(stdx::to_camel_case(ident))
}

/// Converts an identifier to a lower_snake_case form.
Expand Down Expand Up @@ -97,7 +54,9 @@ fn is_camel_case(name: &str) -> bool {
&& !name.chars().any(|snd| {
let ret = match fst {
None => false,
Some(fst) => char_has_case(fst) && snd == '_' || char_has_case(snd) && fst == '_',
Some(fst) => {
stdx::char_has_case(fst) && snd == '_' || stdx::char_has_case(snd) && fst == '_'
}
};
fst = Some(snd);

Expand Down Expand Up @@ -135,11 +94,6 @@ fn is_snake_case<F: Fn(char) -> bool>(ident: &str, wrong_case: F) -> bool {
})
}

// Taken from rustc.
fn char_has_case(c: char) -> bool {
c.is_lowercase() || c.is_uppercase()
}

#[cfg(test)]
mod tests {
use super::*;
Expand Down
Loading

0 comments on commit ab62c01

Please sign in to comment.