Add from_over_into replace for type in Self reference

This commit is contained in:
MarcusGrass 2023-05-31 17:46:05 +02:00
parent 423f081089
commit d4b388eb43
No known key found for this signature in database
GPG key ID: B3F995FE064E3AA9
4 changed files with 108 additions and 2 deletions

View file

@ -163,7 +163,8 @@ fn convert_to_from(
let PatKind::Binding(.., self_ident, None) = input.pat.kind else { return None };
let from = snippet_opt(cx, self_ty.span)?;
let into = snippet_opt(cx, target_ty.span)?;
// If Self is used, it refers to `self_ty`, which is now out `from` snippet
let into = replace_self(&snippet_opt(cx, target_ty.span)?, &from);
let mut suggestions = vec![
// impl Into<T> for U -> impl From<T> for U
@ -212,3 +213,75 @@ fn convert_to_from(
Some(suggestions)
}
fn replace_self(input: &str, replace_with: &str) -> String {
const SELF: &str = "Self";
let mut chunks = input.split(SELF).peekable();
if let Some(first) = chunks.next() {
let mut last_ended_with_break = false;
// Heuristic, we're making a guess that the expansion probably doesn't exceed `input.len() * 2`
let mut output = String::with_capacity(input.len() * 2);
if first.is_empty() || first.ends_with(word_break) {
last_ended_with_break = true;
}
output.push_str(first);
while let Some(val) = chunks.next() {
let is_last = chunks.peek().is_none();
if last_ended_with_break && is_last && val.is_empty() {
output.push_str(replace_with);
break;
}
let this_starts_with_break = val.starts_with(word_break);
let this_ends_with_break = val.ends_with(word_break);
if this_starts_with_break && last_ended_with_break {
output.push_str(replace_with);
} else {
output.push_str(SELF);
}
output.push_str(val);
last_ended_with_break = this_ends_with_break;
}
output
} else {
input.to_string()
}
}
#[inline]
fn word_break(ch: char) -> bool {
!ch.is_alphanumeric()
}
#[cfg(test)]
mod tests {
use crate::from_over_into::replace_self;
#[test]
fn replace_doesnt_touch_coincidental_self() {
let input = "impl Into<SelfType> for String {";
assert_eq!(input, &replace_self(input, "T"));
}
#[test]
fn replace_replaces_self() {
let input = "impl Into<Self> for String {";
assert_eq!("impl Into<String> for String {", &replace_self(input, "String"));
}
#[test]
fn replace_replaces_self_many() {
let input = "impl Into<Self<Self<SelfSelfSelfSelf>>> for Self {";
assert_eq!(
"impl Into<String<String<SelfSelfSelfSelf>>> for String {",
&replace_self(input, "String")
);
}
#[test]
fn replace_replaces_self_many_starts_ends_self() {
let input = "Self impl Into<Self<Self<SelfSelf>>> for Self";
assert_eq!(
"String impl Into<String<String<SelfSelf>>> for String",
&replace_self(input, "String")
);
}
}