Skip to content

Commit ab62c01

Browse files
committed
Auto merge of #15696 - rmehri01:14293_tuple_return_type_to_struct, r=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
2 parents dca63d1 + 9ba8dbc commit ab62c01

File tree

5 files changed

+970
-50
lines changed

5 files changed

+970
-50
lines changed

crates/hir-ty/src/diagnostics/decl_check/case_conv.rs

+4-50
Original file line numberDiff line numberDiff line change
@@ -11,50 +11,7 @@ pub(crate) fn to_camel_case(ident: &str) -> Option<String> {
1111
return None;
1212
}
1313

14-
// Taken from rustc.
15-
let ret = ident
16-
.trim_matches('_')
17-
.split('_')
18-
.filter(|component| !component.is_empty())
19-
.map(|component| {
20-
let mut camel_cased_component = String::with_capacity(component.len());
21-
22-
let mut new_word = true;
23-
let mut prev_is_lower_case = true;
24-
25-
for c in component.chars() {
26-
// Preserve the case if an uppercase letter follows a lowercase letter, so that
27-
// `camelCase` is converted to `CamelCase`.
28-
if prev_is_lower_case && c.is_uppercase() {
29-
new_word = true;
30-
}
31-
32-
if new_word {
33-
camel_cased_component.extend(c.to_uppercase());
34-
} else {
35-
camel_cased_component.extend(c.to_lowercase());
36-
}
37-
38-
prev_is_lower_case = c.is_lowercase();
39-
new_word = false;
40-
}
41-
42-
camel_cased_component
43-
})
44-
.fold((String::new(), None), |(acc, prev): (_, Option<String>), next| {
45-
// separate two components with an underscore if their boundary cannot
46-
// be distinguished using an uppercase/lowercase case distinction
47-
let join = prev
48-
.and_then(|prev| {
49-
let f = next.chars().next()?;
50-
let l = prev.chars().last()?;
51-
Some(!char_has_case(l) && !char_has_case(f))
52-
})
53-
.unwrap_or(false);
54-
(acc + if join { "_" } else { "" } + &next, Some(next))
55-
})
56-
.0;
57-
Some(ret)
14+
Some(stdx::to_camel_case(ident))
5815
}
5916

6017
/// Converts an identifier to a lower_snake_case form.
@@ -97,7 +54,9 @@ fn is_camel_case(name: &str) -> bool {
9754
&& !name.chars().any(|snd| {
9855
let ret = match fst {
9956
None => false,
100-
Some(fst) => char_has_case(fst) && snd == '_' || char_has_case(snd) && fst == '_',
57+
Some(fst) => {
58+
stdx::char_has_case(fst) && snd == '_' || stdx::char_has_case(snd) && fst == '_'
59+
}
10160
};
10261
fst = Some(snd);
10362

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

138-
// Taken from rustc.
139-
fn char_has_case(c: char) -> bool {
140-
c.is_lowercase() || c.is_uppercase()
141-
}
142-
14397
#[cfg(test)]
14498
mod tests {
14599
use super::*;

0 commit comments

Comments
 (0)