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")
);
}
}

View file

@ -88,4 +88,14 @@ impl Into<Opaque> for IntoOpaque {
fn into(self) -> Opaque {}
}
pub struct Lval<T>(T);
pub struct Rval<T>(T);
impl<T> From<Lval<T>> for Rval<Lval<T>> {
fn from(val: Lval<T>) -> Self {
Rval(val)
}
}
fn main() {}

View file

@ -88,4 +88,14 @@ impl Into<Opaque> for IntoOpaque {
fn into(self) -> Opaque {}
}
pub struct Lval<T>(T);
pub struct Rval<T>(T);
impl<T> Into<Rval<Self>> for Lval<T> {
fn into(self) -> Rval<Self> {
Rval(self)
}
}
fn main() {}

View file

@ -71,5 +71,18 @@ LL ~ fn from(val: Vec<T>) -> Self {
LL ~ FromOverInto(val)
|
error: aborting due to 5 previous errors
error: an implementation of `From` is preferred since it gives you `Into<_>` for free where the reverse isn't true
--> $DIR/from_over_into.rs:95:1
|
LL | impl<T> Into<Rval<Self>> for Lval<T> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: replace the `Into` implementation with `From<Lval<T>>`
|
LL ~ impl<T> From<Lval<T>> for Rval<Lval<T>> {
LL ~ fn from(val: Lval<T>) -> Self {
LL ~ Rval(val)
|
error: aborting due to 6 previous errors