diff options
Diffstat (limited to 'crates/ide/src/rename.rs')
-rw-r--r-- | crates/ide/src/rename.rs | 1790 |
1 files changed, 1790 insertions, 0 deletions
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs new file mode 100644 index 000000000..8096dfa0e --- /dev/null +++ b/crates/ide/src/rename.rs | |||
@@ -0,0 +1,1790 @@ | |||
1 | //! Renaming functionality. | ||
2 | //! | ||
3 | //! This is mostly front-end for [`ide_db::rename`], but it also includes the | ||
4 | //! tests. This module also implements a couple of magic tricks, like renaming | ||
5 | //! `self` and to `self` (to switch between associated function and method). | ||
6 | use hir::{AsAssocItem, InFile, Semantics}; | ||
7 | use ide_db::{ | ||
8 | base_db::FileId, | ||
9 | defs::{Definition, NameClass, NameRefClass}, | ||
10 | rename::{bail, format_err, source_edit_from_references, IdentifierKind}, | ||
11 | RootDatabase, | ||
12 | }; | ||
13 | use stdx::never; | ||
14 | use syntax::{ast, AstNode, SyntaxNode}; | ||
15 | |||
16 | use text_edit::TextEdit; | ||
17 | |||
18 | use crate::{FilePosition, RangeInfo, SourceChange}; | ||
19 | |||
20 | pub use ide_db::rename::RenameError; | ||
21 | |||
22 | type RenameResult<T> = Result<T, RenameError>; | ||
23 | |||
24 | /// Prepares a rename. The sole job of this function is to return the TextRange of the thing that is | ||
25 | /// being targeted for a rename. | ||
26 | pub(crate) fn prepare_rename( | ||
27 | db: &RootDatabase, | ||
28 | position: FilePosition, | ||
29 | ) -> RenameResult<RangeInfo<()>> { | ||
30 | let sema = Semantics::new(db); | ||
31 | let source_file = sema.parse(position.file_id); | ||
32 | let syntax = source_file.syntax(); | ||
33 | |||
34 | let def = find_definition(&sema, syntax, position)?; | ||
35 | let frange = def | ||
36 | .range_for_rename(&sema) | ||
37 | .ok_or_else(|| format_err!("No references found at position"))?; | ||
38 | Ok(RangeInfo::new(frange.range, ())) | ||
39 | } | ||
40 | |||
41 | // Feature: Rename | ||
42 | // | ||
43 | // Renames the item below the cursor and all of its references | ||
44 | // | ||
45 | // |=== | ||
46 | // | Editor | Shortcut | ||
47 | // | ||
48 | // | VS Code | kbd:[F2] | ||
49 | // |=== | ||
50 | // | ||
51 | // image::https://user-images.githubusercontent.com/48062697/113065582-055aae80-91b1-11eb-8ade-2b58e6d81883.gif[] | ||
52 | pub(crate) fn rename( | ||
53 | db: &RootDatabase, | ||
54 | position: FilePosition, | ||
55 | new_name: &str, | ||
56 | ) -> RenameResult<SourceChange> { | ||
57 | let sema = Semantics::new(db); | ||
58 | rename_with_semantics(&sema, position, new_name) | ||
59 | } | ||
60 | |||
61 | pub(crate) fn rename_with_semantics( | ||
62 | sema: &Semantics<RootDatabase>, | ||
63 | position: FilePosition, | ||
64 | new_name: &str, | ||
65 | ) -> RenameResult<SourceChange> { | ||
66 | let source_file = sema.parse(position.file_id); | ||
67 | let syntax = source_file.syntax(); | ||
68 | |||
69 | let def = find_definition(sema, syntax, position)?; | ||
70 | |||
71 | if let Definition::Local(local) = def { | ||
72 | if let Some(self_param) = local.as_self_param(sema.db) { | ||
73 | cov_mark::hit!(rename_self_to_param); | ||
74 | return rename_self_to_param(sema, local, self_param, new_name); | ||
75 | } | ||
76 | if new_name == "self" { | ||
77 | cov_mark::hit!(rename_to_self); | ||
78 | return rename_to_self(sema, local); | ||
79 | } | ||
80 | } | ||
81 | |||
82 | def.rename(sema, new_name) | ||
83 | } | ||
84 | |||
85 | /// Called by the client when it is about to rename a file. | ||
86 | pub(crate) fn will_rename_file( | ||
87 | db: &RootDatabase, | ||
88 | file_id: FileId, | ||
89 | new_name_stem: &str, | ||
90 | ) -> Option<SourceChange> { | ||
91 | let sema = Semantics::new(db); | ||
92 | let module = sema.to_module_def(file_id)?; | ||
93 | let def = Definition::ModuleDef(module.into()); | ||
94 | let mut change = def.rename(&sema, new_name_stem).ok()?; | ||
95 | change.file_system_edits.clear(); | ||
96 | Some(change) | ||
97 | } | ||
98 | |||
99 | fn find_definition( | ||
100 | sema: &Semantics<RootDatabase>, | ||
101 | syntax: &SyntaxNode, | ||
102 | position: FilePosition, | ||
103 | ) -> RenameResult<Definition> { | ||
104 | match sema | ||
105 | .find_node_at_offset_with_descend(syntax, position.offset) | ||
106 | .ok_or_else(|| format_err!("No references found at position"))? | ||
107 | { | ||
108 | // renaming aliases would rename the item being aliased as the HIR doesn't track aliases yet | ||
109 | ast::NameLike::Name(name) | ||
110 | if name.syntax().parent().map_or(false, |it| ast::Rename::can_cast(it.kind())) => | ||
111 | { | ||
112 | bail!("Renaming aliases is currently unsupported") | ||
113 | } | ||
114 | ast::NameLike::Name(name) => { | ||
115 | NameClass::classify(sema, &name).map(|class| class.referenced_or_defined(sema.db)) | ||
116 | } | ||
117 | ast::NameLike::NameRef(name_ref) => { | ||
118 | if let Some(def) = | ||
119 | NameRefClass::classify(sema, &name_ref).map(|class| class.referenced(sema.db)) | ||
120 | { | ||
121 | // if the name differs from the definitions name it has to be an alias | ||
122 | if def.name(sema.db).map_or(false, |it| it.to_string() != name_ref.text()) { | ||
123 | bail!("Renaming aliases is currently unsupported"); | ||
124 | } | ||
125 | Some(def) | ||
126 | } else { | ||
127 | None | ||
128 | } | ||
129 | } | ||
130 | ast::NameLike::Lifetime(lifetime) => NameRefClass::classify_lifetime(sema, &lifetime) | ||
131 | .map(|class| NameRefClass::referenced(class, sema.db)) | ||
132 | .or_else(|| { | ||
133 | NameClass::classify_lifetime(sema, &lifetime) | ||
134 | .map(|it| it.referenced_or_defined(sema.db)) | ||
135 | }), | ||
136 | } | ||
137 | .ok_or_else(|| format_err!("No references found at position")) | ||
138 | } | ||
139 | |||
140 | fn rename_to_self(sema: &Semantics<RootDatabase>, local: hir::Local) -> RenameResult<SourceChange> { | ||
141 | if never!(local.is_self(sema.db)) { | ||
142 | bail!("rename_to_self invoked on self"); | ||
143 | } | ||
144 | |||
145 | let fn_def = match local.parent(sema.db) { | ||
146 | hir::DefWithBody::Function(func) => func, | ||
147 | _ => bail!("Cannot rename local to self outside of function"), | ||
148 | }; | ||
149 | |||
150 | if let Some(_) = fn_def.self_param(sema.db) { | ||
151 | bail!("Method already has a self parameter"); | ||
152 | } | ||
153 | |||
154 | let params = fn_def.assoc_fn_params(sema.db); | ||
155 | let first_param = params | ||
156 | .first() | ||
157 | .ok_or_else(|| format_err!("Cannot rename local to self unless it is a parameter"))?; | ||
158 | if first_param.as_local(sema.db) != local { | ||
159 | bail!("Only the first parameter may be renamed to self"); | ||
160 | } | ||
161 | |||
162 | let assoc_item = fn_def | ||
163 | .as_assoc_item(sema.db) | ||
164 | .ok_or_else(|| format_err!("Cannot rename parameter to self for free function"))?; | ||
165 | let impl_ = match assoc_item.container(sema.db) { | ||
166 | hir::AssocItemContainer::Trait(_) => { | ||
167 | bail!("Cannot rename parameter to self for trait functions"); | ||
168 | } | ||
169 | hir::AssocItemContainer::Impl(impl_) => impl_, | ||
170 | }; | ||
171 | let first_param_ty = first_param.ty(); | ||
172 | let impl_ty = impl_.self_ty(sema.db); | ||
173 | let (ty, self_param) = if impl_ty.remove_ref().is_some() { | ||
174 | // if the impl is a ref to the type we can just match the `&T` with self directly | ||
175 | (first_param_ty.clone(), "self") | ||
176 | } else { | ||
177 | first_param_ty.remove_ref().map_or((first_param_ty.clone(), "self"), |ty| { | ||
178 | (ty, if first_param_ty.is_mutable_reference() { "&mut self" } else { "&self" }) | ||
179 | }) | ||
180 | }; | ||
181 | |||
182 | if ty != impl_ty { | ||
183 | bail!("Parameter type differs from impl block type"); | ||
184 | } | ||
185 | |||
186 | let InFile { file_id, value: param_source } = | ||
187 | first_param.source(sema.db).ok_or_else(|| format_err!("No source for parameter found"))?; | ||
188 | |||
189 | let def = Definition::Local(local); | ||
190 | let usages = def.usages(sema).all(); | ||
191 | let mut source_change = SourceChange::default(); | ||
192 | source_change.extend(usages.iter().map(|(&file_id, references)| { | ||
193 | (file_id, source_edit_from_references(references, def, "self")) | ||
194 | })); | ||
195 | source_change.insert_source_edit( | ||
196 | file_id.original_file(sema.db), | ||
197 | TextEdit::replace(param_source.syntax().text_range(), String::from(self_param)), | ||
198 | ); | ||
199 | Ok(source_change) | ||
200 | } | ||
201 | |||
202 | fn rename_self_to_param( | ||
203 | sema: &Semantics<RootDatabase>, | ||
204 | local: hir::Local, | ||
205 | self_param: hir::SelfParam, | ||
206 | new_name: &str, | ||
207 | ) -> RenameResult<SourceChange> { | ||
208 | if new_name == "self" { | ||
209 | // Let's do nothing rather than complain. | ||
210 | cov_mark::hit!(rename_self_to_self); | ||
211 | return Ok(SourceChange::default()); | ||
212 | } | ||
213 | |||
214 | let identifier_kind = IdentifierKind::classify(new_name)?; | ||
215 | |||
216 | let InFile { file_id, value: self_param } = | ||
217 | self_param.source(sema.db).ok_or_else(|| format_err!("cannot find function source"))?; | ||
218 | |||
219 | let def = Definition::Local(local); | ||
220 | let usages = def.usages(sema).all(); | ||
221 | let edit = text_edit_from_self_param(&self_param, new_name) | ||
222 | .ok_or_else(|| format_err!("No target type found"))?; | ||
223 | if usages.len() > 1 && identifier_kind == IdentifierKind::Underscore { | ||
224 | bail!("Cannot rename reference to `_` as it is being referenced multiple times"); | ||
225 | } | ||
226 | let mut source_change = SourceChange::default(); | ||
227 | source_change.insert_source_edit(file_id.original_file(sema.db), edit); | ||
228 | source_change.extend(usages.iter().map(|(&file_id, references)| { | ||
229 | (file_id, source_edit_from_references(references, def, new_name)) | ||
230 | })); | ||
231 | Ok(source_change) | ||
232 | } | ||
233 | |||
234 | fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: &str) -> Option<TextEdit> { | ||
235 | fn target_type_name(impl_def: &ast::Impl) -> Option<String> { | ||
236 | if let Some(ast::Type::PathType(p)) = impl_def.self_ty() { | ||
237 | return Some(p.path()?.segment()?.name_ref()?.text().to_string()); | ||
238 | } | ||
239 | None | ||
240 | } | ||
241 | |||
242 | let impl_def = self_param.syntax().ancestors().find_map(ast::Impl::cast)?; | ||
243 | let type_name = target_type_name(&impl_def)?; | ||
244 | |||
245 | let mut replacement_text = String::from(new_name); | ||
246 | replacement_text.push_str(": "); | ||
247 | match (self_param.amp_token(), self_param.mut_token()) { | ||
248 | (Some(_), None) => replacement_text.push('&'), | ||
249 | (Some(_), Some(_)) => replacement_text.push_str("&mut "), | ||
250 | (_, _) => (), | ||
251 | }; | ||
252 | replacement_text.push_str(type_name.as_str()); | ||
253 | |||
254 | Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text)) | ||
255 | } | ||
256 | |||
257 | #[cfg(test)] | ||
258 | mod tests { | ||
259 | use expect_test::{expect, Expect}; | ||
260 | use stdx::trim_indent; | ||
261 | use test_utils::assert_eq_text; | ||
262 | use text_edit::TextEdit; | ||
263 | |||
264 | use crate::{fixture, FileId}; | ||
265 | |||
266 | use super::{RangeInfo, RenameError}; | ||
267 | |||
268 | fn check(new_name: &str, ra_fixture_before: &str, ra_fixture_after: &str) { | ||
269 | let ra_fixture_after = &trim_indent(ra_fixture_after); | ||
270 | let (analysis, position) = fixture::position(ra_fixture_before); | ||
271 | let rename_result = analysis | ||
272 | .rename(position, new_name) | ||
273 | .unwrap_or_else(|err| panic!("Rename to '{}' was cancelled: {}", new_name, err)); | ||
274 | match rename_result { | ||
275 | Ok(source_change) => { | ||
276 | let mut text_edit_builder = TextEdit::builder(); | ||
277 | let mut file_id: Option<FileId> = None; | ||
278 | for edit in source_change.source_file_edits { | ||
279 | file_id = Some(edit.0); | ||
280 | for indel in edit.1.into_iter() { | ||
281 | text_edit_builder.replace(indel.delete, indel.insert); | ||
282 | } | ||
283 | } | ||
284 | if let Some(file_id) = file_id { | ||
285 | let mut result = analysis.file_text(file_id).unwrap().to_string(); | ||
286 | text_edit_builder.finish().apply(&mut result); | ||
287 | assert_eq_text!(ra_fixture_after, &*result); | ||
288 | } | ||
289 | } | ||
290 | Err(err) => { | ||
291 | if ra_fixture_after.starts_with("error:") { | ||
292 | let error_message = ra_fixture_after | ||
293 | .chars() | ||
294 | .into_iter() | ||
295 | .skip("error:".len()) | ||
296 | .collect::<String>(); | ||
297 | assert_eq!(error_message.trim(), err.to_string()); | ||
298 | return; | ||
299 | } else { | ||
300 | panic!("Rename to '{}' failed unexpectedly: {}", new_name, err) | ||
301 | } | ||
302 | } | ||
303 | }; | ||
304 | } | ||
305 | |||
306 | fn check_expect(new_name: &str, ra_fixture: &str, expect: Expect) { | ||
307 | let (analysis, position) = fixture::position(ra_fixture); | ||
308 | let source_change = | ||
309 | analysis.rename(position, new_name).unwrap().expect("Expect returned a RenameError"); | ||
310 | expect.assert_debug_eq(&source_change) | ||
311 | } | ||
312 | |||
313 | fn check_prepare(ra_fixture: &str, expect: Expect) { | ||
314 | let (analysis, position) = fixture::position(ra_fixture); | ||
315 | let result = analysis | ||
316 | .prepare_rename(position) | ||
317 | .unwrap_or_else(|err| panic!("PrepareRename was cancelled: {}", err)); | ||
318 | match result { | ||
319 | Ok(RangeInfo { range, info: () }) => { | ||
320 | let source = analysis.file_text(position.file_id).unwrap(); | ||
321 | expect.assert_eq(&format!("{:?}: {}", range, &source[range])) | ||
322 | } | ||
323 | Err(RenameError(err)) => expect.assert_eq(&err), | ||
324 | }; | ||
325 | } | ||
326 | |||
327 | #[test] | ||
328 | fn test_prepare_rename_namelikes() { | ||
329 | check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]); | ||
330 | check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"8..17: 'lifetime"#]]); | ||
331 | check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"3..7: name"#]]); | ||
332 | } | ||
333 | |||
334 | #[test] | ||
335 | fn test_prepare_rename_in_macro() { | ||
336 | check_prepare( | ||
337 | r"macro_rules! foo { | ||
338 | ($ident:ident) => { | ||
339 | pub struct $ident; | ||
340 | } | ||
341 | } | ||
342 | foo!(Foo$0);", | ||
343 | expect![[r#"83..86: Foo"#]], | ||
344 | ); | ||
345 | } | ||
346 | |||
347 | #[test] | ||
348 | fn test_prepare_rename_keyword() { | ||
349 | check_prepare(r"struct$0 Foo;", expect![[r#"No references found at position"#]]); | ||
350 | } | ||
351 | |||
352 | #[test] | ||
353 | fn test_prepare_rename_tuple_field() { | ||
354 | check_prepare( | ||
355 | r#" | ||
356 | struct Foo(i32); | ||
357 | |||
358 | fn baz() { | ||
359 | let mut x = Foo(4); | ||
360 | x.0$0 = 5; | ||
361 | } | ||
362 | "#, | ||
363 | expect![[r#"No references found at position"#]], | ||
364 | ); | ||
365 | } | ||
366 | |||
367 | #[test] | ||
368 | fn test_prepare_rename_builtin() { | ||
369 | check_prepare( | ||
370 | r#" | ||
371 | fn foo() { | ||
372 | let x: i32$0 = 0; | ||
373 | } | ||
374 | "#, | ||
375 | expect![[r#"No references found at position"#]], | ||
376 | ); | ||
377 | } | ||
378 | |||
379 | #[test] | ||
380 | fn test_prepare_rename_self() { | ||
381 | check_prepare( | ||
382 | r#" | ||
383 | struct Foo {} | ||
384 | |||
385 | impl Foo { | ||
386 | fn foo(self) -> Self$0 { | ||
387 | self | ||
388 | } | ||
389 | } | ||
390 | "#, | ||
391 | expect![[r#"No references found at position"#]], | ||
392 | ); | ||
393 | } | ||
394 | |||
395 | #[test] | ||
396 | fn test_rename_to_underscore() { | ||
397 | check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#); | ||
398 | } | ||
399 | |||
400 | #[test] | ||
401 | fn test_rename_to_raw_identifier() { | ||
402 | check("r#fn", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let r#fn = 1; }"#); | ||
403 | } | ||
404 | |||
405 | #[test] | ||
406 | fn test_rename_to_invalid_identifier1() { | ||
407 | check( | ||
408 | "invalid!", | ||
409 | r#"fn main() { let i$0 = 1; }"#, | ||
410 | "error: Invalid name `invalid!`: not an identifier", | ||
411 | ); | ||
412 | } | ||
413 | |||
414 | #[test] | ||
415 | fn test_rename_to_invalid_identifier2() { | ||
416 | check( | ||
417 | "multiple tokens", | ||
418 | r#"fn main() { let i$0 = 1; }"#, | ||
419 | "error: Invalid name `multiple tokens`: not an identifier", | ||
420 | ); | ||
421 | } | ||
422 | |||
423 | #[test] | ||
424 | fn test_rename_to_invalid_identifier3() { | ||
425 | check( | ||
426 | "let", | ||
427 | r#"fn main() { let i$0 = 1; }"#, | ||
428 | "error: Invalid name `let`: not an identifier", | ||
429 | ); | ||
430 | } | ||
431 | |||
432 | #[test] | ||
433 | fn test_rename_to_invalid_identifier_lifetime() { | ||
434 | cov_mark::check!(rename_not_an_ident_ref); | ||
435 | check( | ||
436 | "'foo", | ||
437 | r#"fn main() { let i$0 = 1; }"#, | ||
438 | "error: Invalid name `'foo`: not an identifier", | ||
439 | ); | ||
440 | } | ||
441 | |||
442 | #[test] | ||
443 | fn test_rename_to_invalid_identifier_lifetime2() { | ||
444 | cov_mark::check!(rename_not_a_lifetime_ident_ref); | ||
445 | check( | ||
446 | "foo", | ||
447 | r#"fn main<'a>(_: &'a$0 ()) {}"#, | ||
448 | "error: Invalid name `foo`: not a lifetime identifier", | ||
449 | ); | ||
450 | } | ||
451 | |||
452 | #[test] | ||
453 | fn test_rename_to_underscore_invalid() { | ||
454 | cov_mark::check!(rename_underscore_multiple); | ||
455 | check( | ||
456 | "_", | ||
457 | r#"fn main(foo$0: ()) {foo;}"#, | ||
458 | "error: Cannot rename reference to `_` as it is being referenced multiple times", | ||
459 | ); | ||
460 | } | ||
461 | |||
462 | #[test] | ||
463 | fn test_rename_mod_invalid() { | ||
464 | check( | ||
465 | "'foo", | ||
466 | r#"mod foo$0 {}"#, | ||
467 | "error: Invalid name `'foo`: cannot rename module to 'foo", | ||
468 | ); | ||
469 | } | ||
470 | |||
471 | #[test] | ||
472 | fn test_rename_for_local() { | ||
473 | check( | ||
474 | "k", | ||
475 | r#" | ||
476 | fn main() { | ||
477 | let mut i = 1; | ||
478 | let j = 1; | ||
479 | i = i$0 + j; | ||
480 | |||
481 | { i = 0; } | ||
482 | |||
483 | i = 5; | ||
484 | } | ||
485 | "#, | ||
486 | r#" | ||
487 | fn main() { | ||
488 | let mut k = 1; | ||
489 | let j = 1; | ||
490 | k = k + j; | ||
491 | |||
492 | { k = 0; } | ||
493 | |||
494 | k = 5; | ||
495 | } | ||
496 | "#, | ||
497 | ); | ||
498 | } | ||
499 | |||
500 | #[test] | ||
501 | fn test_rename_unresolved_reference() { | ||
502 | check( | ||
503 | "new_name", | ||
504 | r#"fn main() { let _ = unresolved_ref$0; }"#, | ||
505 | "error: No references found at position", | ||
506 | ); | ||
507 | } | ||
508 | |||
509 | #[test] | ||
510 | fn test_rename_for_macro_args() { | ||
511 | check( | ||
512 | "b", | ||
513 | r#" | ||
514 | macro_rules! foo {($i:ident) => {$i} } | ||
515 | fn main() { | ||
516 | let a$0 = "test"; | ||
517 | foo!(a); | ||
518 | } | ||
519 | "#, | ||
520 | r#" | ||
521 | macro_rules! foo {($i:ident) => {$i} } | ||
522 | fn main() { | ||
523 | let b = "test"; | ||
524 | foo!(b); | ||
525 | } | ||
526 | "#, | ||
527 | ); | ||
528 | } | ||
529 | |||
530 | #[test] | ||
531 | fn test_rename_for_macro_args_rev() { | ||
532 | check( | ||
533 | "b", | ||
534 | r#" | ||
535 | macro_rules! foo {($i:ident) => {$i} } | ||
536 | fn main() { | ||
537 | let a = "test"; | ||
538 | foo!(a$0); | ||
539 | } | ||
540 | "#, | ||
541 | r#" | ||
542 | macro_rules! foo {($i:ident) => {$i} } | ||
543 | fn main() { | ||
544 | let b = "test"; | ||
545 | foo!(b); | ||
546 | } | ||
547 | "#, | ||
548 | ); | ||
549 | } | ||
550 | |||
551 | #[test] | ||
552 | fn test_rename_for_macro_define_fn() { | ||
553 | check( | ||
554 | "bar", | ||
555 | r#" | ||
556 | macro_rules! define_fn {($id:ident) => { fn $id{} }} | ||
557 | define_fn!(foo); | ||
558 | fn main() { | ||
559 | fo$0o(); | ||
560 | } | ||
561 | "#, | ||
562 | r#" | ||
563 | macro_rules! define_fn {($id:ident) => { fn $id{} }} | ||
564 | define_fn!(bar); | ||
565 | fn main() { | ||
566 | bar(); | ||
567 | } | ||
568 | "#, | ||
569 | ); | ||
570 | } | ||
571 | |||
572 | #[test] | ||
573 | fn test_rename_for_macro_define_fn_rev() { | ||
574 | check( | ||
575 | "bar", | ||
576 | r#" | ||
577 | macro_rules! define_fn {($id:ident) => { fn $id{} }} | ||
578 | define_fn!(fo$0o); | ||
579 | fn main() { | ||
580 | foo(); | ||
581 | } | ||
582 | "#, | ||
583 | r#" | ||
584 | macro_rules! define_fn {($id:ident) => { fn $id{} }} | ||
585 | define_fn!(bar); | ||
586 | fn main() { | ||
587 | bar(); | ||
588 | } | ||
589 | "#, | ||
590 | ); | ||
591 | } | ||
592 | |||
593 | #[test] | ||
594 | fn test_rename_for_param_inside() { | ||
595 | check("j", r#"fn foo(i : u32) -> u32 { i$0 }"#, r#"fn foo(j : u32) -> u32 { j }"#); | ||
596 | } | ||
597 | |||
598 | #[test] | ||
599 | fn test_rename_refs_for_fn_param() { | ||
600 | check("j", r#"fn foo(i$0 : u32) -> u32 { i }"#, r#"fn foo(j : u32) -> u32 { j }"#); | ||
601 | } | ||
602 | |||
603 | #[test] | ||
604 | fn test_rename_for_mut_param() { | ||
605 | check("j", r#"fn foo(mut i$0 : u32) -> u32 { i }"#, r#"fn foo(mut j : u32) -> u32 { j }"#); | ||
606 | } | ||
607 | |||
608 | #[test] | ||
609 | fn test_rename_struct_field() { | ||
610 | check( | ||
611 | "j", | ||
612 | r#" | ||
613 | struct Foo { i$0: i32 } | ||
614 | |||
615 | impl Foo { | ||
616 | fn new(i: i32) -> Self { | ||
617 | Self { i: i } | ||
618 | } | ||
619 | } | ||
620 | "#, | ||
621 | r#" | ||
622 | struct Foo { j: i32 } | ||
623 | |||
624 | impl Foo { | ||
625 | fn new(i: i32) -> Self { | ||
626 | Self { j: i } | ||
627 | } | ||
628 | } | ||
629 | "#, | ||
630 | ); | ||
631 | } | ||
632 | |||
633 | #[test] | ||
634 | fn test_rename_field_in_field_shorthand() { | ||
635 | cov_mark::check!(test_rename_field_in_field_shorthand); | ||
636 | check( | ||
637 | "j", | ||
638 | r#" | ||
639 | struct Foo { i$0: i32 } | ||
640 | |||
641 | impl Foo { | ||
642 | fn new(i: i32) -> Self { | ||
643 | Self { i } | ||
644 | } | ||
645 | } | ||
646 | "#, | ||
647 | r#" | ||
648 | struct Foo { j: i32 } | ||
649 | |||
650 | impl Foo { | ||
651 | fn new(i: i32) -> Self { | ||
652 | Self { j: i } | ||
653 | } | ||
654 | } | ||
655 | "#, | ||
656 | ); | ||
657 | } | ||
658 | |||
659 | #[test] | ||
660 | fn test_rename_local_in_field_shorthand() { | ||
661 | cov_mark::check!(test_rename_local_in_field_shorthand); | ||
662 | check( | ||
663 | "j", | ||
664 | r#" | ||
665 | struct Foo { i: i32 } | ||
666 | |||
667 | impl Foo { | ||
668 | fn new(i$0: i32) -> Self { | ||
669 | Self { i } | ||
670 | } | ||
671 | } | ||
672 | "#, | ||
673 | r#" | ||
674 | struct Foo { i: i32 } | ||
675 | |||
676 | impl Foo { | ||
677 | fn new(j: i32) -> Self { | ||
678 | Self { i: j } | ||
679 | } | ||
680 | } | ||
681 | "#, | ||
682 | ); | ||
683 | } | ||
684 | |||
685 | #[test] | ||
686 | fn test_field_shorthand_correct_struct() { | ||
687 | check( | ||
688 | "j", | ||
689 | r#" | ||
690 | struct Foo { i$0: i32 } | ||
691 | struct Bar { i: i32 } | ||
692 | |||
693 | impl Bar { | ||
694 | fn new(i: i32) -> Self { | ||
695 | Self { i } | ||
696 | } | ||
697 | } | ||
698 | "#, | ||
699 | r#" | ||
700 | struct Foo { j: i32 } | ||
701 | struct Bar { i: i32 } | ||
702 | |||
703 | impl Bar { | ||
704 | fn new(i: i32) -> Self { | ||
705 | Self { i } | ||
706 | } | ||
707 | } | ||
708 | "#, | ||
709 | ); | ||
710 | } | ||
711 | |||
712 | #[test] | ||
713 | fn test_shadow_local_for_struct_shorthand() { | ||
714 | check( | ||
715 | "j", | ||
716 | r#" | ||
717 | struct Foo { i: i32 } | ||
718 | |||
719 | fn baz(i$0: i32) -> Self { | ||
720 | let x = Foo { i }; | ||
721 | { | ||
722 | let i = 0; | ||
723 | Foo { i } | ||
724 | } | ||
725 | } | ||
726 | "#, | ||
727 | r#" | ||
728 | struct Foo { i: i32 } | ||
729 | |||
730 | fn baz(j: i32) -> Self { | ||
731 | let x = Foo { i: j }; | ||
732 | { | ||
733 | let i = 0; | ||
734 | Foo { i } | ||
735 | } | ||
736 | } | ||
737 | "#, | ||
738 | ); | ||
739 | } | ||
740 | |||
741 | #[test] | ||
742 | fn test_rename_mod() { | ||
743 | check_expect( | ||
744 | "foo2", | ||
745 | r#" | ||
746 | //- /lib.rs | ||
747 | mod bar; | ||
748 | |||
749 | //- /bar.rs | ||
750 | mod foo$0; | ||
751 | |||
752 | //- /bar/foo.rs | ||
753 | // empty | ||
754 | "#, | ||
755 | expect![[r#" | ||
756 | SourceChange { | ||
757 | source_file_edits: { | ||
758 | FileId( | ||
759 | 1, | ||
760 | ): TextEdit { | ||
761 | indels: [ | ||
762 | Indel { | ||
763 | insert: "foo2", | ||
764 | delete: 4..7, | ||
765 | }, | ||
766 | ], | ||
767 | }, | ||
768 | }, | ||
769 | file_system_edits: [ | ||
770 | MoveFile { | ||
771 | src: FileId( | ||
772 | 2, | ||
773 | ), | ||
774 | dst: AnchoredPathBuf { | ||
775 | anchor: FileId( | ||
776 | 2, | ||
777 | ), | ||
778 | path: "foo2.rs", | ||
779 | }, | ||
780 | }, | ||
781 | ], | ||
782 | is_snippet: false, | ||
783 | } | ||
784 | "#]], | ||
785 | ); | ||
786 | } | ||
787 | |||
788 | #[test] | ||
789 | fn test_rename_mod_in_use_tree() { | ||
790 | check_expect( | ||
791 | "quux", | ||
792 | r#" | ||
793 | //- /main.rs | ||
794 | pub mod foo; | ||
795 | pub mod bar; | ||
796 | fn main() {} | ||
797 | |||
798 | //- /foo.rs | ||
799 | pub struct FooContent; | ||
800 | |||
801 | //- /bar.rs | ||
802 | use crate::foo$0::FooContent; | ||
803 | "#, | ||
804 | expect![[r#" | ||
805 | SourceChange { | ||
806 | source_file_edits: { | ||
807 | FileId( | ||
808 | 0, | ||
809 | ): TextEdit { | ||
810 | indels: [ | ||
811 | Indel { | ||
812 | insert: "quux", | ||
813 | delete: 8..11, | ||
814 | }, | ||
815 | ], | ||
816 | }, | ||
817 | FileId( | ||
818 | 2, | ||
819 | ): TextEdit { | ||
820 | indels: [ | ||
821 | Indel { | ||
822 | insert: "quux", | ||
823 | delete: 11..14, | ||
824 | }, | ||
825 | ], | ||
826 | }, | ||
827 | }, | ||
828 | file_system_edits: [ | ||
829 | MoveFile { | ||
830 | src: FileId( | ||
831 | 1, | ||
832 | ), | ||
833 | dst: AnchoredPathBuf { | ||
834 | anchor: FileId( | ||
835 | 1, | ||
836 | ), | ||
837 | path: "quux.rs", | ||
838 | }, | ||
839 | }, | ||
840 | ], | ||
841 | is_snippet: false, | ||
842 | } | ||
843 | "#]], | ||
844 | ); | ||
845 | } | ||
846 | |||
847 | #[test] | ||
848 | fn test_rename_mod_in_dir() { | ||
849 | check_expect( | ||
850 | "foo2", | ||
851 | r#" | ||
852 | //- /lib.rs | ||
853 | mod fo$0o; | ||
854 | //- /foo/mod.rs | ||
855 | // empty | ||
856 | "#, | ||
857 | expect![[r#" | ||
858 | SourceChange { | ||
859 | source_file_edits: { | ||
860 | FileId( | ||
861 | 0, | ||
862 | ): TextEdit { | ||
863 | indels: [ | ||
864 | Indel { | ||
865 | insert: "foo2", | ||
866 | delete: 4..7, | ||
867 | }, | ||
868 | ], | ||
869 | }, | ||
870 | }, | ||
871 | file_system_edits: [ | ||
872 | MoveFile { | ||
873 | src: FileId( | ||
874 | 1, | ||
875 | ), | ||
876 | dst: AnchoredPathBuf { | ||
877 | anchor: FileId( | ||
878 | 1, | ||
879 | ), | ||
880 | path: "../foo2/mod.rs", | ||
881 | }, | ||
882 | }, | ||
883 | ], | ||
884 | is_snippet: false, | ||
885 | } | ||
886 | "#]], | ||
887 | ); | ||
888 | } | ||
889 | |||
890 | #[test] | ||
891 | fn test_rename_unusually_nested_mod() { | ||
892 | check_expect( | ||
893 | "bar", | ||
894 | r#" | ||
895 | //- /lib.rs | ||
896 | mod outer { mod fo$0o; } | ||
897 | |||
898 | //- /outer/foo.rs | ||
899 | // empty | ||
900 | "#, | ||
901 | expect![[r#" | ||
902 | SourceChange { | ||
903 | source_file_edits: { | ||
904 | FileId( | ||
905 | 0, | ||
906 | ): TextEdit { | ||
907 | indels: [ | ||
908 | Indel { | ||
909 | insert: "bar", | ||
910 | delete: 16..19, | ||
911 | }, | ||
912 | ], | ||
913 | }, | ||
914 | }, | ||
915 | file_system_edits: [ | ||
916 | MoveFile { | ||
917 | src: FileId( | ||
918 | 1, | ||
919 | ), | ||
920 | dst: AnchoredPathBuf { | ||
921 | anchor: FileId( | ||
922 | 1, | ||
923 | ), | ||
924 | path: "bar.rs", | ||
925 | }, | ||
926 | }, | ||
927 | ], | ||
928 | is_snippet: false, | ||
929 | } | ||
930 | "#]], | ||
931 | ); | ||
932 | } | ||
933 | |||
934 | #[test] | ||
935 | fn test_module_rename_in_path() { | ||
936 | check( | ||
937 | "baz", | ||
938 | r#" | ||
939 | mod $0foo { pub fn bar() {} } | ||
940 | |||
941 | fn main() { foo::bar(); } | ||
942 | "#, | ||
943 | r#" | ||
944 | mod baz { pub fn bar() {} } | ||
945 | |||
946 | fn main() { baz::bar(); } | ||
947 | "#, | ||
948 | ); | ||
949 | } | ||
950 | |||
951 | #[test] | ||
952 | fn test_rename_mod_filename_and_path() { | ||
953 | check_expect( | ||
954 | "foo2", | ||
955 | r#" | ||
956 | //- /lib.rs | ||
957 | mod bar; | ||
958 | fn f() { | ||
959 | bar::foo::fun() | ||
960 | } | ||
961 | |||
962 | //- /bar.rs | ||
963 | pub mod foo$0; | ||
964 | |||
965 | //- /bar/foo.rs | ||
966 | // pub fn fun() {} | ||
967 | "#, | ||
968 | expect![[r#" | ||
969 | SourceChange { | ||
970 | source_file_edits: { | ||
971 | FileId( | ||
972 | 0, | ||
973 | ): TextEdit { | ||
974 | indels: [ | ||
975 | Indel { | ||
976 | insert: "foo2", | ||
977 | delete: 27..30, | ||
978 | }, | ||
979 | ], | ||
980 | }, | ||
981 | FileId( | ||
982 | 1, | ||
983 | ): TextEdit { | ||
984 | indels: [ | ||
985 | Indel { | ||
986 | insert: "foo2", | ||
987 | delete: 8..11, | ||
988 | }, | ||
989 | ], | ||
990 | }, | ||
991 | }, | ||
992 | file_system_edits: [ | ||
993 | MoveFile { | ||
994 | src: FileId( | ||
995 | 2, | ||
996 | ), | ||
997 | dst: AnchoredPathBuf { | ||
998 | anchor: FileId( | ||
999 | 2, | ||
1000 | ), | ||
1001 | path: "foo2.rs", | ||
1002 | }, | ||
1003 | }, | ||
1004 | ], | ||
1005 | is_snippet: false, | ||
1006 | } | ||
1007 | "#]], | ||
1008 | ); | ||
1009 | } | ||
1010 | |||
1011 | #[test] | ||
1012 | fn test_enum_variant_from_module_1() { | ||
1013 | cov_mark::check!(rename_non_local); | ||
1014 | check( | ||
1015 | "Baz", | ||
1016 | r#" | ||
1017 | mod foo { | ||
1018 | pub enum Foo { Bar$0 } | ||
1019 | } | ||
1020 | |||
1021 | fn func(f: foo::Foo) { | ||
1022 | match f { | ||
1023 | foo::Foo::Bar => {} | ||
1024 | } | ||
1025 | } | ||
1026 | "#, | ||
1027 | r#" | ||
1028 | mod foo { | ||
1029 | pub enum Foo { Baz } | ||
1030 | } | ||
1031 | |||
1032 | fn func(f: foo::Foo) { | ||
1033 | match f { | ||
1034 | foo::Foo::Baz => {} | ||
1035 | } | ||
1036 | } | ||
1037 | "#, | ||
1038 | ); | ||
1039 | } | ||
1040 | |||
1041 | #[test] | ||
1042 | fn test_enum_variant_from_module_2() { | ||
1043 | check( | ||
1044 | "baz", | ||
1045 | r#" | ||
1046 | mod foo { | ||
1047 | pub struct Foo { pub bar$0: uint } | ||
1048 | } | ||
1049 | |||
1050 | fn foo(f: foo::Foo) { | ||
1051 | let _ = f.bar; | ||
1052 | } | ||
1053 | "#, | ||
1054 | r#" | ||
1055 | mod foo { | ||
1056 | pub struct Foo { pub baz: uint } | ||
1057 | } | ||
1058 | |||
1059 | fn foo(f: foo::Foo) { | ||
1060 | let _ = f.baz; | ||
1061 | } | ||
1062 | "#, | ||
1063 | ); | ||
1064 | } | ||
1065 | |||
1066 | #[test] | ||
1067 | fn test_parameter_to_self() { | ||
1068 | cov_mark::check!(rename_to_self); | ||
1069 | check( | ||
1070 | "self", | ||
1071 | r#" | ||
1072 | struct Foo { i: i32 } | ||
1073 | |||
1074 | impl Foo { | ||
1075 | fn f(foo$0: &mut Foo) -> i32 { | ||
1076 | foo.i | ||
1077 | } | ||
1078 | } | ||
1079 | "#, | ||
1080 | r#" | ||
1081 | struct Foo { i: i32 } | ||
1082 | |||
1083 | impl Foo { | ||
1084 | fn f(&mut self) -> i32 { | ||
1085 | self.i | ||
1086 | } | ||
1087 | } | ||
1088 | "#, | ||
1089 | ); | ||
1090 | check( | ||
1091 | "self", | ||
1092 | r#" | ||
1093 | struct Foo { i: i32 } | ||
1094 | |||
1095 | impl Foo { | ||
1096 | fn f(foo$0: Foo) -> i32 { | ||
1097 | foo.i | ||
1098 | } | ||
1099 | } | ||
1100 | "#, | ||
1101 | r#" | ||
1102 | struct Foo { i: i32 } | ||
1103 | |||
1104 | impl Foo { | ||
1105 | fn f(self) -> i32 { | ||
1106 | self.i | ||
1107 | } | ||
1108 | } | ||
1109 | "#, | ||
1110 | ); | ||
1111 | } | ||
1112 | |||
1113 | #[test] | ||
1114 | fn test_parameter_to_self_error_no_impl() { | ||
1115 | check( | ||
1116 | "self", | ||
1117 | r#" | ||
1118 | struct Foo { i: i32 } | ||
1119 | |||
1120 | fn f(foo$0: &mut Foo) -> i32 { | ||
1121 | foo.i | ||
1122 | } | ||
1123 | "#, | ||
1124 | "error: Cannot rename parameter to self for free function", | ||
1125 | ); | ||
1126 | check( | ||
1127 | "self", | ||
1128 | r#" | ||
1129 | struct Foo { i: i32 } | ||
1130 | struct Bar; | ||
1131 | |||
1132 | impl Bar { | ||
1133 | fn f(foo$0: &mut Foo) -> i32 { | ||
1134 | foo.i | ||
1135 | } | ||
1136 | } | ||
1137 | "#, | ||
1138 | "error: Parameter type differs from impl block type", | ||
1139 | ); | ||
1140 | } | ||
1141 | |||
1142 | #[test] | ||
1143 | fn test_parameter_to_self_error_not_first() { | ||
1144 | check( | ||
1145 | "self", | ||
1146 | r#" | ||
1147 | struct Foo { i: i32 } | ||
1148 | impl Foo { | ||
1149 | fn f(x: (), foo$0: &mut Foo) -> i32 { | ||
1150 | foo.i | ||
1151 | } | ||
1152 | } | ||
1153 | "#, | ||
1154 | "error: Only the first parameter may be renamed to self", | ||
1155 | ); | ||
1156 | } | ||
1157 | |||
1158 | #[test] | ||
1159 | fn test_parameter_to_self_impl_ref() { | ||
1160 | check( | ||
1161 | "self", | ||
1162 | r#" | ||
1163 | struct Foo { i: i32 } | ||
1164 | impl &Foo { | ||
1165 | fn f(foo$0: &Foo) -> i32 { | ||
1166 | foo.i | ||
1167 | } | ||
1168 | } | ||
1169 | "#, | ||
1170 | r#" | ||
1171 | struct Foo { i: i32 } | ||
1172 | impl &Foo { | ||
1173 | fn f(self) -> i32 { | ||
1174 | self.i | ||
1175 | } | ||
1176 | } | ||
1177 | "#, | ||
1178 | ); | ||
1179 | } | ||
1180 | |||
1181 | #[test] | ||
1182 | fn test_self_to_parameter() { | ||
1183 | check( | ||
1184 | "foo", | ||
1185 | r#" | ||
1186 | struct Foo { i: i32 } | ||
1187 | |||
1188 | impl Foo { | ||
1189 | fn f(&mut $0self) -> i32 { | ||
1190 | self.i | ||
1191 | } | ||
1192 | } | ||
1193 | "#, | ||
1194 | r#" | ||
1195 | struct Foo { i: i32 } | ||
1196 | |||
1197 | impl Foo { | ||
1198 | fn f(foo: &mut Foo) -> i32 { | ||
1199 | foo.i | ||
1200 | } | ||
1201 | } | ||
1202 | "#, | ||
1203 | ); | ||
1204 | } | ||
1205 | |||
1206 | #[test] | ||
1207 | fn test_owned_self_to_parameter() { | ||
1208 | cov_mark::check!(rename_self_to_param); | ||
1209 | check( | ||
1210 | "foo", | ||
1211 | r#" | ||
1212 | struct Foo { i: i32 } | ||
1213 | |||
1214 | impl Foo { | ||
1215 | fn f($0self) -> i32 { | ||
1216 | self.i | ||
1217 | } | ||
1218 | } | ||
1219 | "#, | ||
1220 | r#" | ||
1221 | struct Foo { i: i32 } | ||
1222 | |||
1223 | impl Foo { | ||
1224 | fn f(foo: Foo) -> i32 { | ||
1225 | foo.i | ||
1226 | } | ||
1227 | } | ||
1228 | "#, | ||
1229 | ); | ||
1230 | } | ||
1231 | |||
1232 | #[test] | ||
1233 | fn test_self_in_path_to_parameter() { | ||
1234 | check( | ||
1235 | "foo", | ||
1236 | r#" | ||
1237 | struct Foo { i: i32 } | ||
1238 | |||
1239 | impl Foo { | ||
1240 | fn f(&self) -> i32 { | ||
1241 | let self_var = 1; | ||
1242 | self$0.i | ||
1243 | } | ||
1244 | } | ||
1245 | "#, | ||
1246 | r#" | ||
1247 | struct Foo { i: i32 } | ||
1248 | |||
1249 | impl Foo { | ||
1250 | fn f(foo: &Foo) -> i32 { | ||
1251 | let self_var = 1; | ||
1252 | foo.i | ||
1253 | } | ||
1254 | } | ||
1255 | "#, | ||
1256 | ); | ||
1257 | } | ||
1258 | |||
1259 | #[test] | ||
1260 | fn test_rename_field_put_init_shorthand() { | ||
1261 | cov_mark::check!(test_rename_field_put_init_shorthand); | ||
1262 | check( | ||
1263 | "bar", | ||
1264 | r#" | ||
1265 | struct Foo { i$0: i32 } | ||
1266 | |||
1267 | fn foo(bar: i32) -> Foo { | ||
1268 | Foo { i: bar } | ||
1269 | } | ||
1270 | "#, | ||
1271 | r#" | ||
1272 | struct Foo { bar: i32 } | ||
1273 | |||
1274 | fn foo(bar: i32) -> Foo { | ||
1275 | Foo { bar } | ||
1276 | } | ||
1277 | "#, | ||
1278 | ); | ||
1279 | } | ||
1280 | |||
1281 | #[test] | ||
1282 | fn test_rename_local_put_init_shorthand() { | ||
1283 | cov_mark::check!(test_rename_local_put_init_shorthand); | ||
1284 | check( | ||
1285 | "i", | ||
1286 | r#" | ||
1287 | struct Foo { i: i32 } | ||
1288 | |||
1289 | fn foo(bar$0: i32) -> Foo { | ||
1290 | Foo { i: bar } | ||
1291 | } | ||
1292 | "#, | ||
1293 | r#" | ||
1294 | struct Foo { i: i32 } | ||
1295 | |||
1296 | fn foo(i: i32) -> Foo { | ||
1297 | Foo { i } | ||
1298 | } | ||
1299 | "#, | ||
1300 | ); | ||
1301 | } | ||
1302 | |||
1303 | #[test] | ||
1304 | fn test_struct_field_pat_into_shorthand() { | ||
1305 | cov_mark::check!(test_rename_field_put_init_shorthand_pat); | ||
1306 | check( | ||
1307 | "baz", | ||
1308 | r#" | ||
1309 | struct Foo { i$0: i32 } | ||
1310 | |||
1311 | fn foo(foo: Foo) { | ||
1312 | let Foo { i: ref baz @ qux } = foo; | ||
1313 | let _ = qux; | ||
1314 | } | ||
1315 | "#, | ||
1316 | r#" | ||
1317 | struct Foo { baz: i32 } | ||
1318 | |||
1319 | fn foo(foo: Foo) { | ||
1320 | let Foo { ref baz @ qux } = foo; | ||
1321 | let _ = qux; | ||
1322 | } | ||
1323 | "#, | ||
1324 | ); | ||
1325 | } | ||
1326 | |||
1327 | #[test] | ||
1328 | fn test_rename_binding_in_destructure_pat() { | ||
1329 | let expected_fixture = r#" | ||
1330 | struct Foo { | ||
1331 | i: i32, | ||
1332 | } | ||
1333 | |||
1334 | fn foo(foo: Foo) { | ||
1335 | let Foo { i: bar } = foo; | ||
1336 | let _ = bar; | ||
1337 | } | ||
1338 | "#; | ||
1339 | check( | ||
1340 | "bar", | ||
1341 | r#" | ||
1342 | struct Foo { | ||
1343 | i: i32, | ||
1344 | } | ||
1345 | |||
1346 | fn foo(foo: Foo) { | ||
1347 | let Foo { i: b } = foo; | ||
1348 | let _ = b$0; | ||
1349 | } | ||
1350 | "#, | ||
1351 | expected_fixture, | ||
1352 | ); | ||
1353 | check( | ||
1354 | "bar", | ||
1355 | r#" | ||
1356 | struct Foo { | ||
1357 | i: i32, | ||
1358 | } | ||
1359 | |||
1360 | fn foo(foo: Foo) { | ||
1361 | let Foo { i } = foo; | ||
1362 | let _ = i$0; | ||
1363 | } | ||
1364 | "#, | ||
1365 | expected_fixture, | ||
1366 | ); | ||
1367 | } | ||
1368 | |||
1369 | #[test] | ||
1370 | fn test_rename_binding_in_destructure_param_pat() { | ||
1371 | check( | ||
1372 | "bar", | ||
1373 | r#" | ||
1374 | struct Foo { | ||
1375 | i: i32 | ||
1376 | } | ||
1377 | |||
1378 | fn foo(Foo { i }: foo) -> i32 { | ||
1379 | i$0 | ||
1380 | } | ||
1381 | "#, | ||
1382 | r#" | ||
1383 | struct Foo { | ||
1384 | i: i32 | ||
1385 | } | ||
1386 | |||
1387 | fn foo(Foo { i: bar }: foo) -> i32 { | ||
1388 | bar | ||
1389 | } | ||
1390 | "#, | ||
1391 | ) | ||
1392 | } | ||
1393 | |||
1394 | #[test] | ||
1395 | fn test_struct_field_complex_ident_pat() { | ||
1396 | check( | ||
1397 | "baz", | ||
1398 | r#" | ||
1399 | struct Foo { i$0: i32 } | ||
1400 | |||
1401 | fn foo(foo: Foo) { | ||
1402 | let Foo { ref i } = foo; | ||
1403 | } | ||
1404 | "#, | ||
1405 | r#" | ||
1406 | struct Foo { baz: i32 } | ||
1407 | |||
1408 | fn foo(foo: Foo) { | ||
1409 | let Foo { baz: ref i } = foo; | ||
1410 | } | ||
1411 | "#, | ||
1412 | ); | ||
1413 | } | ||
1414 | |||
1415 | #[test] | ||
1416 | fn test_rename_lifetimes() { | ||
1417 | cov_mark::check!(rename_lifetime); | ||
1418 | check( | ||
1419 | "'yeeee", | ||
1420 | r#" | ||
1421 | trait Foo<'a> { | ||
1422 | fn foo() -> &'a (); | ||
1423 | } | ||
1424 | impl<'a> Foo<'a> for &'a () { | ||
1425 | fn foo() -> &'a$0 () { | ||
1426 | unimplemented!() | ||
1427 | } | ||
1428 | } | ||
1429 | "#, | ||
1430 | r#" | ||
1431 | trait Foo<'a> { | ||
1432 | fn foo() -> &'a (); | ||
1433 | } | ||
1434 | impl<'yeeee> Foo<'yeeee> for &'yeeee () { | ||
1435 | fn foo() -> &'yeeee () { | ||
1436 | unimplemented!() | ||
1437 | } | ||
1438 | } | ||
1439 | "#, | ||
1440 | ) | ||
1441 | } | ||
1442 | |||
1443 | #[test] | ||
1444 | fn test_rename_bind_pat() { | ||
1445 | check( | ||
1446 | "new_name", | ||
1447 | r#" | ||
1448 | fn main() { | ||
1449 | enum CustomOption<T> { | ||
1450 | None, | ||
1451 | Some(T), | ||
1452 | } | ||
1453 | |||
1454 | let test_variable = CustomOption::Some(22); | ||
1455 | |||
1456 | match test_variable { | ||
1457 | CustomOption::Some(foo$0) if foo == 11 => {} | ||
1458 | _ => (), | ||
1459 | } | ||
1460 | }"#, | ||
1461 | r#" | ||
1462 | fn main() { | ||
1463 | enum CustomOption<T> { | ||
1464 | None, | ||
1465 | Some(T), | ||
1466 | } | ||
1467 | |||
1468 | let test_variable = CustomOption::Some(22); | ||
1469 | |||
1470 | match test_variable { | ||
1471 | CustomOption::Some(new_name) if new_name == 11 => {} | ||
1472 | _ => (), | ||
1473 | } | ||
1474 | }"#, | ||
1475 | ); | ||
1476 | } | ||
1477 | |||
1478 | #[test] | ||
1479 | fn test_rename_label() { | ||
1480 | check( | ||
1481 | "'foo", | ||
1482 | r#" | ||
1483 | fn foo<'a>() -> &'a () { | ||
1484 | 'a: { | ||
1485 | 'b: loop { | ||
1486 | break 'a$0; | ||
1487 | } | ||
1488 | } | ||
1489 | } | ||
1490 | "#, | ||
1491 | r#" | ||
1492 | fn foo<'a>() -> &'a () { | ||
1493 | 'foo: { | ||
1494 | 'b: loop { | ||
1495 | break 'foo; | ||
1496 | } | ||
1497 | } | ||
1498 | } | ||
1499 | "#, | ||
1500 | ) | ||
1501 | } | ||
1502 | |||
1503 | #[test] | ||
1504 | fn test_self_to_self() { | ||
1505 | cov_mark::check!(rename_self_to_self); | ||
1506 | check( | ||
1507 | "self", | ||
1508 | r#" | ||
1509 | struct Foo; | ||
1510 | impl Foo { | ||
1511 | fn foo(self$0) {} | ||
1512 | } | ||
1513 | "#, | ||
1514 | r#" | ||
1515 | struct Foo; | ||
1516 | impl Foo { | ||
1517 | fn foo(self) {} | ||
1518 | } | ||
1519 | "#, | ||
1520 | ) | ||
1521 | } | ||
1522 | |||
1523 | #[test] | ||
1524 | fn test_rename_field_in_pat_in_macro_doesnt_shorthand() { | ||
1525 | // ideally we would be able to make this emit a short hand, but I doubt this is easily possible | ||
1526 | check( | ||
1527 | "baz", | ||
1528 | r#" | ||
1529 | macro_rules! foo { | ||
1530 | ($pattern:pat) => { | ||
1531 | let $pattern = loop {}; | ||
1532 | }; | ||
1533 | } | ||
1534 | struct Foo { | ||
1535 | bar$0: u32, | ||
1536 | } | ||
1537 | fn foo() { | ||
1538 | foo!(Foo { bar: baz }); | ||
1539 | } | ||
1540 | "#, | ||
1541 | r#" | ||
1542 | macro_rules! foo { | ||
1543 | ($pattern:pat) => { | ||
1544 | let $pattern = loop {}; | ||
1545 | }; | ||
1546 | } | ||
1547 | struct Foo { | ||
1548 | baz: u32, | ||
1549 | } | ||
1550 | fn foo() { | ||
1551 | foo!(Foo { baz: baz }); | ||
1552 | } | ||
1553 | "#, | ||
1554 | ) | ||
1555 | } | ||
1556 | |||
1557 | #[test] | ||
1558 | fn test_rename_tuple_field() { | ||
1559 | check( | ||
1560 | "foo", | ||
1561 | r#" | ||
1562 | struct Foo(i32); | ||
1563 | |||
1564 | fn baz() { | ||
1565 | let mut x = Foo(4); | ||
1566 | x.0$0 = 5; | ||
1567 | } | ||
1568 | "#, | ||
1569 | "error: No identifier available to rename", | ||
1570 | ); | ||
1571 | } | ||
1572 | |||
1573 | #[test] | ||
1574 | fn test_rename_builtin() { | ||
1575 | check( | ||
1576 | "foo", | ||
1577 | r#" | ||
1578 | fn foo() { | ||
1579 | let x: i32$0 = 0; | ||
1580 | } | ||
1581 | "#, | ||
1582 | "error: Cannot rename builtin type", | ||
1583 | ); | ||
1584 | } | ||
1585 | |||
1586 | #[test] | ||
1587 | fn test_rename_self() { | ||
1588 | check( | ||
1589 | "foo", | ||
1590 | r#" | ||
1591 | struct Foo {} | ||
1592 | |||
1593 | impl Foo { | ||
1594 | fn foo(self) -> Self$0 { | ||
1595 | self | ||
1596 | } | ||
1597 | } | ||
1598 | "#, | ||
1599 | "error: Cannot rename `Self`", | ||
1600 | ); | ||
1601 | } | ||
1602 | |||
1603 | #[test] | ||
1604 | fn test_rename_ignores_self_ty() { | ||
1605 | check( | ||
1606 | "Fo0", | ||
1607 | r#" | ||
1608 | struct $0Foo; | ||
1609 | |||
1610 | impl Foo where Self: {} | ||
1611 | "#, | ||
1612 | r#" | ||
1613 | struct Fo0; | ||
1614 | |||
1615 | impl Fo0 where Self: {} | ||
1616 | "#, | ||
1617 | ); | ||
1618 | } | ||
1619 | |||
1620 | #[test] | ||
1621 | fn test_rename_fails_on_aliases() { | ||
1622 | check( | ||
1623 | "Baz", | ||
1624 | r#" | ||
1625 | struct Foo; | ||
1626 | use Foo as Bar$0; | ||
1627 | "#, | ||
1628 | "error: Renaming aliases is currently unsupported", | ||
1629 | ); | ||
1630 | check( | ||
1631 | "Baz", | ||
1632 | r#" | ||
1633 | struct Foo; | ||
1634 | use Foo as Bar; | ||
1635 | use Bar$0; | ||
1636 | "#, | ||
1637 | "error: Renaming aliases is currently unsupported", | ||
1638 | ); | ||
1639 | } | ||
1640 | |||
1641 | #[test] | ||
1642 | fn test_rename_trait_method() { | ||
1643 | let res = r" | ||
1644 | trait Foo { | ||
1645 | fn foo(&self) { | ||
1646 | self.foo(); | ||
1647 | } | ||
1648 | } | ||
1649 | |||
1650 | impl Foo for () { | ||
1651 | fn foo(&self) { | ||
1652 | self.foo(); | ||
1653 | } | ||
1654 | }"; | ||
1655 | check( | ||
1656 | "foo", | ||
1657 | r#" | ||
1658 | trait Foo { | ||
1659 | fn bar$0(&self) { | ||
1660 | self.bar(); | ||
1661 | } | ||
1662 | } | ||
1663 | |||
1664 | impl Foo for () { | ||
1665 | fn bar(&self) { | ||
1666 | self.bar(); | ||
1667 | } | ||
1668 | }"#, | ||
1669 | res, | ||
1670 | ); | ||
1671 | check( | ||
1672 | "foo", | ||
1673 | r#" | ||
1674 | trait Foo { | ||
1675 | fn bar(&self) { | ||
1676 | self.bar$0(); | ||
1677 | } | ||
1678 | } | ||
1679 | |||
1680 | impl Foo for () { | ||
1681 | fn bar(&self) { | ||
1682 | self.bar(); | ||
1683 | } | ||
1684 | }"#, | ||
1685 | res, | ||
1686 | ); | ||
1687 | check( | ||
1688 | "foo", | ||
1689 | r#" | ||
1690 | trait Foo { | ||
1691 | fn bar(&self) { | ||
1692 | self.bar(); | ||
1693 | } | ||
1694 | } | ||
1695 | |||
1696 | impl Foo for () { | ||
1697 | fn bar$0(&self) { | ||
1698 | self.bar(); | ||
1699 | } | ||
1700 | }"#, | ||
1701 | res, | ||
1702 | ); | ||
1703 | check( | ||
1704 | "foo", | ||
1705 | r#" | ||
1706 | trait Foo { | ||
1707 | fn bar(&self) { | ||
1708 | self.bar(); | ||
1709 | } | ||
1710 | } | ||
1711 | |||
1712 | impl Foo for () { | ||
1713 | fn bar(&self) { | ||
1714 | self.bar$0(); | ||
1715 | } | ||
1716 | }"#, | ||
1717 | res, | ||
1718 | ); | ||
1719 | } | ||
1720 | |||
1721 | #[test] | ||
1722 | fn test_rename_trait_const() { | ||
1723 | let res = r" | ||
1724 | trait Foo { | ||
1725 | const FOO: (); | ||
1726 | } | ||
1727 | |||
1728 | impl Foo for () { | ||
1729 | const FOO: (); | ||
1730 | } | ||
1731 | fn f() { <()>::FOO; }"; | ||
1732 | check( | ||
1733 | "FOO", | ||
1734 | r#" | ||
1735 | trait Foo { | ||
1736 | const BAR$0: (); | ||
1737 | } | ||
1738 | |||
1739 | impl Foo for () { | ||
1740 | const BAR: (); | ||
1741 | } | ||
1742 | fn f() { <()>::BAR; }"#, | ||
1743 | res, | ||
1744 | ); | ||
1745 | check( | ||
1746 | "FOO", | ||
1747 | r#" | ||
1748 | trait Foo { | ||
1749 | const BAR: (); | ||
1750 | } | ||
1751 | |||
1752 | impl Foo for () { | ||
1753 | const BAR$0: (); | ||
1754 | } | ||
1755 | fn f() { <()>::BAR; }"#, | ||
1756 | res, | ||
1757 | ); | ||
1758 | check( | ||
1759 | "FOO", | ||
1760 | r#" | ||
1761 | trait Foo { | ||
1762 | const BAR: (); | ||
1763 | } | ||
1764 | |||
1765 | impl Foo for () { | ||
1766 | const BAR: (); | ||
1767 | } | ||
1768 | fn f() { <()>::BAR$0; }"#, | ||
1769 | res, | ||
1770 | ); | ||
1771 | } | ||
1772 | |||
1773 | #[test] | ||
1774 | fn macros_are_broken_lol() { | ||
1775 | cov_mark::check!(macros_are_broken_lol); | ||
1776 | check( | ||
1777 | "lol", | ||
1778 | r#" | ||
1779 | macro_rules! m { () => { fn f() {} } } | ||
1780 | m!(); | ||
1781 | fn main() { f$0() } | ||
1782 | "#, | ||
1783 | r#" | ||
1784 | macro_rules! m { () => { fn f() {} } } | ||
1785 | lol | ||
1786 | fn main() { lol() } | ||
1787 | "#, | ||
1788 | ) | ||
1789 | } | ||
1790 | } | ||