1use hir::{AsAssocItem, FindPathConfig, HasContainer, HirDisplay, InFile, Name, Semantics, sym};
8use ide_db::{
9 FileId, FileRange, RootDatabase,
10 defs::{Definition, NameClass, NameRefClass},
11 rename::{IdentifierKind, RenameDefinition, bail, format_err, source_edit_from_references},
12 source_change::SourceChangeBuilder,
13};
14use itertools::Itertools;
15use std::fmt::Write;
16use stdx::{always, format_to, never};
17use syntax::{
18 AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize,
19 ast::{self, HasArgList, make, prec::ExprPrecedence},
20};
21
22use ide_db::text_edit::TextEdit;
23
24use crate::{FilePosition, RangeInfo, SourceChange};
25
26pub use ide_db::rename::RenameError;
27
28type RenameResult<T> = Result<T, RenameError>;
29
30pub struct RenameConfig {
31 pub prefer_no_std: bool,
32 pub prefer_prelude: bool,
33 pub prefer_absolute: bool,
34 pub show_conflicts: bool,
35}
36
37impl RenameConfig {
38 fn find_path_config(&self) -> FindPathConfig {
39 FindPathConfig {
40 prefer_no_std: self.prefer_no_std,
41 prefer_prelude: self.prefer_prelude,
42 prefer_absolute: self.prefer_absolute,
43 allow_unstable: true,
44 }
45 }
46
47 fn ide_db_config(&self) -> ide_db::rename::RenameConfig {
48 ide_db::rename::RenameConfig { show_conflicts: self.show_conflicts }
49 }
50}
51
52fn ok_if_any<T, E>(iter: impl Iterator<Item = Result<T, E>>) -> Result<Vec<T>, E> {
54 let mut err = None;
55 let oks = iter
56 .filter_map(|item| match item {
57 Ok(it) => Some(it),
58 Err(it) => {
59 err = Some(it);
60 None
61 }
62 })
63 .collect::<Vec<_>>();
64 if !oks.is_empty() {
65 Ok(oks)
66 } else if let Some(err) = err {
67 Err(err)
68 } else {
69 Ok(Vec::new())
70 }
71}
72
73pub(crate) fn prepare_rename(
76 db: &RootDatabase,
77 position: FilePosition,
78) -> RenameResult<RangeInfo<()>> {
79 let sema = Semantics::new(db);
80 let source_file = sema.parse_guess_edition(position.file_id);
81 let syntax = source_file.syntax();
82 if let Some(lifetime_token) = syntax.token_at_offset(position.offset).find(|t| t.text() == "'_")
83 {
84 return Ok(RangeInfo::new(lifetime_token.text_range(), ()));
85 }
86 let res = find_definitions(&sema, syntax, position, &Name::new_symbol_root(sym::underscore))?
87 .filter(|(_, _, def, _, _)| def.range_for_rename(&sema).is_some())
88 .map(|(frange, kind, _, _, _)| {
89 always!(
90 frange.range.contains_inclusive(position.offset)
91 && frange.file_id == position.file_id
92 );
93
94 Ok(match kind {
95 SyntaxKind::LIFETIME => {
96 TextRange::new(frange.range.start() + TextSize::from(1), frange.range.end())
97 }
98 _ => frange.range,
99 })
100 })
101 .reduce(|acc, cur| match (acc, cur) {
102 (Ok(acc_inner), Ok(cur_inner)) if acc_inner == cur_inner => Ok(acc_inner),
104 (e @ Err(_), _) | (_, e @ Err(_)) => e,
105 _ => bail!("inconsistent text range"),
106 });
107
108 match res {
109 Some(res) => res.map(|range| RangeInfo::new(range, ())),
111 None => bail!("No references found at position"),
112 }
113}
114
115pub(crate) fn rename(
125 db: &RootDatabase,
126 position: FilePosition,
127 new_name: &str,
128 config: &RenameConfig,
129) -> RenameResult<SourceChange> {
130 let sema = Semantics::new(db);
131 let file_id = sema
132 .attach_first_edition_opt(position.file_id)
133 .ok_or_else(|| format_err!("No references found at position"))?;
134 let source_file = sema.parse(file_id);
135 let syntax = source_file.syntax();
136
137 let edition = file_id.edition(db);
138 let (new_name, kind) = IdentifierKind::classify(edition, new_name)?;
139 if kind == IdentifierKind::Lifetime
140 && let Some(lifetime_token) =
141 syntax.token_at_offset(position.offset).find(|t| t.text() == "'_")
142 {
143 let new_name_str = new_name.display(db, edition).to_string();
144 return rename_elided_lifetime(position, lifetime_token, &new_name_str);
145 }
146
147 let defs = find_definitions(&sema, syntax, position, &new_name)?;
148 let alias_fallback =
149 alias_fallback(syntax, position, &new_name.display(db, edition).to_string());
150
151 let ops: RenameResult<Vec<SourceChange>> = match alias_fallback {
152 Some(_) => ok_if_any(
153 defs
154 .map(|(.., def, new_name, _)| {
157 match kind {
158 IdentifierKind::Ident => (),
159 IdentifierKind::Lifetime => {
160 bail!("Cannot alias reference to a lifetime identifier")
161 }
162 IdentifierKind::Underscore => bail!("Cannot alias reference to `_`"),
163 IdentifierKind::LowercaseSelf => {
164 bail!("Cannot rename alias reference to `self`")
165 }
166 };
167 let mut usages = def.usages(&sema).all();
168
169 match usages.references.get_mut(&file_id).and_then(|refs| {
171 refs.iter()
172 .position(|ref_| ref_.range.contains_inclusive(position.offset))
173 .map(|idx| refs.remove(idx))
174 }) {
175 Some(_) => (),
176 None => never!(),
177 };
178
179 let mut source_change = SourceChange::default();
180 source_change.extend(usages.references.get_mut(&file_id).iter().map(|refs| {
181 (
182 position.file_id,
183 source_edit_from_references(db, refs, def, &new_name, edition),
184 )
185 }));
186
187 Ok(source_change)
188 }),
189 ),
190 None => ok_if_any(defs.map(|(.., def, new_name, rename_def)| {
191 if let Definition::Local(local) = def {
192 if let Some(self_param) = local.as_self_param(sema.db) {
193 cov_mark::hit!(rename_self_to_param);
194 return rename_self_to_param(
195 &sema,
196 local,
197 self_param,
198 &new_name,
199 kind,
200 config.find_path_config(),
201 );
202 }
203 if kind == IdentifierKind::LowercaseSelf {
204 cov_mark::hit!(rename_to_self);
205 return rename_to_self(&sema, local);
206 }
207 }
208 def.rename(&sema, new_name.as_str(), rename_def, &config.ide_db_config())
209 })),
210 };
211
212 ops?.into_iter()
213 .chain(alias_fallback)
214 .reduce(|acc, elem| acc.merge(elem))
215 .ok_or_else(|| format_err!("No references found at position"))
216}
217
218pub(crate) fn will_rename_file(
220 db: &RootDatabase,
221 file_id: FileId,
222 new_name_stem: &str,
223 config: &RenameConfig,
224) -> Option<SourceChange> {
225 let sema = Semantics::new(db);
226 let module = sema.file_to_module_def(file_id)?;
227 let def = Definition::Module(module);
228 let mut change =
229 def.rename(&sema, new_name_stem, RenameDefinition::Yes, &config.ide_db_config()).ok()?;
230 change.file_system_edits.clear();
231 Some(change)
232}
233
234fn alias_fallback(
236 syntax: &SyntaxNode,
237 FilePosition { file_id, offset }: FilePosition,
238 new_name: &str,
239) -> Option<SourceChange> {
240 let use_tree = syntax
241 .token_at_offset(offset)
242 .flat_map(|syntax| syntax.parent_ancestors())
243 .find_map(ast::UseTree::cast)?;
244
245 let last_path_segment = use_tree.path()?.segments().last()?.name_ref()?;
246 if !last_path_segment.syntax().text_range().contains_inclusive(offset) {
247 return None;
248 };
249
250 let mut builder = SourceChangeBuilder::new(file_id);
251
252 match use_tree.rename() {
253 Some(rename) => {
254 let offset = rename.syntax().text_range();
255 builder.replace(offset, format!("as {new_name}"));
256 }
257 None => {
258 let offset = use_tree.syntax().text_range().end();
259 builder.insert(offset, format!(" as {new_name}"));
260 }
261 }
262
263 Some(builder.finish())
264}
265
266fn find_definitions(
267 sema: &Semantics<'_, RootDatabase>,
268 syntax: &SyntaxNode,
269 FilePosition { file_id, offset }: FilePosition,
270 new_name: &Name,
271) -> RenameResult<impl Iterator<Item = (FileRange, SyntaxKind, Definition, Name, RenameDefinition)>>
272{
273 let maybe_format_args =
274 syntax.token_at_offset(offset).find(|t| matches!(t.kind(), SyntaxKind::STRING));
275
276 if let Some((range, _, _, Some(resolution))) =
277 maybe_format_args.and_then(|token| sema.check_for_format_args_template(token, offset))
278 {
279 return Ok(vec![(
280 FileRange { file_id, range },
281 SyntaxKind::STRING,
282 Definition::from(resolution),
283 new_name.clone(),
284 RenameDefinition::Yes,
285 )]
286 .into_iter());
287 }
288
289 let original_ident = syntax
290 .token_at_offset(offset)
291 .max_by_key(|t| {
292 t.kind().is_any_identifier() || matches!(t.kind(), SyntaxKind::LIFETIME_IDENT)
293 })
294 .map(|t| {
295 if t.kind() == SyntaxKind::LIFETIME_IDENT {
296 Name::new_lifetime(t.text())
297 } else {
298 Name::new_root(t.text())
299 }
300 })
301 .ok_or_else(|| format_err!("No references found at position"))?;
302 let symbols =
303 sema.find_namelike_at_offset_with_descend(syntax, offset).map(|name_like| {
304 let kind = name_like.syntax().kind();
305 let range = sema
306 .original_range_opt(name_like.syntax())
307 .ok_or_else(|| format_err!("No references found at position"))?;
308 let res = match &name_like {
309 ast::NameLike::Name(name)
311 if name
312 .syntax()
313 .parent().is_some_and(|it| ast::Rename::can_cast(it.kind()))
314 =>
321 {
322 bail!("Renaming aliases is currently unsupported")
323 }
324 ast::NameLike::Name(name) => NameClass::classify(sema, name)
325 .map(|class| match class {
326 NameClass::Definition(it) | NameClass::ConstReference(it) => it,
327 NameClass::PatFieldShorthand { local_def, field_ref: _, adt_subst: _ } => {
328 Definition::Local(local_def)
329 }
330 })
331 .ok_or_else(|| format_err!("No references found at position")),
332 ast::NameLike::NameRef(name_ref) => {
333 NameRefClass::classify(sema, name_ref)
334 .map(|class| match class {
335 NameRefClass::Definition(def, _) => def,
336 NameRefClass::FieldShorthand { local_ref, field_ref: _, adt_subst: _ } => {
337 Definition::Local(local_ref)
338 }
339 NameRefClass::ExternCrateShorthand { decl, .. } => {
340 Definition::ExternCrateDecl(decl)
341 }
342 })
343 .filter(|def| !matches!(def, Definition::ExternCrateDecl(..)))
345 .ok_or_else(|| format_err!("No references found at position"))
346 .and_then(|def| {
347 if def
349 .name(sema.db).is_some_and(|it| it.as_str() != name_ref.text().trim_start_matches("r#"))
350 {
351 Err(format_err!("Renaming aliases is currently unsupported"))
352 } else {
353 Ok(def)
354 }
355 })
356 }
357 ast::NameLike::Lifetime(lifetime) => {
358 NameRefClass::classify_lifetime(sema, lifetime)
359 .and_then(|class| match class {
360 NameRefClass::Definition(def, _) => Some(def),
361 _ => None,
362 })
363 .or_else(|| {
364 NameClass::classify_lifetime(sema, lifetime).and_then(|it| match it {
365 NameClass::Definition(it) => Some(it),
366 _ => None,
367 })
368 })
369 .ok_or_else(|| format_err!("No references found at position"))
370 }
371 };
372 res.map(|def| {
373 let n = def.name(sema.db)?;
374 if n == original_ident {
375 Some((range, kind, def, new_name.clone(), RenameDefinition::Yes))
376 } else if let Some(suffix) = n.as_str().strip_prefix(original_ident.as_str()) {
377 Some((range, kind, def, Name::new_root(&format!("{}{suffix}", new_name.as_str())), RenameDefinition::No))
378 } else {
379 n.as_str().strip_suffix(original_ident.as_str().trim_start_matches('\''))
380 .map(|prefix| (range, kind, def, Name::new_root(&format!("{prefix}{}", new_name.as_str())), RenameDefinition::No))
381 }
382 })
383 });
384
385 let res: RenameResult<Vec<_>> = ok_if_any(symbols.filter_map(Result::transpose));
386 match res {
387 Ok(v) => {
388 Ok(v.into_iter()
390 .unique_by(|&(.., def, _, _)| def)
391 .map(|(a, b, c, d, e)| (a.into_file_id(sema.db), b, c, d, e))
392 .collect::<Vec<_>>()
393 .into_iter())
394 }
395 Err(e) => Err(e),
396 }
397}
398
399fn transform_assoc_fn_into_method_call(
400 sema: &Semantics<'_, RootDatabase>,
401 source_change: &mut SourceChange,
402 f: hir::Function,
403) {
404 let calls = Definition::Function(f).usages(sema).all();
405 for (_file_id, calls) in calls {
406 for call in calls {
407 let Some(fn_name) = call.name.as_name_ref() else { continue };
408 let Some(path) = fn_name.syntax().parent().and_then(ast::PathSegment::cast) else {
409 continue;
410 };
411 let path = path.parent_path();
412 let Some(call) =
414 path.syntax().parent().and_then(|it| ast::CallExpr::cast(it.parent()?))
415 else {
416 continue;
417 };
418
419 let Some(arg_list) = call.arg_list() else { continue };
420 let mut args = arg_list.args();
421 let Some(mut self_arg) = args.next() else { continue };
422 let second_arg = args.next();
423
424 loop {
426 let self_ = match &self_arg {
427 ast::Expr::RefExpr(self_) => self_.expr(),
428 ast::Expr::ParenExpr(self_) => self_.expr(),
429 ast::Expr::PrefixExpr(self_)
430 if self_.op_kind() == Some(ast::UnaryOp::Deref) =>
431 {
432 self_.expr()
433 }
434 _ => break,
435 };
436 self_arg = match self_ {
437 Some(it) => it,
438 None => break,
439 };
440 }
441
442 let self_needs_parens =
443 self_arg.precedence().needs_parentheses_in(ExprPrecedence::Postfix);
444
445 let replace_start = path.syntax().text_range().start();
446 let replace_end = match second_arg {
447 Some(second_arg) => second_arg.syntax().text_range().start(),
448 None => arg_list
449 .r_paren_token()
450 .map(|it| it.text_range().start())
451 .unwrap_or_else(|| arg_list.syntax().text_range().end()),
452 };
453 let replace_range = TextRange::new(replace_start, replace_end);
454 let macro_file = sema.hir_file_for(fn_name.syntax());
455 let Some((replace_range, _)) =
456 InFile::new(macro_file, replace_range).original_node_file_range_opt(sema.db)
457 else {
458 continue;
459 };
460
461 let Some(macro_mapped_self) = sema.original_range_opt(self_arg.syntax()) else {
462 continue;
463 };
464 let mut replacement = String::new();
465 if self_needs_parens {
466 replacement.push('(');
467 }
468 replacement.push_str(macro_mapped_self.text(sema.db));
469 if self_needs_parens {
470 replacement.push(')');
471 }
472 replacement.push('.');
473 format_to!(replacement, "{fn_name}");
474 replacement.push('(');
475
476 source_change.insert_source_edit(
477 replace_range.file_id.file_id(sema.db),
478 TextEdit::replace(replace_range.range, replacement),
479 );
480 }
481 }
482}
483
484fn rename_to_self(
485 sema: &Semantics<'_, RootDatabase>,
486 local: hir::Local,
487) -> RenameResult<SourceChange> {
488 if never!(local.is_self(sema.db)) {
489 bail!("rename_to_self invoked on self");
490 }
491
492 let fn_def = match local.parent(sema.db) {
493 hir::ExpressionStoreOwner::Body(hir::DefWithBody::Function(func)) => func,
494 _ => bail!("Cannot rename local to self outside of function"),
495 };
496
497 if fn_def.self_param(sema.db).is_some() {
498 bail!("Method already has a self parameter");
499 }
500
501 let params = fn_def.assoc_fn_params(sema.db);
502 let first_param = params
503 .first()
504 .ok_or_else(|| format_err!("Cannot rename local to self unless it is a parameter"))?;
505 match first_param.as_local(sema.db) {
506 Some(plocal) => {
507 if plocal != local {
508 bail!("Only the first parameter may be renamed to self");
509 }
510 }
511 None => bail!("rename_to_self invoked on destructuring parameter"),
512 }
513
514 let assoc_item = fn_def
515 .as_assoc_item(sema.db)
516 .ok_or_else(|| format_err!("Cannot rename parameter to self for free function"))?;
517 let impl_ = match assoc_item.container(sema.db) {
518 hir::AssocItemContainer::Trait(_) => {
519 bail!("Cannot rename parameter to self for trait functions");
520 }
521 hir::AssocItemContainer::Impl(impl_) => impl_,
522 };
523 let first_param_ty = first_param.ty();
524 let impl_ty = impl_.self_ty(sema.db);
525 let (ty, self_param) = if impl_ty.remove_ref().is_some() {
526 (first_param_ty.clone(), "self")
528 } else {
529 first_param_ty.remove_ref().map_or((first_param_ty.clone(), "self"), |ty| {
530 (ty, if first_param_ty.is_mutable_reference() { "&mut self" } else { "&self" })
531 })
532 };
533
534 if ty != impl_ty {
535 bail!("Parameter type differs from impl block type");
536 }
537
538 let InFile { file_id, value: param_source } = sema
539 .source(first_param.clone())
540 .ok_or_else(|| format_err!("No source for parameter found"))?;
541
542 let def = Definition::Local(local);
543 let usages = def.usages(sema).all();
544 let mut source_change = SourceChange::default();
545 source_change.extend(usages.iter().map(|(file_id, references)| {
546 (
547 file_id.file_id(sema.db),
548 source_edit_from_references(
549 sema.db,
550 references,
551 def,
552 &Name::new_symbol_root(sym::self_),
553 file_id.edition(sema.db),
554 ),
555 )
556 }));
557 source_change.insert_source_edit(
558 file_id.original_file(sema.db).file_id(sema.db),
559 TextEdit::replace(param_source.syntax().text_range(), String::from(self_param)),
560 );
561 transform_assoc_fn_into_method_call(sema, &mut source_change, fn_def);
562 Ok(source_change)
563}
564
565#[derive(Debug, Clone, Copy, PartialEq, Eq)]
566enum CallReceiverAdjust {
567 Deref,
568 Ref,
569 RefMut,
570 None,
571}
572
573fn method_to_assoc_fn_call_self_adjust(
574 sema: &Semantics<'_, RootDatabase>,
575 self_arg: &ast::Expr,
576) -> CallReceiverAdjust {
577 let mut result = CallReceiverAdjust::None;
578 let self_adjust = sema.expr_adjustments(self_arg);
579 if let Some(self_adjust) = self_adjust {
580 let mut i = 0;
581 while i < self_adjust.len() {
582 if matches!(self_adjust[i].kind, hir::Adjust::Deref(..))
583 && matches!(
584 self_adjust.get(i + 1),
585 Some(hir::Adjustment { kind: hir::Adjust::Borrow(..), .. })
586 )
587 {
588 i += 2;
590 continue;
591 }
592
593 match self_adjust[i].kind {
594 hir::Adjust::Deref(_) if result == CallReceiverAdjust::None => {
595 result = CallReceiverAdjust::Deref;
598 }
599 hir::Adjust::Borrow(hir::AutoBorrow::Ref(mutability)) => {
600 match (result, mutability) {
601 (CallReceiverAdjust::RefMut, hir::Mutability::Shared) => {}
602 (_, hir::Mutability::Mut) => result = CallReceiverAdjust::RefMut,
603 (_, hir::Mutability::Shared) => result = CallReceiverAdjust::Ref,
604 }
605 }
606 _ => {}
607 }
608
609 i += 1;
610 }
611 }
612 result
613}
614
615fn transform_method_call_into_assoc_fn(
616 sema: &Semantics<'_, RootDatabase>,
617 source_change: &mut SourceChange,
618 f: hir::Function,
619 find_path_config: FindPathConfig,
620) {
621 let calls = Definition::Function(f).usages(sema).all();
622 for (_file_id, calls) in calls {
623 for call in calls {
624 let Some(fn_name) = call.name.as_name_ref() else { continue };
625 let Some(method_call) = fn_name.syntax().parent().and_then(ast::MethodCallExpr::cast)
626 else {
627 continue;
628 };
629 let Some(mut self_arg) = method_call.receiver() else {
630 continue;
631 };
632
633 let Some(scope) = sema.scope(fn_name.syntax()) else {
634 continue;
635 };
636 let self_adjust = method_to_assoc_fn_call_self_adjust(sema, &self_arg);
637
638 while let ast::Expr::ParenExpr(it) = &self_arg {
640 self_arg = match it.expr() {
641 Some(it) => it,
642 None => break,
643 };
644 }
645
646 let needs_comma = method_call.arg_list().is_some_and(|it| it.args().next().is_some());
647
648 let self_needs_parens = self_adjust != CallReceiverAdjust::None
649 && self_arg.precedence().needs_parentheses_in(ExprPrecedence::Prefix);
650
651 let replace_start = method_call.syntax().text_range().start();
652 let replace_end = method_call
653 .arg_list()
654 .and_then(|it| it.l_paren_token())
655 .map(|it| it.text_range().end())
656 .unwrap_or_else(|| method_call.syntax().text_range().end());
657 let replace_range = TextRange::new(replace_start, replace_end);
658 let macro_file = sema.hir_file_for(fn_name.syntax());
659 let Some((replace_range, _)) =
660 InFile::new(macro_file, replace_range).original_node_file_range_opt(sema.db)
661 else {
662 continue;
663 };
664
665 let fn_container_path = match f.container(sema.db) {
666 hir::ItemContainer::Trait(trait_) => {
667 let Some(path) = scope.module().find_path(
671 sema.db,
672 hir::ItemInNs::Types(trait_.into()),
673 find_path_config,
674 ) else {
675 continue;
676 };
677 path.display(sema.db, replace_range.file_id.edition(sema.db)).to_string()
678 }
679 hir::ItemContainer::Impl(impl_) => {
680 let ty = impl_.self_ty(sema.db);
681 match ty.as_adt() {
682 Some(adt) => {
683 let Some(path) = scope.module().find_path(
684 sema.db,
685 hir::ItemInNs::Types(adt.into()),
686 find_path_config,
687 ) else {
688 continue;
689 };
690 path.display(sema.db, replace_range.file_id.edition(sema.db))
691 .to_string()
692 }
693 None => {
694 let Ok(mut ty) =
695 ty.display_source_code(sema.db, scope.module().into(), false)
696 else {
697 continue;
698 };
699 ty.insert(0, '<');
700 ty.push('>');
701 ty
702 }
703 }
704 }
705 _ => continue,
706 };
707
708 let Some(macro_mapped_self) = sema.original_range_opt(self_arg.syntax()) else {
709 continue;
710 };
711 let mut replacement = String::new();
712 replacement.push_str(&fn_container_path);
713 replacement.push_str("::");
714 format_to!(replacement, "{fn_name}");
715 replacement.push('(');
716 replacement.push_str(match self_adjust {
717 CallReceiverAdjust::Deref => "*",
718 CallReceiverAdjust::Ref => "&",
719 CallReceiverAdjust::RefMut => "&mut ",
720 CallReceiverAdjust::None => "",
721 });
722 if self_needs_parens {
723 replacement.push('(');
724 }
725 replacement.push_str(macro_mapped_self.text(sema.db));
726 if self_needs_parens {
727 replacement.push(')');
728 }
729 if needs_comma {
730 replacement.push_str(", ");
731 }
732
733 source_change.insert_source_edit(
734 replace_range.file_id.file_id(sema.db),
735 TextEdit::replace(replace_range.range, replacement),
736 );
737 }
738 }
739}
740
741fn rename_self_to_param(
742 sema: &Semantics<'_, RootDatabase>,
743 local: hir::Local,
744 self_param: hir::SelfParam,
745 new_name: &Name,
746 identifier_kind: IdentifierKind,
747 find_path_config: FindPathConfig,
748) -> RenameResult<SourceChange> {
749 if identifier_kind == IdentifierKind::LowercaseSelf {
750 cov_mark::hit!(rename_self_to_self);
752 return Ok(SourceChange::default());
753 }
754
755 let fn_def = match local.parent(sema.db) {
756 hir::ExpressionStoreOwner::Body(hir::DefWithBody::Function(func)) => func,
757 _ => bail!("Cannot rename local to self outside of function"),
758 };
759
760 let InFile { file_id, value: self_param } =
761 sema.source(self_param).ok_or_else(|| format_err!("cannot find function source"))?;
762
763 let def = Definition::Local(local);
764 let usages = def.usages(sema).all();
765 let edit = text_edit_from_self_param(
766 &self_param,
767 new_name.display(sema.db, file_id.edition(sema.db)).to_string(),
768 )
769 .ok_or_else(|| format_err!("No target type found"))?;
770 if usages.len() > 1 && identifier_kind == IdentifierKind::Underscore {
771 bail!("Cannot rename reference to `_` as it is being referenced multiple times");
772 }
773 let mut source_change = SourceChange::default();
774 source_change.insert_source_edit(file_id.original_file(sema.db).file_id(sema.db), edit);
775 source_change.extend(usages.iter().map(|(file_id, references)| {
776 (
777 file_id.file_id(sema.db),
778 source_edit_from_references(
779 sema.db,
780 references,
781 def,
782 new_name,
783 file_id.edition(sema.db),
784 ),
785 )
786 }));
787 transform_method_call_into_assoc_fn(sema, &mut source_change, fn_def, find_path_config);
788 Ok(source_change)
789}
790
791fn text_edit_from_self_param(self_param: &ast::SelfParam, new_name: String) -> Option<TextEdit> {
792 let mut replacement_text = new_name;
793 replacement_text.push_str(": ");
794
795 if self_param.amp_token().is_some() {
796 replacement_text.push('&');
797 }
798 if let Some(lifetime) = self_param.lifetime() {
799 write!(replacement_text, "{lifetime} ").unwrap();
800 }
801 if self_param.amp_token().and(self_param.mut_token()).is_some() {
802 replacement_text.push_str("mut ");
803 }
804
805 replacement_text.push_str("Self");
806
807 Some(TextEdit::replace(self_param.syntax().text_range(), replacement_text))
808}
809
810fn rename_elided_lifetime(
811 position: FilePosition,
812 lifetime_token: syntax::SyntaxToken,
813 new_name: &str,
814) -> RenameResult<SourceChange> {
815 let parent = lifetime_token.parent().unwrap();
816 let root = parent.ancestors().last().unwrap();
817
818 let mut builder = SourceChangeBuilder::new(position.file_id);
819
820 let editor = builder.make_editor(&root);
821
822 editor.replace(lifetime_token, make::lifetime(new_name).syntax().clone());
823
824 if let Some(has_generic_params) = parent.ancestors().find_map(ast::AnyHasGenericParams::cast) {
825 let lifetime_param = make::lifetime_param(make::lifetime(new_name));
826 editor.add_generic_param(&has_generic_params, lifetime_param.into());
827 }
828
829 builder.add_file_edits(position.file_id, editor);
830
831 Ok(builder.finish())
832}
833
834#[cfg(test)]
835mod tests {
836 use expect_test::{Expect, expect};
837 use ide_db::source_change::SourceChange;
838 use ide_db::text_edit::TextEdit;
839 use itertools::Itertools;
840 use stdx::trim_indent;
841 use test_utils::assert_eq_text;
842
843 use crate::fixture;
844
845 use super::{RangeInfo, RenameConfig, RenameError};
846
847 const TEST_CONFIG: RenameConfig = RenameConfig {
848 prefer_no_std: false,
849 prefer_prelude: true,
850 prefer_absolute: false,
851 show_conflicts: true,
852 };
853
854 #[track_caller]
855 fn check(
856 new_name: &str,
857 #[rust_analyzer::rust_fixture] ra_fixture_before: &str,
858 #[rust_analyzer::rust_fixture] ra_fixture_after: &str,
859 ) {
860 let ra_fixture_after = &trim_indent(ra_fixture_after);
861 let (analysis, position) = fixture::position(ra_fixture_before);
862 if !ra_fixture_after.starts_with("error: ")
863 && let Err(err) = analysis.prepare_rename(position).unwrap()
864 {
865 panic!("Prepare rename to '{new_name}' was failed: {err}")
866 }
867 let rename_result = analysis
868 .rename(position, new_name, &TEST_CONFIG)
869 .unwrap_or_else(|err| panic!("Rename to '{new_name}' was cancelled: {err}"));
870 match rename_result {
871 Ok(source_change) => {
872 let mut text_edit_builder = TextEdit::builder();
873 let (&file_id, edit) = match source_change.source_file_edits.len() {
874 0 => return,
875 1 => source_change.source_file_edits.iter().next().unwrap(),
876 _ => panic!(),
877 };
878 for indel in edit.0.iter() {
879 text_edit_builder.replace(indel.delete, indel.insert.clone());
880 }
881 let mut result = analysis.file_text(file_id).unwrap().to_string();
882 text_edit_builder.finish().apply(&mut result);
883 assert_eq_text!(ra_fixture_after, &*result);
884 }
885 Err(err) => {
886 if ra_fixture_after.starts_with("error:") {
887 let error_message =
888 ra_fixture_after.chars().skip("error:".len()).collect::<String>();
889 assert_eq!(error_message.trim(), err.to_string());
890 } else {
891 panic!("Rename to '{new_name}' failed unexpectedly: {err}")
892 }
893 }
894 };
895 }
896
897 #[track_caller]
898 fn check_conflicts(new_name: &str, #[rust_analyzer::rust_fixture] ra_fixture: &str) {
899 let (analysis, position, conflicts) = fixture::annotations(ra_fixture);
900 let source_change = analysis.rename(position, new_name, &TEST_CONFIG).unwrap().unwrap();
901 let expected_conflicts = conflicts
902 .into_iter()
903 .map(|(file_range, _)| (file_range.file_id, file_range.range))
904 .sorted_unstable_by_key(|(file_id, range)| (*file_id, range.start()))
905 .collect_vec();
906 let found_conflicts = source_change
907 .source_file_edits
908 .iter()
909 .filter(|(_, (edit, _))| edit.change_annotation().is_some())
910 .flat_map(|(file_id, (edit, _))| {
911 edit.into_iter().map(move |edit| (*file_id, edit.delete))
912 })
913 .sorted_unstable_by_key(|(file_id, range)| (*file_id, range.start()))
914 .collect_vec();
915 assert_eq!(
916 expected_conflicts, found_conflicts,
917 "rename conflicts mismatch: {source_change:#?}"
918 );
919 }
920
921 fn check_expect(
922 new_name: &str,
923 #[rust_analyzer::rust_fixture] ra_fixture: &str,
924 expect: Expect,
925 ) {
926 let (analysis, position) = fixture::position(ra_fixture);
927 let source_change = analysis
928 .rename(position, new_name, &TEST_CONFIG)
929 .unwrap()
930 .expect("Expect returned a RenameError");
931 expect.assert_eq(&filter_expect(source_change))
932 }
933
934 fn check_expect_will_rename_file(
935 new_name: &str,
936 #[rust_analyzer::rust_fixture] ra_fixture: &str,
937 expect: Expect,
938 ) {
939 let (analysis, position) = fixture::position(ra_fixture);
940 let source_change = analysis
941 .will_rename_file(position.file_id, new_name, &TEST_CONFIG)
942 .unwrap()
943 .expect("Expect returned a RenameError");
944 expect.assert_eq(&filter_expect(source_change))
945 }
946
947 fn check_prepare(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
948 let (analysis, position) = fixture::position(ra_fixture);
949 let result = analysis
950 .prepare_rename(position)
951 .unwrap_or_else(|err| panic!("PrepareRename was cancelled: {err}"));
952 match result {
953 Ok(RangeInfo { range, info: () }) => {
954 let source = analysis.file_text(position.file_id).unwrap();
955 expect.assert_eq(&format!("{range:?}: {}", &source[range]))
956 }
957 Err(RenameError(err)) => expect.assert_eq(&err),
958 };
959 }
960
961 fn filter_expect(source_change: SourceChange) -> String {
962 let source_file_edits = source_change
963 .source_file_edits
964 .into_iter()
965 .map(|(id, (text_edit, _))| (id, text_edit.into_iter().collect::<Vec<_>>()))
966 .collect::<Vec<_>>();
967
968 format!(
969 "source_file_edits: {:#?}\nfile_system_edits: {:#?}\n",
970 source_file_edits, source_change.file_system_edits
971 )
972 }
973
974 #[test]
975 fn rename_will_shadow() {
976 check_conflicts(
977 "new_name",
978 r#"
979fn foo() {
980 let mut new_name = 123;
981 let old_name$0 = 456;
982 // ^^^^^^^^
983 new_name = 789 + new_name;
984}
985 "#,
986 );
987 }
988
989 #[test]
990 fn rename_will_be_shadowed() {
991 check_conflicts(
992 "new_name",
993 r#"
994fn foo() {
995 let mut old_name$0 = 456;
996 // ^^^^^^^^
997 let new_name = 123;
998 old_name = 789 + old_name;
999 // ^^^^^^^^ ^^^^^^^^
1000}
1001 "#,
1002 );
1003 }
1004
1005 #[test]
1006 fn test_prepare_rename_namelikes() {
1007 check_prepare(r"fn name$0<'lifetime>() {}", expect![[r#"3..7: name"#]]);
1008 check_prepare(r"fn name<'lifetime$0>() {}", expect![[r#"9..17: lifetime"#]]);
1009 check_prepare(r"fn name<'lifetime>() { name$0(); }", expect![[r#"23..27: name"#]]);
1010 }
1011
1012 #[test]
1013 fn test_prepare_rename_in_macro() {
1014 check_prepare(
1015 r"macro_rules! foo {
1016 ($ident:ident) => {
1017 pub struct $ident;
1018 }
1019}
1020foo!(Foo$0);",
1021 expect![[r#"83..86: Foo"#]],
1022 );
1023 }
1024
1025 #[test]
1026 fn test_prepare_rename_keyword() {
1027 check_prepare(r"struct$0 Foo;", expect![[r#"No references found at position"#]]);
1028 }
1029
1030 #[test]
1031 fn test_prepare_rename_tuple_field() {
1032 check_prepare(
1033 r#"
1034struct Foo(i32);
1035
1036fn baz() {
1037 let mut x = Foo(4);
1038 x.0$0 = 5;
1039}
1040"#,
1041 expect![[r#"No references found at position"#]],
1042 );
1043 }
1044
1045 #[test]
1046 fn test_prepare_rename_builtin() {
1047 check_prepare(
1048 r#"
1049fn foo() {
1050 let x: i32$0 = 0;
1051}
1052"#,
1053 expect![[r#"No references found at position"#]],
1054 );
1055 }
1056
1057 #[test]
1058 fn test_prepare_rename_self() {
1059 check_prepare(
1060 r#"
1061struct Foo {}
1062
1063impl Foo {
1064 fn foo(self) -> Self$0 {
1065 self
1066 }
1067}
1068"#,
1069 expect![[r#"No references found at position"#]],
1070 );
1071 }
1072
1073 #[test]
1074 fn test_rename_to_underscore() {
1075 check("_", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let _ = 1; }"#);
1076 }
1077
1078 #[test]
1079 fn test_rename_to_raw_identifier() {
1080 check("r#fn", r#"fn main() { let i$0 = 1; }"#, r#"fn main() { let r#fn = 1; }"#);
1081 }
1082
1083 #[test]
1084 fn test_rename_to_invalid_identifier1() {
1085 check(
1086 "invalid!",
1087 r#"fn main() { let i$0 = 1; }"#,
1088 "error: Invalid name `invalid!`: not an identifier",
1089 );
1090 }
1091
1092 #[test]
1093 fn test_rename_to_invalid_identifier2() {
1094 check(
1095 "multiple tokens",
1096 r#"fn main() { let i$0 = 1; }"#,
1097 "error: Invalid name `multiple tokens`: not an identifier",
1098 );
1099 }
1100
1101 #[test]
1102 fn test_rename_to_invalid_identifier3() {
1103 check(
1104 "super",
1105 r#"fn main() { let i$0 = 1; }"#,
1106 "error: Invalid name `super`: cannot rename to a keyword",
1107 );
1108 }
1109
1110 #[test]
1111 fn test_rename_to_invalid_identifier_lifetime() {
1112 cov_mark::check!(rename_not_an_ident_ref);
1113 check(
1114 "'foo",
1115 r#"fn main() { let i$0 = 1; }"#,
1116 "error: Invalid name `'foo`: not an identifier",
1117 );
1118 }
1119
1120 #[test]
1121 fn test_rename_to_invalid_identifier_lifetime2() {
1122 check(
1123 "_",
1124 r#"fn main<'a>(_: &'a$0 ()) {}"#,
1125 r#"error: Invalid name `_`: not a lifetime identifier"#,
1126 );
1127 }
1128
1129 #[test]
1130 fn test_rename_accepts_lifetime_without_apostrophe() {
1131 check("foo", r#"fn main<'a>(_: &'a$0 ()) {}"#, r#"fn main<'foo>(_: &'foo ()) {}"#);
1132 }
1133
1134 #[test]
1135 fn test_rename_to_underscore_invalid() {
1136 cov_mark::check!(rename_underscore_multiple);
1137 check(
1138 "_",
1139 r#"fn main(foo$0: ()) {foo;}"#,
1140 "error: Cannot rename reference to `_` as it is being referenced multiple times",
1141 );
1142 }
1143
1144 #[test]
1145 fn test_rename_mod_invalid() {
1146 check(
1147 "'foo",
1148 r#"mod foo$0 {}"#,
1149 "error: Invalid name `'foo`: cannot rename module to 'foo",
1150 );
1151 }
1152
1153 #[test]
1154 fn test_rename_mod_invalid_raw_ident() {
1155 check(
1156 "r#self",
1157 r#"mod foo$0 {}"#,
1158 "error: Invalid name `self`: cannot rename module to self",
1159 );
1160 }
1161
1162 #[test]
1163 fn test_rename_for_local() {
1164 check(
1165 "k",
1166 r#"
1167fn main() {
1168 let mut i = 1;
1169 let j = 1;
1170 i = i$0 + j;
1171
1172 { i = 0; }
1173
1174 i = 5;
1175}
1176"#,
1177 r#"
1178fn main() {
1179 let mut k = 1;
1180 let j = 1;
1181 k = k + j;
1182
1183 { k = 0; }
1184
1185 k = 5;
1186}
1187"#,
1188 );
1189 }
1190
1191 #[test]
1192 fn test_rename_unresolved_reference() {
1193 check(
1194 "new_name",
1195 r#"fn main() { let _ = unresolved_ref$0; }"#,
1196 "error: No references found at position",
1197 );
1198 }
1199
1200 #[test]
1201 fn test_rename_macro_multiple_occurrences() {
1202 check(
1203 "Baaah",
1204 r#"macro_rules! foo {
1205 ($ident:ident) => {
1206 const $ident: () = ();
1207 struct $ident {}
1208 };
1209}
1210
1211foo!($0Foo);
1212const _: () = Foo;
1213const _: Foo = Foo {};
1214 "#,
1215 r#"
1216macro_rules! foo {
1217 ($ident:ident) => {
1218 const $ident: () = ();
1219 struct $ident {}
1220 };
1221}
1222
1223foo!(Baaah);
1224const _: () = Baaah;
1225const _: Baaah = Baaah {};
1226 "#,
1227 )
1228 }
1229
1230 #[test]
1231 fn test_rename_for_macro_args() {
1232 check(
1233 "b",
1234 r#"
1235macro_rules! foo {($i:ident) => {$i} }
1236fn main() {
1237 let a$0 = "test";
1238 foo!(a);
1239}
1240"#,
1241 r#"
1242macro_rules! foo {($i:ident) => {$i} }
1243fn main() {
1244 let b = "test";
1245 foo!(b);
1246}
1247"#,
1248 );
1249 }
1250
1251 #[test]
1252 fn test_rename_for_macro_args_rev() {
1253 check(
1254 "b",
1255 r#"
1256macro_rules! foo {($i:ident) => {$i} }
1257fn main() {
1258 let a = "test";
1259 foo!(a$0);
1260}
1261"#,
1262 r#"
1263macro_rules! foo {($i:ident) => {$i} }
1264fn main() {
1265 let b = "test";
1266 foo!(b);
1267}
1268"#,
1269 );
1270 }
1271
1272 #[test]
1273 fn test_rename_for_macro_define_fn() {
1274 check(
1275 "bar",
1276 r#"
1277macro_rules! define_fn {($id:ident) => { fn $id{} }}
1278define_fn!(foo);
1279fn main() {
1280 fo$0o();
1281}
1282"#,
1283 r#"
1284macro_rules! define_fn {($id:ident) => { fn $id{} }}
1285define_fn!(bar);
1286fn main() {
1287 bar();
1288}
1289"#,
1290 );
1291 }
1292
1293 #[test]
1294 fn test_rename_for_macro_define_fn_rev() {
1295 check(
1296 "bar",
1297 r#"
1298macro_rules! define_fn {($id:ident) => { fn $id{} }}
1299define_fn!(fo$0o);
1300fn main() {
1301 foo();
1302}
1303"#,
1304 r#"
1305macro_rules! define_fn {($id:ident) => { fn $id{} }}
1306define_fn!(bar);
1307fn main() {
1308 bar();
1309}
1310"#,
1311 );
1312 }
1313
1314 #[test]
1315 fn test_rename_for_param_inside() {
1316 check("j", r#"fn foo(i : u32) -> u32 { i$0 }"#, r#"fn foo(j : u32) -> u32 { j }"#);
1317 }
1318
1319 #[test]
1320 fn test_rename_refs_for_fn_param() {
1321 check("j", r#"fn foo(i$0 : u32) -> u32 { i }"#, r#"fn foo(j : u32) -> u32 { j }"#);
1322 }
1323
1324 #[test]
1325 fn test_rename_for_mut_param() {
1326 check("j", r#"fn foo(mut i$0 : u32) -> u32 { i }"#, r#"fn foo(mut j : u32) -> u32 { j }"#);
1327 }
1328
1329 #[test]
1330 fn test_rename_struct_field() {
1331 check(
1332 "foo",
1333 r#"
1334struct Foo { field$0: i32 }
1335
1336impl Foo {
1337 fn new(i: i32) -> Self {
1338 Self { field: i }
1339 }
1340}
1341"#,
1342 r#"
1343struct Foo { foo: i32 }
1344
1345impl Foo {
1346 fn new(i: i32) -> Self {
1347 Self { foo: i }
1348 }
1349}
1350"#,
1351 );
1352 }
1353
1354 #[test]
1355 fn test_rename_field_in_field_shorthand() {
1356 cov_mark::check!(test_rename_field_in_field_shorthand);
1357 check(
1358 "field",
1359 r#"
1360struct Foo { foo$0: i32 }
1361
1362impl Foo {
1363 fn foo(foo: i32) {
1364 Self { foo };
1365 }
1366}
1367"#,
1368 r#"
1369struct Foo { field: i32 }
1370
1371impl Foo {
1372 fn foo(foo: i32) {
1373 Self { field: foo };
1374 }
1375}
1376"#,
1377 );
1378 }
1379
1380 #[test]
1381 fn test_rename_local_in_field_shorthand() {
1382 cov_mark::check!(test_rename_local_in_field_shorthand);
1383 check(
1384 "j",
1385 r#"
1386struct Foo { i: i32 }
1387
1388impl Foo {
1389 fn new(i$0: i32) -> Self {
1390 Self { i }
1391 }
1392}
1393"#,
1394 r#"
1395struct Foo { i: i32 }
1396
1397impl Foo {
1398 fn new(j: i32) -> Self {
1399 Self { i: j }
1400 }
1401}
1402"#,
1403 );
1404 }
1405
1406 #[test]
1407 fn test_field_shorthand_correct_struct() {
1408 check(
1409 "j",
1410 r#"
1411struct Foo { i$0: i32 }
1412struct Bar { i: i32 }
1413
1414impl Bar {
1415 fn new(i: i32) -> Self {
1416 Self { i }
1417 }
1418}
1419"#,
1420 r#"
1421struct Foo { j: i32 }
1422struct Bar { i: i32 }
1423
1424impl Bar {
1425 fn new(i: i32) -> Self {
1426 Self { i }
1427 }
1428}
1429"#,
1430 );
1431 }
1432
1433 #[test]
1434 fn test_shadow_local_for_struct_shorthand() {
1435 check(
1436 "j",
1437 r#"
1438struct Foo { i: i32 }
1439
1440fn baz(i$0: i32) -> Self {
1441 let x = Foo { i };
1442 {
1443 let i = 0;
1444 Foo { i }
1445 }
1446}
1447"#,
1448 r#"
1449struct Foo { i: i32 }
1450
1451fn baz(j: i32) -> Self {
1452 let x = Foo { i: j };
1453 {
1454 let i = 0;
1455 Foo { i }
1456 }
1457}
1458"#,
1459 );
1460 }
1461
1462 #[test]
1463 fn test_rename_mod() {
1464 check_expect(
1465 "foo2",
1466 r#"
1467//- /lib.rs
1468mod bar;
1469
1470//- /bar.rs
1471mod foo$0;
1472
1473//- /bar/foo.rs
1474// empty
1475"#,
1476 expect![[r#"
1477 source_file_edits: [
1478 (
1479 FileId(
1480 1,
1481 ),
1482 [
1483 Indel {
1484 insert: "foo2",
1485 delete: 4..7,
1486 },
1487 ],
1488 ),
1489 ]
1490 file_system_edits: [
1491 MoveFile {
1492 src: FileId(
1493 2,
1494 ),
1495 dst: AnchoredPathBuf {
1496 anchor: FileId(
1497 2,
1498 ),
1499 path: "foo2.rs",
1500 },
1501 },
1502 ]
1503 "#]],
1504 );
1505 }
1506
1507 #[test]
1508 fn test_rename_mod_in_use_tree() {
1509 check_expect(
1510 "quux",
1511 r#"
1512//- /main.rs
1513pub mod foo;
1514pub mod bar;
1515fn main() {}
1516
1517//- /foo.rs
1518pub struct FooContent;
1519
1520//- /bar.rs
1521use crate::foo$0::FooContent;
1522"#,
1523 expect![[r#"
1524 source_file_edits: [
1525 (
1526 FileId(
1527 0,
1528 ),
1529 [
1530 Indel {
1531 insert: "quux",
1532 delete: 8..11,
1533 },
1534 ],
1535 ),
1536 (
1537 FileId(
1538 2,
1539 ),
1540 [
1541 Indel {
1542 insert: "quux",
1543 delete: 11..14,
1544 },
1545 ],
1546 ),
1547 ]
1548 file_system_edits: [
1549 MoveFile {
1550 src: FileId(
1551 1,
1552 ),
1553 dst: AnchoredPathBuf {
1554 anchor: FileId(
1555 1,
1556 ),
1557 path: "quux.rs",
1558 },
1559 },
1560 ]
1561 "#]],
1562 );
1563 }
1564
1565 #[test]
1566 fn test_rename_mod_in_dir() {
1567 check_expect(
1568 "foo2",
1569 r#"
1570//- /lib.rs
1571mod fo$0o;
1572//- /foo/mod.rs
1573// empty
1574"#,
1575 expect![[r#"
1576 source_file_edits: [
1577 (
1578 FileId(
1579 0,
1580 ),
1581 [
1582 Indel {
1583 insert: "foo2",
1584 delete: 4..7,
1585 },
1586 ],
1587 ),
1588 ]
1589 file_system_edits: [
1590 MoveDir {
1591 src: AnchoredPathBuf {
1592 anchor: FileId(
1593 1,
1594 ),
1595 path: "../foo",
1596 },
1597 src_id: FileId(
1598 1,
1599 ),
1600 dst: AnchoredPathBuf {
1601 anchor: FileId(
1602 1,
1603 ),
1604 path: "../foo2",
1605 },
1606 },
1607 ]
1608 "#]],
1609 );
1610 }
1611
1612 #[test]
1613 fn test_rename_unusually_nested_mod() {
1614 check_expect(
1615 "bar",
1616 r#"
1617//- /lib.rs
1618mod outer { mod fo$0o; }
1619
1620//- /outer/foo.rs
1621// empty
1622"#,
1623 expect![[r#"
1624 source_file_edits: [
1625 (
1626 FileId(
1627 0,
1628 ),
1629 [
1630 Indel {
1631 insert: "bar",
1632 delete: 16..19,
1633 },
1634 ],
1635 ),
1636 ]
1637 file_system_edits: [
1638 MoveFile {
1639 src: FileId(
1640 1,
1641 ),
1642 dst: AnchoredPathBuf {
1643 anchor: FileId(
1644 1,
1645 ),
1646 path: "bar.rs",
1647 },
1648 },
1649 ]
1650 "#]],
1651 );
1652 }
1653
1654 #[test]
1655 fn test_module_rename_in_path() {
1656 check(
1657 "baz",
1658 r#"
1659mod $0foo {
1660 pub use self::bar as qux;
1661 pub fn bar() {}
1662}
1663
1664fn main() { foo::bar(); }
1665"#,
1666 r#"
1667mod baz {
1668 pub use self::bar as qux;
1669 pub fn bar() {}
1670}
1671
1672fn main() { baz::bar(); }
1673"#,
1674 );
1675 }
1676
1677 #[test]
1678 fn test_rename_mod_filename_and_path() {
1679 check_expect(
1680 "foo2",
1681 r#"
1682//- /lib.rs
1683mod bar;
1684fn f() {
1685 bar::foo::fun()
1686}
1687
1688//- /bar.rs
1689pub mod foo$0;
1690
1691//- /bar/foo.rs
1692// pub fn fun() {}
1693"#,
1694 expect![[r#"
1695 source_file_edits: [
1696 (
1697 FileId(
1698 0,
1699 ),
1700 [
1701 Indel {
1702 insert: "foo2",
1703 delete: 27..30,
1704 },
1705 ],
1706 ),
1707 (
1708 FileId(
1709 1,
1710 ),
1711 [
1712 Indel {
1713 insert: "foo2",
1714 delete: 8..11,
1715 },
1716 ],
1717 ),
1718 ]
1719 file_system_edits: [
1720 MoveFile {
1721 src: FileId(
1722 2,
1723 ),
1724 dst: AnchoredPathBuf {
1725 anchor: FileId(
1726 2,
1727 ),
1728 path: "foo2.rs",
1729 },
1730 },
1731 ]
1732 "#]],
1733 );
1734 }
1735
1736 #[test]
1737 fn test_rename_mod_recursive() {
1738 check_expect(
1739 "foo2",
1740 r#"
1741//- /lib.rs
1742mod foo$0;
1743
1744//- /foo.rs
1745mod bar;
1746mod corge;
1747
1748//- /foo/bar.rs
1749mod qux;
1750
1751//- /foo/bar/qux.rs
1752mod quux;
1753
1754//- /foo/bar/qux/quux/mod.rs
1755// empty
1756
1757//- /foo/corge.rs
1758// empty
1759"#,
1760 expect![[r#"
1761 source_file_edits: [
1762 (
1763 FileId(
1764 0,
1765 ),
1766 [
1767 Indel {
1768 insert: "foo2",
1769 delete: 4..7,
1770 },
1771 ],
1772 ),
1773 ]
1774 file_system_edits: [
1775 MoveFile {
1776 src: FileId(
1777 1,
1778 ),
1779 dst: AnchoredPathBuf {
1780 anchor: FileId(
1781 1,
1782 ),
1783 path: "foo2.rs",
1784 },
1785 },
1786 MoveDir {
1787 src: AnchoredPathBuf {
1788 anchor: FileId(
1789 1,
1790 ),
1791 path: "foo",
1792 },
1793 src_id: FileId(
1794 1,
1795 ),
1796 dst: AnchoredPathBuf {
1797 anchor: FileId(
1798 1,
1799 ),
1800 path: "foo2",
1801 },
1802 },
1803 ]
1804 "#]],
1805 )
1806 }
1807 #[test]
1808 fn test_rename_mod_ref_by_super() {
1809 check(
1810 "baz",
1811 r#"
1812 mod $0foo {
1813 struct X;
1814
1815 mod bar {
1816 use super::X;
1817 }
1818 }
1819 "#,
1820 r#"
1821 mod baz {
1822 struct X;
1823
1824 mod bar {
1825 use super::X;
1826 }
1827 }
1828 "#,
1829 )
1830 }
1831
1832 #[test]
1833 fn test_rename_mod_in_macro() {
1834 check(
1835 "bar",
1836 r#"
1837//- /foo.rs
1838
1839//- /lib.rs
1840macro_rules! submodule {
1841 ($name:ident) => {
1842 mod $name;
1843 };
1844}
1845
1846submodule!($0foo);
1847"#,
1848 r#"
1849macro_rules! submodule {
1850 ($name:ident) => {
1851 mod $name;
1852 };
1853}
1854
1855submodule!(bar);
1856"#,
1857 )
1858 }
1859
1860 #[test]
1861 fn test_rename_mod_for_crate_root() {
1862 check_expect_will_rename_file(
1863 "main",
1864 r#"
1865//- /lib.rs
1866use crate::foo as bar;
1867fn foo() {}
1868mod bar$0;
1869"#,
1870 expect![[r#"
1871 source_file_edits: []
1872 file_system_edits: []
1873 "#]],
1874 )
1875 }
1876
1877 #[test]
1878 fn test_rename_mod_to_raw_ident() {
1879 check_expect(
1880 "r#fn",
1881 r#"
1882//- /lib.rs
1883mod foo$0;
1884
1885fn main() { foo::bar::baz(); }
1886
1887//- /foo.rs
1888pub mod bar;
1889
1890//- /foo/bar.rs
1891pub fn baz() {}
1892"#,
1893 expect![[r#"
1894 source_file_edits: [
1895 (
1896 FileId(
1897 0,
1898 ),
1899 [
1900 Indel {
1901 insert: "r#fn",
1902 delete: 4..7,
1903 },
1904 Indel {
1905 insert: "r#fn",
1906 delete: 22..25,
1907 },
1908 ],
1909 ),
1910 ]
1911 file_system_edits: [
1912 MoveFile {
1913 src: FileId(
1914 1,
1915 ),
1916 dst: AnchoredPathBuf {
1917 anchor: FileId(
1918 1,
1919 ),
1920 path: "fn.rs",
1921 },
1922 },
1923 MoveDir {
1924 src: AnchoredPathBuf {
1925 anchor: FileId(
1926 1,
1927 ),
1928 path: "foo",
1929 },
1930 src_id: FileId(
1931 1,
1932 ),
1933 dst: AnchoredPathBuf {
1934 anchor: FileId(
1935 1,
1936 ),
1937 path: "fn",
1938 },
1939 },
1940 ]
1941 "#]],
1942 );
1943 }
1944
1945 #[test]
1946 fn test_rename_mod_from_raw_ident() {
1947 check_expect(
1948 "foo",
1949 r#"
1950//- /lib.rs
1951mod r#fn$0;
1952
1953fn main() { r#fn::bar::baz(); }
1954
1955//- /fn.rs
1956pub mod bar;
1957
1958//- /fn/bar.rs
1959pub fn baz() {}
1960"#,
1961 expect![[r#"
1962 source_file_edits: [
1963 (
1964 FileId(
1965 0,
1966 ),
1967 [
1968 Indel {
1969 insert: "foo",
1970 delete: 4..8,
1971 },
1972 Indel {
1973 insert: "foo",
1974 delete: 23..27,
1975 },
1976 ],
1977 ),
1978 ]
1979 file_system_edits: [
1980 MoveFile {
1981 src: FileId(
1982 1,
1983 ),
1984 dst: AnchoredPathBuf {
1985 anchor: FileId(
1986 1,
1987 ),
1988 path: "foo.rs",
1989 },
1990 },
1991 MoveDir {
1992 src: AnchoredPathBuf {
1993 anchor: FileId(
1994 1,
1995 ),
1996 path: "fn",
1997 },
1998 src_id: FileId(
1999 1,
2000 ),
2001 dst: AnchoredPathBuf {
2002 anchor: FileId(
2003 1,
2004 ),
2005 path: "foo",
2006 },
2007 },
2008 ]
2009 "#]],
2010 );
2011 }
2012
2013 #[test]
2014 fn test_rename_each_usage_gets_appropriate_rawness() {
2015 check_expect(
2016 "dyn",
2017 r#"
2018//- /a.rs crate:a edition:2015
2019pub fn foo() {}
2020
2021//- /b.rs crate:b edition:2018 deps:a new_source_root:local
2022fn bar() {
2023 a::foo$0();
2024}
2025 "#,
2026 expect![[r#"
2027 source_file_edits: [
2028 (
2029 FileId(
2030 0,
2031 ),
2032 [
2033 Indel {
2034 insert: "dyn",
2035 delete: 7..10,
2036 },
2037 ],
2038 ),
2039 (
2040 FileId(
2041 1,
2042 ),
2043 [
2044 Indel {
2045 insert: "r#dyn",
2046 delete: 18..21,
2047 },
2048 ],
2049 ),
2050 ]
2051 file_system_edits: []
2052 "#]],
2053 );
2054
2055 check_expect(
2056 "dyn",
2057 r#"
2058//- /a.rs crate:a edition:2018
2059pub fn foo() {}
2060
2061//- /b.rs crate:b edition:2015 deps:a new_source_root:local
2062fn bar() {
2063 a::foo$0();
2064}
2065 "#,
2066 expect![[r#"
2067 source_file_edits: [
2068 (
2069 FileId(
2070 0,
2071 ),
2072 [
2073 Indel {
2074 insert: "r#dyn",
2075 delete: 7..10,
2076 },
2077 ],
2078 ),
2079 (
2080 FileId(
2081 1,
2082 ),
2083 [
2084 Indel {
2085 insert: "dyn",
2086 delete: 18..21,
2087 },
2088 ],
2089 ),
2090 ]
2091 file_system_edits: []
2092 "#]],
2093 );
2094
2095 check_expect(
2096 "r#dyn",
2097 r#"
2098//- /a.rs crate:a edition:2018
2099pub fn foo$0() {}
2100
2101//- /b.rs crate:b edition:2015 deps:a new_source_root:local
2102fn bar() {
2103 a::foo();
2104}
2105 "#,
2106 expect![[r#"
2107 source_file_edits: [
2108 (
2109 FileId(
2110 0,
2111 ),
2112 [
2113 Indel {
2114 insert: "r#dyn",
2115 delete: 7..10,
2116 },
2117 ],
2118 ),
2119 (
2120 FileId(
2121 1,
2122 ),
2123 [
2124 Indel {
2125 insert: "dyn",
2126 delete: 18..21,
2127 },
2128 ],
2129 ),
2130 ]
2131 file_system_edits: []
2132 "#]],
2133 );
2134 }
2135
2136 #[test]
2137 fn rename_raw_identifier() {
2138 check_expect(
2139 "abc",
2140 r#"
2141//- /a.rs crate:a edition:2015
2142pub fn dyn() {}
2143
2144fn foo() {
2145 dyn$0();
2146}
2147
2148//- /b.rs crate:b edition:2018 deps:a new_source_root:local
2149fn bar() {
2150 a::r#dyn();
2151}
2152 "#,
2153 expect![[r#"
2154 source_file_edits: [
2155 (
2156 FileId(
2157 0,
2158 ),
2159 [
2160 Indel {
2161 insert: "abc",
2162 delete: 7..10,
2163 },
2164 Indel {
2165 insert: "abc",
2166 delete: 32..35,
2167 },
2168 ],
2169 ),
2170 (
2171 FileId(
2172 1,
2173 ),
2174 [
2175 Indel {
2176 insert: "abc",
2177 delete: 18..23,
2178 },
2179 ],
2180 ),
2181 ]
2182 file_system_edits: []
2183 "#]],
2184 );
2185
2186 check_expect(
2187 "abc",
2188 r#"
2189//- /a.rs crate:a edition:2018
2190pub fn r#dyn() {}
2191
2192fn foo() {
2193 r#dyn$0();
2194}
2195
2196//- /b.rs crate:b edition:2015 deps:a new_source_root:local
2197fn bar() {
2198 a::dyn();
2199}
2200 "#,
2201 expect![[r#"
2202 source_file_edits: [
2203 (
2204 FileId(
2205 0,
2206 ),
2207 [
2208 Indel {
2209 insert: "abc",
2210 delete: 7..12,
2211 },
2212 Indel {
2213 insert: "abc",
2214 delete: 34..39,
2215 },
2216 ],
2217 ),
2218 (
2219 FileId(
2220 1,
2221 ),
2222 [
2223 Indel {
2224 insert: "abc",
2225 delete: 18..21,
2226 },
2227 ],
2228 ),
2229 ]
2230 file_system_edits: []
2231 "#]],
2232 );
2233 }
2234
2235 #[test]
2236 fn test_enum_variant_from_module_1() {
2237 cov_mark::check!(rename_non_local);
2238 check(
2239 "Baz",
2240 r#"
2241mod foo {
2242 pub enum Foo { Bar$0 }
2243}
2244
2245fn func(f: foo::Foo) {
2246 match f {
2247 foo::Foo::Bar => {}
2248 }
2249}
2250"#,
2251 r#"
2252mod foo {
2253 pub enum Foo { Baz }
2254}
2255
2256fn func(f: foo::Foo) {
2257 match f {
2258 foo::Foo::Baz => {}
2259 }
2260}
2261"#,
2262 );
2263 }
2264
2265 #[test]
2266 fn test_enum_variant_from_module_2() {
2267 check(
2268 "baz",
2269 r#"
2270mod foo {
2271 pub struct Foo { pub bar$0: uint }
2272}
2273
2274fn foo(f: foo::Foo) {
2275 let _ = f.bar;
2276}
2277"#,
2278 r#"
2279mod foo {
2280 pub struct Foo { pub baz: uint }
2281}
2282
2283fn foo(f: foo::Foo) {
2284 let _ = f.baz;
2285}
2286"#,
2287 );
2288 }
2289
2290 #[test]
2291 fn test_parameter_to_self() {
2292 cov_mark::check!(rename_to_self);
2293 check(
2294 "self",
2295 r#"
2296struct Foo { i: i32 }
2297
2298impl Foo {
2299 fn f(foo$0: &mut Foo) -> i32 {
2300 foo.i
2301 }
2302}
2303"#,
2304 r#"
2305struct Foo { i: i32 }
2306
2307impl Foo {
2308 fn f(&mut self) -> i32 {
2309 self.i
2310 }
2311}
2312"#,
2313 );
2314 check(
2315 "self",
2316 r#"
2317struct Foo { i: i32 }
2318
2319impl Foo {
2320 fn f(foo$0: Foo) -> i32 {
2321 foo.i
2322 }
2323}
2324"#,
2325 r#"
2326struct Foo { i: i32 }
2327
2328impl Foo {
2329 fn f(self) -> i32 {
2330 self.i
2331 }
2332}
2333"#,
2334 );
2335 }
2336
2337 #[test]
2338 fn test_parameter_to_self_error_no_impl() {
2339 check(
2340 "self",
2341 r#"
2342struct Foo { i: i32 }
2343
2344fn f(foo$0: &mut Foo) -> i32 {
2345 foo.i
2346}
2347"#,
2348 "error: Cannot rename parameter to self for free function",
2349 );
2350 check(
2351 "self",
2352 r#"
2353struct Foo { i: i32 }
2354struct Bar;
2355
2356impl Bar {
2357 fn f(foo$0: &mut Foo) -> i32 {
2358 foo.i
2359 }
2360}
2361"#,
2362 "error: Parameter type differs from impl block type",
2363 );
2364 }
2365
2366 #[test]
2367 fn test_parameter_to_self_error_not_first() {
2368 check(
2369 "self",
2370 r#"
2371struct Foo { i: i32 }
2372impl Foo {
2373 fn f(x: (), foo$0: &mut Foo) -> i32 {
2374 foo.i
2375 }
2376}
2377"#,
2378 "error: Only the first parameter may be renamed to self",
2379 );
2380 }
2381
2382 #[test]
2383 fn test_parameter_to_self_impl_ref() {
2384 check(
2385 "self",
2386 r#"
2387struct Foo { i: i32 }
2388impl &Foo {
2389 fn f(foo$0: &Foo) -> i32 {
2390 foo.i
2391 }
2392}
2393"#,
2394 r#"
2395struct Foo { i: i32 }
2396impl &Foo {
2397 fn f(self) -> i32 {
2398 self.i
2399 }
2400}
2401"#,
2402 );
2403 }
2404
2405 #[test]
2406 fn test_self_to_parameter() {
2407 check(
2408 "foo",
2409 r#"
2410struct Foo { i: i32 }
2411
2412impl Foo {
2413 fn f(&mut $0self) -> i32 {
2414 self.i
2415 }
2416}
2417"#,
2418 r#"
2419struct Foo { i: i32 }
2420
2421impl Foo {
2422 fn f(foo: &mut Self) -> i32 {
2423 foo.i
2424 }
2425}
2426"#,
2427 );
2428 }
2429
2430 #[test]
2431 fn test_owned_self_to_parameter() {
2432 cov_mark::check!(rename_self_to_param);
2433 check(
2434 "foo",
2435 r#"
2436struct Foo { i: i32 }
2437
2438impl Foo {
2439 fn f($0self) -> i32 {
2440 self.i
2441 }
2442}
2443"#,
2444 r#"
2445struct Foo { i: i32 }
2446
2447impl Foo {
2448 fn f(foo: Self) -> i32 {
2449 foo.i
2450 }
2451}
2452"#,
2453 );
2454 }
2455
2456 #[test]
2457 fn test_owned_self_to_parameter_with_lifetime() {
2458 cov_mark::check!(rename_self_to_param);
2459 check(
2460 "foo",
2461 r#"
2462struct Foo<'a> { i: &'a i32 }
2463
2464impl<'a> Foo<'a> {
2465 fn f(&'a $0self) -> i32 {
2466 self.i
2467 }
2468}
2469"#,
2470 r#"
2471struct Foo<'a> { i: &'a i32 }
2472
2473impl<'a> Foo<'a> {
2474 fn f(foo: &'a Self) -> i32 {
2475 foo.i
2476 }
2477}
2478"#,
2479 );
2480 }
2481
2482 #[test]
2483 fn test_self_outside_of_methods() {
2484 check(
2485 "foo",
2486 r#"
2487fn f($0self) -> i32 {
2488 self.i
2489}
2490"#,
2491 r#"
2492fn f(foo: Self) -> i32 {
2493 foo.i
2494}
2495"#,
2496 );
2497 }
2498
2499 #[test]
2500 fn no_type_value_ns_confuse() {
2501 check(
2503 "bar",
2504 r#"
2505struct foo {}
2506fn f(foo$0: i32) -> i32 {
2507 use foo as _;
2508}
2509"#,
2510 r#"
2511struct foo {}
2512fn f(bar: i32) -> i32 {
2513 use foo as _;
2514}
2515"#,
2516 );
2517 }
2518
2519 #[test]
2520 fn test_self_in_path_to_parameter() {
2521 check(
2522 "foo",
2523 r#"
2524struct Foo { i: i32 }
2525
2526impl Foo {
2527 fn f(&self) -> i32 {
2528 let self_var = 1;
2529 self$0.i
2530 }
2531}
2532"#,
2533 r#"
2534struct Foo { i: i32 }
2535
2536impl Foo {
2537 fn f(foo: &Self) -> i32 {
2538 let self_var = 1;
2539 foo.i
2540 }
2541}
2542"#,
2543 );
2544 }
2545
2546 #[test]
2547 fn test_rename_field_put_init_shorthand() {
2548 cov_mark::check!(test_rename_field_put_init_shorthand);
2549 check(
2550 "bar",
2551 r#"
2552struct Foo { i$0: i32 }
2553
2554fn foo(bar: i32) -> Foo {
2555 Foo { i: bar }
2556}
2557"#,
2558 r#"
2559struct Foo { bar: i32 }
2560
2561fn foo(bar: i32) -> Foo {
2562 Foo { bar }
2563}
2564"#,
2565 );
2566 }
2567
2568 #[test]
2569 fn test_rename_local_simple() {
2570 check(
2571 "i",
2572 r#"
2573fn foo(bar$0: i32) -> i32 {
2574 bar
2575}
2576"#,
2577 r#"
2578fn foo(i: i32) -> i32 {
2579 i
2580}
2581"#,
2582 );
2583 }
2584
2585 #[test]
2586 fn test_rename_local_put_init_shorthand() {
2587 cov_mark::check!(test_rename_local_put_init_shorthand);
2588 check(
2589 "i",
2590 r#"
2591struct Foo { i: i32 }
2592
2593fn foo(bar$0: i32) -> Foo {
2594 Foo { i: bar }
2595}
2596"#,
2597 r#"
2598struct Foo { i: i32 }
2599
2600fn foo(i: i32) -> Foo {
2601 Foo { i }
2602}
2603"#,
2604 );
2605 }
2606
2607 #[test]
2608 fn test_struct_field_pat_into_shorthand() {
2609 cov_mark::check!(test_rename_field_put_init_shorthand_pat);
2610 check(
2611 "baz",
2612 r#"
2613struct Foo { i$0: i32 }
2614
2615fn foo(foo: Foo) {
2616 let Foo { i: ref baz @ qux } = foo;
2617 let _ = qux;
2618}
2619"#,
2620 r#"
2621struct Foo { baz: i32 }
2622
2623fn foo(foo: Foo) {
2624 let Foo { baz: ref baz @ qux } = foo;
2625 let _ = qux;
2626}
2627"#,
2628 );
2629 check(
2630 "baz",
2631 r#"
2632struct Foo { i$0: i32 }
2633
2634fn foo(foo: Foo) {
2635 let Foo { i: ref baz } = foo;
2636 let _ = qux;
2637}
2638"#,
2639 r#"
2640struct Foo { baz: i32 }
2641
2642fn foo(foo: Foo) {
2643 let Foo { ref baz } = foo;
2644 let _ = qux;
2645}
2646"#,
2647 );
2648 }
2649
2650 #[test]
2651 fn test_struct_local_pat_into_shorthand() {
2652 cov_mark::check!(test_rename_local_put_init_shorthand_pat);
2653 check(
2654 "field",
2655 r#"
2656struct Foo { field: i32 }
2657
2658fn foo(foo: Foo) {
2659 let Foo { field: qux$0 } = foo;
2660 let _ = qux;
2661}
2662"#,
2663 r#"
2664struct Foo { field: i32 }
2665
2666fn foo(foo: Foo) {
2667 let Foo { field } = foo;
2668 let _ = field;
2669}
2670"#,
2671 );
2672 check(
2673 "field",
2674 r#"
2675struct Foo { field: i32 }
2676
2677fn foo(foo: Foo) {
2678 let Foo { field: x @ qux$0 } = foo;
2679 let _ = qux;
2680}
2681"#,
2682 r#"
2683struct Foo { field: i32 }
2684
2685fn foo(foo: Foo) {
2686 let Foo { field: x @ field } = foo;
2687 let _ = field;
2688}
2689"#,
2690 );
2691 }
2692
2693 #[test]
2694 fn test_rename_binding_in_destructure_pat() {
2695 let expected_fixture = r#"
2696struct Foo {
2697 i: i32,
2698}
2699
2700fn foo(foo: Foo) {
2701 let Foo { i: bar } = foo;
2702 let _ = bar;
2703}
2704"#;
2705 check(
2706 "bar",
2707 r#"
2708struct Foo {
2709 i: i32,
2710}
2711
2712fn foo(foo: Foo) {
2713 let Foo { i: b } = foo;
2714 let _ = b$0;
2715}
2716"#,
2717 expected_fixture,
2718 );
2719 check(
2720 "bar",
2721 r#"
2722struct Foo {
2723 i: i32,
2724}
2725
2726fn foo(foo: Foo) {
2727 let Foo { i } = foo;
2728 let _ = i$0;
2729}
2730"#,
2731 expected_fixture,
2732 );
2733 }
2734
2735 #[test]
2736 fn test_rename_binding_in_destructure_param_pat() {
2737 check(
2738 "bar",
2739 r#"
2740struct Foo {
2741 i: i32
2742}
2743
2744fn foo(Foo { i }: Foo) -> i32 {
2745 i$0
2746}
2747"#,
2748 r#"
2749struct Foo {
2750 i: i32
2751}
2752
2753fn foo(Foo { i: bar }: Foo) -> i32 {
2754 bar
2755}
2756"#,
2757 )
2758 }
2759
2760 #[test]
2761 fn test_struct_field_complex_ident_pat() {
2762 cov_mark::check!(rename_record_pat_field_name_split);
2763 check(
2764 "baz",
2765 r#"
2766struct Foo { i$0: i32 }
2767
2768fn foo(foo: Foo) {
2769 let Foo { ref i } = foo;
2770}
2771"#,
2772 r#"
2773struct Foo { baz: i32 }
2774
2775fn foo(foo: Foo) {
2776 let Foo { baz: ref i } = foo;
2777}
2778"#,
2779 );
2780 }
2781
2782 #[test]
2783 fn test_rename_lifetimes() {
2784 check(
2785 "'yeeee",
2786 r#"
2787trait Foo<'a> {
2788 fn foo() -> &'a ();
2789}
2790impl<'a> Foo<'a> for &'a () {
2791 fn foo() -> &'a$0 () {
2792 unimplemented!()
2793 }
2794}
2795"#,
2796 r#"
2797trait Foo<'a> {
2798 fn foo() -> &'a ();
2799}
2800impl<'yeeee> Foo<'yeeee> for &'yeeee () {
2801 fn foo() -> &'yeeee () {
2802 unimplemented!()
2803 }
2804}
2805"#,
2806 )
2807 }
2808
2809 #[test]
2810 fn test_rename_bind_pat() {
2811 check(
2812 "new_name",
2813 r#"
2814fn main() {
2815 enum CustomOption<T> {
2816 None,
2817 Some(T),
2818 }
2819
2820 let test_variable = CustomOption::Some(22);
2821
2822 match test_variable {
2823 CustomOption::Some(foo$0) if foo == 11 => {}
2824 _ => (),
2825 }
2826}"#,
2827 r#"
2828fn main() {
2829 enum CustomOption<T> {
2830 None,
2831 Some(T),
2832 }
2833
2834 let test_variable = CustomOption::Some(22);
2835
2836 match test_variable {
2837 CustomOption::Some(new_name) if new_name == 11 => {}
2838 _ => (),
2839 }
2840}"#,
2841 );
2842 }
2843
2844 #[test]
2845 fn test_rename_label() {
2846 check(
2847 "'foo",
2848 r#"
2849fn foo<'a>() -> &'a () {
2850 'a: {
2851 'b: loop {
2852 break 'a$0;
2853 }
2854 }
2855}
2856"#,
2857 r#"
2858fn foo<'a>() -> &'a () {
2859 'foo: {
2860 'b: loop {
2861 break 'foo;
2862 }
2863 }
2864}
2865"#,
2866 )
2867 }
2868
2869 #[test]
2870 fn test_rename_label_new_name_without_apostrophe() {
2871 check(
2872 "foo",
2873 r#"
2874fn main() {
2875 'outer$0: loop {
2876 'inner: loop {
2877 break 'outer;
2878 }
2879 }
2880}
2881 "#,
2882 r#"
2883fn main() {
2884 'foo: loop {
2885 'inner: loop {
2886 break 'foo;
2887 }
2888 }
2889}
2890 "#,
2891 );
2892 }
2893
2894 #[test]
2895 fn test_self_to_self() {
2896 cov_mark::check!(rename_self_to_self);
2897 check(
2898 "self",
2899 r#"
2900struct Foo;
2901impl Foo {
2902 fn foo(self$0) {}
2903}
2904"#,
2905 r#"
2906struct Foo;
2907impl Foo {
2908 fn foo(self) {}
2909}
2910"#,
2911 )
2912 }
2913
2914 #[test]
2915 fn test_rename_field_in_pat_in_macro_doesnt_shorthand() {
2916 check(
2918 "baz",
2919 r#"
2920macro_rules! foo {
2921 ($pattern:pat) => {
2922 let $pattern = loop {};
2923 };
2924}
2925struct Foo {
2926 bar$0: u32,
2927}
2928fn foo() {
2929 foo!(Foo { bar: baz });
2930}
2931"#,
2932 r#"
2933macro_rules! foo {
2934 ($pattern:pat) => {
2935 let $pattern = loop {};
2936 };
2937}
2938struct Foo {
2939 baz: u32,
2940}
2941fn foo() {
2942 foo!(Foo { baz: baz });
2943}
2944"#,
2945 )
2946 }
2947
2948 #[test]
2949 fn test_rename_tuple_field() {
2950 check(
2951 "foo",
2952 r#"
2953struct Foo(i32);
2954
2955fn baz() {
2956 let mut x = Foo(4);
2957 x.0$0 = 5;
2958}
2959"#,
2960 "error: No references found at position",
2961 );
2962 }
2963
2964 #[test]
2965 fn test_rename_builtin() {
2966 check(
2967 "foo",
2968 r#"
2969fn foo() {
2970 let x: i32$0 = 0;
2971}
2972"#,
2973 "error: Cannot rename builtin type",
2974 );
2975 }
2976
2977 #[test]
2978 fn test_rename_self() {
2979 check(
2980 "foo",
2981 r#"
2982struct Foo {}
2983
2984impl Foo {
2985 fn foo(self) -> Self$0 {
2986 self
2987 }
2988}
2989"#,
2990 "error: No references found at position",
2991 );
2992 }
2993
2994 #[test]
2995 fn test_rename_ignores_self_ty() {
2996 check(
2997 "Fo0",
2998 r#"
2999struct $0Foo;
3000
3001impl Foo where Self: {}
3002"#,
3003 r#"
3004struct Fo0;
3005
3006impl Fo0 where Self: {}
3007"#,
3008 );
3009 }
3010
3011 #[test]
3012 fn test_rename_fails_on_aliases() {
3013 check(
3014 "Baz",
3015 r#"
3016struct Foo;
3017use Foo as Bar$0;
3018"#,
3019 "error: Renaming aliases is currently unsupported",
3020 );
3021 check(
3022 "Baz",
3023 r#"
3024struct Foo;
3025use Foo as Bar;
3026use Bar$0;
3027"#,
3028 "error: Renaming aliases is currently unsupported",
3029 );
3030 }
3031
3032 #[test]
3033 fn test_rename_trait_method() {
3034 let res = r"
3035trait Foo {
3036 fn foo(&self) {
3037 self.foo();
3038 }
3039}
3040
3041impl Foo for () {
3042 fn foo(&self) {
3043 self.foo();
3044 }
3045}";
3046 check(
3047 "foo",
3048 r#"
3049trait Foo {
3050 fn bar$0(&self) {
3051 self.bar();
3052 }
3053}
3054
3055impl Foo for () {
3056 fn bar(&self) {
3057 self.bar();
3058 }
3059}"#,
3060 res,
3061 );
3062 check(
3063 "foo",
3064 r#"
3065trait Foo {
3066 fn bar(&self) {
3067 self.bar$0();
3068 }
3069}
3070
3071impl Foo for () {
3072 fn bar(&self) {
3073 self.bar();
3074 }
3075}"#,
3076 res,
3077 );
3078 check(
3079 "foo",
3080 r#"
3081trait Foo {
3082 fn bar(&self) {
3083 self.bar();
3084 }
3085}
3086
3087impl Foo for () {
3088 fn bar$0(&self) {
3089 self.bar();
3090 }
3091}"#,
3092 res,
3093 );
3094 check(
3095 "foo",
3096 r#"
3097trait Foo {
3098 fn bar(&self) {
3099 self.bar();
3100 }
3101}
3102
3103impl Foo for () {
3104 fn bar(&self) {
3105 self.bar$0();
3106 }
3107}"#,
3108 res,
3109 );
3110 }
3111
3112 #[test]
3113 fn test_rename_trait_method_prefix_of_second() {
3114 check(
3115 "qux",
3116 r#"
3117trait Foo {
3118 fn foo$0() {}
3119 fn foobar() {}
3120}
3121"#,
3122 r#"
3123trait Foo {
3124 fn qux() {}
3125 fn foobar() {}
3126}
3127"#,
3128 );
3129 }
3130
3131 #[test]
3132 fn test_rename_trait_const() {
3133 let res = r"
3134trait Foo {
3135 const FOO: ();
3136}
3137
3138impl Foo for () {
3139 const FOO: ();
3140}
3141fn f() { <()>::FOO; }";
3142 check(
3143 "FOO",
3144 r#"
3145trait Foo {
3146 const BAR$0: ();
3147}
3148
3149impl Foo for () {
3150 const BAR: ();
3151}
3152fn f() { <()>::BAR; }"#,
3153 res,
3154 );
3155 check(
3156 "FOO",
3157 r#"
3158trait Foo {
3159 const BAR: ();
3160}
3161
3162impl Foo for () {
3163 const BAR$0: ();
3164}
3165fn f() { <()>::BAR; }"#,
3166 res,
3167 );
3168 check(
3169 "FOO",
3170 r#"
3171trait Foo {
3172 const BAR: ();
3173}
3174
3175impl Foo for () {
3176 const BAR: ();
3177}
3178fn f() { <()>::BAR$0; }"#,
3179 res,
3180 );
3181 }
3182
3183 #[test]
3184 fn defs_from_macros_arent_renamed() {
3185 check(
3186 "lol",
3187 r#"
3188macro_rules! m { () => { fn f() {} } }
3189m!();
3190fn main() { f$0() }
3191"#,
3192 "error: No identifier available to rename",
3193 )
3194 }
3195
3196 #[test]
3197 fn attributed_item() {
3198 check(
3199 "function",
3200 r#"
3201//- proc_macros: identity
3202
3203#[proc_macros::identity]
3204fn func$0() {
3205 func();
3206}
3207"#,
3208 r#"
3209
3210#[proc_macros::identity]
3211fn function() {
3212 function();
3213}
3214"#,
3215 )
3216 }
3217
3218 #[test]
3219 fn in_macro_multi_mapping() {
3220 check(
3221 "a",
3222 r#"
3223fn foo() {
3224 macro_rules! match_ast2 {
3225 ($node:ident {
3226 $( $res:expr, )*
3227 }) => {{
3228 $( if $node { $res } else )*
3229 { loop {} }
3230 }};
3231 }
3232 let $0d = 3;
3233 match_ast2! {
3234 d {
3235 d,
3236 d,
3237 }
3238 };
3239}
3240"#,
3241 r#"
3242fn foo() {
3243 macro_rules! match_ast2 {
3244 ($node:ident {
3245 $( $res:expr, )*
3246 }) => {{
3247 $( if $node { $res } else )*
3248 { loop {} }
3249 }};
3250 }
3251 let a = 3;
3252 match_ast2! {
3253 a {
3254 a,
3255 a,
3256 }
3257 };
3258}
3259"#,
3260 )
3261 }
3262
3263 #[test]
3264 fn rename_multi_local() {
3265 check(
3266 "bar",
3267 r#"
3268fn foo((foo$0 | foo | foo): ()) {
3269 foo;
3270 let foo;
3271}
3272"#,
3273 r#"
3274fn foo((bar | bar | bar): ()) {
3275 bar;
3276 let foo;
3277}
3278"#,
3279 );
3280 check(
3281 "bar",
3282 r#"
3283fn foo((foo | foo$0 | foo): ()) {
3284 foo;
3285 let foo;
3286}
3287"#,
3288 r#"
3289fn foo((bar | bar | bar): ()) {
3290 bar;
3291 let foo;
3292}
3293"#,
3294 );
3295 check(
3296 "bar",
3297 r#"
3298fn foo((foo | foo | foo): ()) {
3299 foo$0;
3300 let foo;
3301}
3302"#,
3303 r#"
3304fn foo((bar | bar | bar): ()) {
3305 bar;
3306 let foo;
3307}
3308"#,
3309 );
3310 }
3311
3312 #[test]
3313 fn regression_13498() {
3314 check(
3315 "Testing",
3316 r"
3317mod foo {
3318 pub struct Test$0;
3319}
3320
3321use foo::Test as Tester;
3322
3323fn main() {
3324 let t = Tester;
3325}
3326",
3327 r"
3328mod foo {
3329 pub struct Testing;
3330}
3331
3332use foo::Testing as Tester;
3333
3334fn main() {
3335 let t = Tester;
3336}
3337",
3338 )
3339 }
3340
3341 #[test]
3342 fn extern_crate() {
3343 check_prepare(
3344 r"
3345//- /lib.rs crate:main deps:foo
3346extern crate foo$0;
3347use foo as qux;
3348//- /foo.rs crate:foo
3349",
3350 expect![[r#"No references found at position"#]],
3351 );
3352 }
3367
3368 #[test]
3369 fn extern_crate_rename() {
3370 check_prepare(
3371 r"
3372//- /lib.rs crate:main deps:foo
3373extern crate foo as qux$0;
3374use qux as frob;
3375//- /foo.rs crate:foo
3376",
3377 expect!["Renaming aliases is currently unsupported"],
3378 );
3379 }
3395
3396 #[test]
3397 fn extern_crate_self() {
3398 check_prepare(
3399 r"
3400extern crate self$0;
3401use self as qux;
3402",
3403 expect!["No references found at position"],
3404 );
3405 }
3418
3419 #[test]
3420 fn extern_crate_self_rename() {
3421 check_prepare(
3422 r"
3423//- /lib.rs crate:main deps:foo
3424extern crate self as qux$0;
3425use qux as frob;
3426//- /foo.rs crate:foo
3427",
3428 expect!["Renaming aliases is currently unsupported"],
3429 );
3430 }
3445
3446 #[test]
3447 fn disallow_renaming_for_non_local_definition() {
3448 check(
3449 "Baz",
3450 r#"
3451//- /lib.rs crate:lib new_source_root:library
3452pub struct S;
3453//- /main.rs crate:main deps:lib new_source_root:local
3454use lib::S;
3455fn main() { let _: S$0; }
3456"#,
3457 "error: Cannot rename a non-local definition",
3458 );
3459 }
3460
3461 #[test]
3462 fn disallow_renaming_for_builtin_macros() {
3463 check(
3464 "Baz",
3465 r#"
3466//- minicore: derive, hash
3467//- /main.rs crate:main
3468use core::hash::Hash;
3469#[derive(H$0ash)]
3470struct A;
3471 "#,
3472 "error: Cannot rename a non-local definition",
3473 );
3474 }
3475
3476 #[test]
3477 fn implicit_format_args() {
3478 check(
3479 "fbar",
3480 r#"
3481//- minicore: fmt
3482fn test() {
3483 let foo = "foo";
3484 format_args!("hello {foo} {foo$0} {}", foo);
3485}
3486"#,
3487 r#"
3488fn test() {
3489 let fbar = "foo";
3490 format_args!("hello {fbar} {fbar} {}", fbar);
3491}
3492"#,
3493 );
3494 }
3495
3496 #[test]
3497 fn implicit_format_args2() {
3498 check(
3499 "fo",
3500 r#"
3501//- minicore: fmt
3502fn test() {
3503 let foo = "foo";
3504 format_args!("hello {foo} {foo$0} {}", foo);
3505}
3506"#,
3507 r#"
3508fn test() {
3509 let fo = "foo";
3510 format_args!("hello {fo} {fo} {}", fo);
3511}
3512"#,
3513 );
3514 }
3515
3516 #[test]
3517 fn asm_operand() {
3518 check(
3519 "bose",
3520 r#"
3521//- minicore: asm
3522fn test() {
3523 core::arch::asm!(
3524 "push {base}",
3525 base$0 = const 0
3526 );
3527}
3528"#,
3529 r#"
3530fn test() {
3531 core::arch::asm!(
3532 "push {bose}",
3533 bose = const 0
3534 );
3535}
3536"#,
3537 );
3538 }
3539
3540 #[test]
3541 fn asm_operand2() {
3542 check(
3543 "bose",
3544 r#"
3545//- minicore: asm
3546fn test() {
3547 core::arch::asm!(
3548 "push {base$0}",
3549 "push {base}",
3550 boo = const 0,
3551 virtual_free = sym VIRTUAL_FREE,
3552 base = const 0,
3553 boo = const 0,
3554 );
3555}
3556"#,
3557 r#"
3558fn test() {
3559 core::arch::asm!(
3560 "push {bose}",
3561 "push {bose}",
3562 boo = const 0,
3563 virtual_free = sym VIRTUAL_FREE,
3564 bose = const 0,
3565 boo = const 0,
3566 );
3567}
3568"#,
3569 );
3570 }
3571
3572 #[test]
3573 fn rename_path_inside_use_tree() {
3574 check(
3575 "Baz",
3576 r#"
3577//- /main.rs crate:main
3578mod module;
3579mod foo { pub struct Foo; }
3580mod bar { use super::Foo; }
3581
3582use foo::Foo$0;
3583
3584fn main() { let _: Foo; }
3585//- /module.rs
3586use crate::foo::Foo;
3587"#,
3588 r#"
3589mod module;
3590mod foo { pub struct Foo; }
3591mod bar { use super::Baz; }
3592
3593use foo::Foo as Baz;
3594
3595fn main() { let _: Baz; }
3596"#,
3597 )
3598 }
3599
3600 #[test]
3601 fn rename_path_inside_use_tree_foreign() {
3602 check(
3603 "Baz",
3604 r#"
3605//- /lib.rs crate:lib new_source_root:library
3606pub struct S;
3607//- /main.rs crate:main deps:lib new_source_root:local
3608use lib::S$0;
3609fn main() { let _: S; }
3610"#,
3611 r#"
3612use lib::S as Baz;
3613fn main() { let _: Baz; }
3614"#,
3615 );
3616 }
3617
3618 #[test]
3619 fn rename_type_param_ref_in_use_bound() {
3620 check(
3621 "U",
3622 r#"
3623fn foo<T>() -> impl use<T$0> Trait {}
3624"#,
3625 r#"
3626fn foo<U>() -> impl use<U> Trait {}
3627"#,
3628 );
3629 }
3630
3631 #[test]
3632 fn rename_type_param_in_use_bound() {
3633 check(
3634 "U",
3635 r#"
3636fn foo<T$0>() -> impl use<T> Trait {}
3637"#,
3638 r#"
3639fn foo<U>() -> impl use<U> Trait {}
3640"#,
3641 );
3642 }
3643
3644 #[test]
3645 fn rename_lifetime_param_ref_in_use_bound() {
3646 check(
3647 "u",
3648 r#"
3649fn foo<'t>() -> impl use<'t$0> Trait {}
3650"#,
3651 r#"
3652fn foo<'u>() -> impl use<'u> Trait {}
3653"#,
3654 );
3655 }
3656
3657 #[test]
3658 fn rename_lifetime_param_in_use_bound() {
3659 check(
3660 "u",
3661 r#"
3662fn foo<'t$0>() -> impl use<'t> Trait {}
3663"#,
3664 r#"
3665fn foo<'u>() -> impl use<'u> Trait {}
3666"#,
3667 );
3668 }
3669
3670 #[test]
3671 fn rename_parent_type_param_in_use_bound() {
3672 check(
3673 "U",
3674 r#"
3675trait Trait<T> {
3676 fn foo() -> impl use<T$0> Trait {}
3677}
3678"#,
3679 r#"
3680trait Trait<U> {
3681 fn foo() -> impl use<U> Trait {}
3682}
3683"#,
3684 );
3685 }
3686
3687 #[test]
3688 fn rename_macro_generated_type_from_type_with_a_suffix() {
3689 check(
3690 "Bar",
3691 r#"
3692//- proc_macros: generate_suffixed_type
3693#[proc_macros::generate_suffixed_type]
3694struct Foo$0;
3695fn usage(_: FooSuffix) {}
3696usage(FooSuffix);
3697"#,
3698 r#"
3699#[proc_macros::generate_suffixed_type]
3700struct Bar;
3701fn usage(_: BarSuffix) {}
3702usage(BarSuffix);
3703"#,
3704 );
3705 }
3706
3707 #[test]
3708 #[should_panic]
3710 fn rename_macro_generated_type_from_type_usage_with_a_suffix() {
3711 check(
3712 "Bar",
3713 r#"
3714//- proc_macros: generate_suffixed_type
3715#[proc_macros::generate_suffixed_type]
3716struct Foo;
3717fn usage(_: FooSuffix) {}
3718usage(FooSuffix);
3719fn other_place() { Foo$0; }
3720"#,
3721 r#"
3722#[proc_macros::generate_suffixed_type]
3723struct Bar;
3724fn usage(_: BarSuffix) {}
3725usage(BarSuffix);
3726fn other_place() { Bar; }
3727"#,
3728 );
3729 }
3730
3731 #[test]
3732 fn rename_macro_generated_type_from_variant_with_a_suffix() {
3733 check(
3734 "Bar",
3735 r#"
3736//- proc_macros: generate_suffixed_type
3737#[proc_macros::generate_suffixed_type]
3738enum Quux {
3739 Foo$0,
3740}
3741fn usage(_: FooSuffix) {}
3742usage(FooSuffix);
3743"#,
3744 r#"
3745#[proc_macros::generate_suffixed_type]
3746enum Quux {
3747 Bar,
3748}
3749fn usage(_: BarSuffix) {}
3750usage(BarSuffix);
3751"#,
3752 );
3753 }
3754
3755 #[test]
3756 #[should_panic]
3758 fn rename_macro_generated_type_from_variant_usage_with_a_suffix() {
3759 check(
3760 "Bar",
3761 r#"
3762//- proc_macros: generate_suffixed_type
3763#[proc_macros::generate_suffixed_type]
3764enum Quux {
3765 Foo,
3766}
3767fn usage(_: FooSuffix) {}
3768usage(FooSuffix);
3769fn other_place() { Quux::Foo$0; }
3770"#,
3771 r#"
3772#[proc_macros::generate_suffixed_type]
3773enum Quux {
3774 Bar,
3775}
3776fn usage(_: BarSuffix) {}
3777usage(BartSuffix);
3778fn other_place() { Quux::Bar$0; }
3779"#,
3780 );
3781 }
3782
3783 #[test]
3784 fn rename_to_self_callers() {
3785 check(
3786 "self",
3787 r#"
3788//- minicore: add
3789struct Foo;
3790impl core::ops::Add for Foo {
3791 type Target = Foo;
3792 fn add(self, _: Self) -> Foo { Foo }
3793}
3794
3795impl Foo {
3796 fn foo(th$0is: &Self) {}
3797}
3798
3799fn bar(v: &Foo) {
3800 Foo::foo(v);
3801}
3802
3803fn baz() {
3804 Foo::foo(&Foo);
3805 Foo::foo(Foo + Foo);
3806}
3807 "#,
3808 r#"
3809struct Foo;
3810impl core::ops::Add for Foo {
3811 type Target = Foo;
3812 fn add(self, _: Self) -> Foo { Foo }
3813}
3814
3815impl Foo {
3816 fn foo(&self) {}
3817}
3818
3819fn bar(v: &Foo) {
3820 v.foo();
3821}
3822
3823fn baz() {
3824 Foo.foo();
3825 (Foo + Foo).foo();
3826}
3827 "#,
3828 );
3829 check(
3831 "self",
3832 r#"
3833struct Foo;
3834
3835impl Foo {
3836 fn foo(th$0is: &Self, v: i32) {}
3837}
3838
3839fn bar(v: Foo) {
3840 Foo::foo(&v, 123);
3841}
3842 "#,
3843 r#"
3844struct Foo;
3845
3846impl Foo {
3847 fn foo(&self, v: i32) {}
3848}
3849
3850fn bar(v: Foo) {
3851 v.foo(123);
3852}
3853 "#,
3854 );
3855 }
3856
3857 #[test]
3858 fn rename_to_self_callers_in_macro() {
3859 check(
3860 "self",
3861 r#"
3862struct Foo;
3863
3864impl Foo {
3865 fn foo(th$0is: &Self, v: i32) {}
3866}
3867
3868macro_rules! m { ($it:expr) => { $it } }
3869fn bar(v: Foo) {
3870 m!(Foo::foo(&v, 123));
3871}
3872 "#,
3873 r#"
3874struct Foo;
3875
3876impl Foo {
3877 fn foo(&self, v: i32) {}
3878}
3879
3880macro_rules! m { ($it:expr) => { $it } }
3881fn bar(v: Foo) {
3882 m!(v.foo( 123));
3883}
3884 "#,
3885 );
3886 }
3887
3888 #[test]
3889 fn rename_from_self_callers() {
3890 check(
3891 "this",
3892 r#"
3893//- minicore: add
3894struct Foo;
3895impl Foo {
3896 fn foo(&sel$0f) {}
3897}
3898impl core::ops::Add for Foo {
3899 type Output = Foo;
3900
3901 fn add(self, _rhs: Self) -> Self::Output {
3902 Foo
3903 }
3904}
3905
3906fn bar(v: &Foo) {
3907 v.foo();
3908 (Foo + Foo).foo();
3909}
3910
3911mod baz {
3912 fn baz(v: super::Foo) {
3913 v.foo();
3914 }
3915}
3916 "#,
3917 r#"
3918struct Foo;
3919impl Foo {
3920 fn foo(this: &Self) {}
3921}
3922impl core::ops::Add for Foo {
3923 type Output = Foo;
3924
3925 fn add(self, _rhs: Self) -> Self::Output {
3926 Foo
3927 }
3928}
3929
3930fn bar(v: &Foo) {
3931 Foo::foo(v);
3932 Foo::foo(&(Foo + Foo));
3933}
3934
3935mod baz {
3936 fn baz(v: super::Foo) {
3937 crate::Foo::foo(&v);
3938 }
3939}
3940 "#,
3941 );
3942 check(
3944 "this",
3945 r#"
3946struct Foo;
3947impl Foo {
3948 fn foo(&sel$0f, _v: i32) {}
3949}
3950
3951fn bar() {
3952 Foo.foo(1);
3953}
3954 "#,
3955 r#"
3956struct Foo;
3957impl Foo {
3958 fn foo(this: &Self, _v: i32) {}
3959}
3960
3961fn bar() {
3962 Foo::foo(&Foo, 1);
3963}
3964 "#,
3965 );
3966 }
3967
3968 #[test]
3969 fn rename_constructor_locals() {
3970 check(
3971 "field",
3972 r#"
3973struct Struct {
3974 struct_field$0: String,
3975}
3976
3977impl Struct {
3978 fn new(struct_field: String) -> Self {
3979 if false {
3980 return Self { struct_field };
3981 }
3982 Self { struct_field }
3983 }
3984}
3985
3986mod foo {
3987 macro_rules! m {
3988 ($it:expr) => { return $it };
3989 }
3990
3991 impl crate::Struct {
3992 fn with_foo(struct_field: String) -> crate::Struct {
3993 m!(crate::Struct { struct_field });
3994 }
3995 }
3996}
3997 "#,
3998 r#"
3999struct Struct {
4000 field: String,
4001}
4002
4003impl Struct {
4004 fn new(field: String) -> Self {
4005 if false {
4006 return Self { field };
4007 }
4008 Self { field }
4009 }
4010}
4011
4012mod foo {
4013 macro_rules! m {
4014 ($it:expr) => { return $it };
4015 }
4016
4017 impl crate::Struct {
4018 fn with_foo(field: String) -> crate::Struct {
4019 m!(crate::Struct { field });
4020 }
4021 }
4022}
4023 "#,
4024 );
4025 }
4026
4027 #[test]
4028 fn test_rename_elided_lifetime_fn_no_generics() {
4029 check(
4030 "'a",
4031 r#"
4032fn foo(x: &'_$0 str) {}
4033"#,
4034 r#"
4035fn foo<'a>(x: &'a str) {}
4036"#,
4037 );
4038 }
4039
4040 #[test]
4041 fn test_rename_elided_lifetime_fn_with_generics() {
4042 check(
4043 "'a",
4044 r#"
4045fn foo<T>(x: &'_$0 str, y: T) {}
4046"#,
4047 r#"
4048fn foo<'a, T>(x: &'a str, y: T) {}
4049"#,
4050 );
4051 }
4052
4053 #[test]
4054 fn test_rename_elided_lifetime_impl_no_generics() {
4055 check(
4056 "'a",
4057 r#"
4058struct Foo<'a>(&'a str);
4059impl Foo<'_$0> {}
4060"#,
4061 r#"
4062struct Foo<'a>(&'a str);
4063impl<'a> Foo<'a> {}
4064"#,
4065 );
4066 }
4067
4068 #[test]
4069 fn test_rename_elided_lifetime_impl_with_generics() {
4070 check(
4071 "'a",
4072 r#"
4073struct Foo<'a, T>(&'a str, T);
4074impl<T> Foo<'_$0, T> {}
4075"#,
4076 r#"
4077struct Foo<'a, T>(&'a str, T);
4078impl<'a, T> Foo<'a, T> {}
4079"#,
4080 );
4081 }
4082
4083 #[test]
4084 fn test_rename_mut_pattern_with_macro() {
4085 check(
4086 "new",
4087 r#"
4088//- minicore: option
4089macro_rules! pat_macro {
4090 ($pat:pat) => {
4091 $pat
4092 };
4093}
4094
4095pub fn main() {
4096 match None {
4097 pat_macro!(Some(mut old$0)) => {
4098 old += 1,
4099 }
4100 None => {}
4101 }
4102}
4103"#,
4104 r#"
4105macro_rules! pat_macro {
4106 ($pat:pat) => {
4107 $pat
4108 };
4109}
4110
4111pub fn main() {
4112 match None {
4113 pat_macro!(Some(mut new)) => {
4114 new += 1,
4115 }
4116 None => {}
4117 }
4118}
4119"#,
4120 );
4121 }
4122 #[test]
4123 fn test_rename_ref_pattern_with_macro() {
4124 check(
4125 "new",
4126 r#"
4127//- minicore: option
4128macro_rules! pat_macro {
4129 ($pat:pat) => {
4130 $pat
4131 };
4132}
4133
4134pub fn main() {
4135 match None {
4136 pat_macro!(Some(ref old$0)) => {
4137 old += 1,
4138 }
4139 None => {}
4140 }
4141}
4142"#,
4143 r#"
4144macro_rules! pat_macro {
4145 ($pat:pat) => {
4146 $pat
4147 };
4148}
4149
4150pub fn main() {
4151 match None {
4152 pat_macro!(Some(ref new)) => {
4153 new += 1,
4154 }
4155 None => {}
4156 }
4157}
4158"#,
4159 );
4160 }
4161}