@@ -163,7 +163,8 @@ fn convert_to_from(
163
163
let PatKind :: Binding ( .., self_ident, None ) = input. pat . kind else { return None } ;
164
164
165
165
let from = snippet_opt ( cx, self_ty. span ) ?;
166
- let into = snippet_opt ( cx, target_ty. span ) ?;
166
+ // If Self is used, it refers to `self_ty`, which is now out `from` snippet
167
+ let into = replace_self ( & snippet_opt ( cx, target_ty. span ) ?, & from) ;
167
168
168
169
let mut suggestions = vec ! [
169
170
// impl Into<T> for U -> impl From<T> for U
@@ -212,3 +213,75 @@ fn convert_to_from(
212
213
213
214
Some ( suggestions)
214
215
}
216
+
217
+ fn replace_self ( input : & str , replace_with : & str ) -> String {
218
+ const SELF : & str = "Self" ;
219
+ let mut chunks = input. split ( SELF ) . peekable ( ) ;
220
+ if let Some ( first) = chunks. next ( ) {
221
+ let mut last_ended_with_break = false ;
222
+ // Heuristic, we're making a guess that the expansion probably doesn't exceed `input.len() * 2`
223
+ let mut output = String :: with_capacity ( input. len ( ) * 2 ) ;
224
+ if first. is_empty ( ) || first. ends_with ( word_break) {
225
+ last_ended_with_break = true ;
226
+ }
227
+ output. push_str ( first) ;
228
+ while let Some ( val) = chunks. next ( ) {
229
+ let is_last = chunks. peek ( ) . is_none ( ) ;
230
+ if last_ended_with_break && is_last && val. is_empty ( ) {
231
+ output. push_str ( replace_with) ;
232
+ break ;
233
+ }
234
+ let this_starts_with_break = val. starts_with ( word_break) ;
235
+ let this_ends_with_break = val. ends_with ( word_break) ;
236
+ if this_starts_with_break && last_ended_with_break {
237
+ output. push_str ( replace_with) ;
238
+ } else {
239
+ output. push_str ( SELF ) ;
240
+ }
241
+ output. push_str ( val) ;
242
+ last_ended_with_break = this_ends_with_break;
243
+ }
244
+ output
245
+ } else {
246
+ input. to_string ( )
247
+ }
248
+ }
249
+
250
+ #[ inline]
251
+ fn word_break ( ch : char ) -> bool {
252
+ !ch. is_alphanumeric ( )
253
+ }
254
+
255
+ #[ cfg( test) ]
256
+ mod tests {
257
+ use crate :: from_over_into:: replace_self;
258
+
259
+ #[ test]
260
+ fn replace_doesnt_touch_coincidental_self ( ) {
261
+ let input = "impl Into<SelfType> for String {" ;
262
+ assert_eq ! ( input, & replace_self( input, "T" ) ) ;
263
+ }
264
+
265
+ #[ test]
266
+ fn replace_replaces_self ( ) {
267
+ let input = "impl Into<Self> for String {" ;
268
+ assert_eq ! ( "impl Into<String> for String {" , & replace_self( input, "String" ) ) ;
269
+ }
270
+ #[ test]
271
+ fn replace_replaces_self_many ( ) {
272
+ let input = "impl Into<Self<Self<SelfSelfSelfSelf>>> for Self {" ;
273
+ assert_eq ! (
274
+ "impl Into<String<String<SelfSelfSelfSelf>>> for String {" ,
275
+ & replace_self( input, "String" )
276
+ ) ;
277
+ }
278
+
279
+ #[ test]
280
+ fn replace_replaces_self_many_starts_ends_self ( ) {
281
+ let input = "Self impl Into<Self<Self<SelfSelf>>> for Self" ;
282
+ assert_eq ! (
283
+ "String impl Into<String<String<SelfSelf>>> for String" ,
284
+ & replace_self( input, "String" )
285
+ ) ;
286
+ }
287
+ }
0 commit comments