mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-28 11:20:54 +00:00
Merge pull request #20351 from ChayimFriedman2/rename-self1
feat: When renaming a parameter to `self`, change callers to use method call syntax
This commit is contained in:
commit
5daac5567b
@ -206,6 +206,7 @@ impl EditionedFileId {
|
||||
|
||||
#[salsa_macros::input(debug)]
|
||||
pub struct FileText {
|
||||
#[returns(ref)]
|
||||
pub text: Arc<str>,
|
||||
pub file_id: vfs::FileId,
|
||||
}
|
||||
@ -357,7 +358,7 @@ fn parse(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Parse<ast::SourceFil
|
||||
let _p = tracing::info_span!("parse", ?file_id).entered();
|
||||
let (file_id, edition) = file_id.unpack(db.as_dyn_database());
|
||||
let text = db.file_text(file_id).text(db);
|
||||
ast::SourceFile::parse(&text, edition)
|
||||
ast::SourceFile::parse(text, edition)
|
||||
}
|
||||
|
||||
fn parse_errors(db: &dyn RootQueryDb, file_id: EditionedFileId) -> Option<&[SyntaxError]> {
|
||||
|
@ -890,7 +890,7 @@ fn include_str_expand(
|
||||
};
|
||||
|
||||
let text = db.file_text(file_id.file_id(db));
|
||||
let text = &*text.text(db);
|
||||
let text = &**text.text(db);
|
||||
|
||||
ExpandResult::ok(quote!(call_site =>#text))
|
||||
}
|
||||
|
@ -99,6 +99,16 @@ impl FileRange {
|
||||
pub fn into_file_id(self, db: &dyn ExpandDatabase) -> FileRangeWrapper<FileId> {
|
||||
FileRangeWrapper { file_id: self.file_id.file_id(db), range: self.range }
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn file_text(self, db: &dyn ExpandDatabase) -> &triomphe::Arc<str> {
|
||||
db.file_text(self.file_id.file_id(db)).text(db)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn text(self, db: &dyn ExpandDatabase) -> &str {
|
||||
&self.file_text(db)[self.range]
|
||||
}
|
||||
}
|
||||
|
||||
/// `AstId` points to an AST node in any file.
|
||||
|
@ -149,7 +149,7 @@ impl TestDB {
|
||||
.into_iter()
|
||||
.filter_map(|file_id| {
|
||||
let text = self.file_text(file_id.file_id(self));
|
||||
let annotations = extract_annotations(&text.text(self));
|
||||
let annotations = extract_annotations(text.text(self));
|
||||
if annotations.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
@ -244,7 +244,7 @@ pub trait LineIndexDatabase: base_db::RootQueryDb {
|
||||
|
||||
fn line_index(db: &dyn LineIndexDatabase, file_id: FileId) -> Arc<LineIndex> {
|
||||
let text = db.file_text(file_id).text(db);
|
||||
Arc::new(LineIndex::new(&text))
|
||||
Arc::new(LineIndex::new(text))
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
|
@ -487,9 +487,9 @@ impl<'a> FindUsages<'a> {
|
||||
scope.entries.iter().map(|(&file_id, &search_range)| {
|
||||
let text = db.file_text(file_id.file_id(db)).text(db);
|
||||
let search_range =
|
||||
search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text)));
|
||||
search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&**text)));
|
||||
|
||||
(text, file_id, search_range)
|
||||
(text.clone(), file_id, search_range)
|
||||
})
|
||||
}
|
||||
|
||||
@ -854,14 +854,7 @@ impl<'a> FindUsages<'a> {
|
||||
&finder,
|
||||
name,
|
||||
is_possibly_self.into_iter().map(|position| {
|
||||
(
|
||||
self.sema
|
||||
.db
|
||||
.file_text(position.file_id.file_id(self.sema.db))
|
||||
.text(self.sema.db),
|
||||
position.file_id,
|
||||
position.range,
|
||||
)
|
||||
(position.file_text(self.sema.db).clone(), position.file_id, position.range)
|
||||
}),
|
||||
|path, name_position| {
|
||||
let has_self = path
|
||||
@ -1067,12 +1060,12 @@ impl<'a> FindUsages<'a> {
|
||||
let file_text = sema.db.file_text(file_id.file_id(self.sema.db));
|
||||
let text = file_text.text(sema.db);
|
||||
let search_range =
|
||||
search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&*text)));
|
||||
search_range.unwrap_or_else(|| TextRange::up_to(TextSize::of(&**text)));
|
||||
|
||||
let tree = LazyCell::new(|| sema.parse(file_id).syntax().clone());
|
||||
let finder = &Finder::new("self");
|
||||
|
||||
for offset in Self::match_indices(&text, finder, search_range) {
|
||||
for offset in Self::match_indices(text, finder, search_range) {
|
||||
for name_ref in Self::find_nodes(sema, "self", file_id, &tree, offset)
|
||||
.filter_map(ast::NameRef::cast)
|
||||
{
|
||||
|
@ -229,7 +229,7 @@ pub(crate) fn check_diagnostics_with_config(
|
||||
let line_index = db.line_index(file_id);
|
||||
|
||||
let mut actual = annotations.remove(&file_id).unwrap_or_default();
|
||||
let mut expected = extract_annotations(&db.file_text(file_id).text(&db));
|
||||
let mut expected = extract_annotations(db.file_text(file_id).text(&db));
|
||||
expected.sort_by_key(|(range, s)| (range.start(), s.clone()));
|
||||
actual.sort_by_key(|(range, s)| (range.start(), s.clone()));
|
||||
// FIXME: We should panic on duplicates instead, but includes currently cause us to report
|
||||
|
@ -186,7 +186,7 @@ impl<'db> MatchFinder<'db> {
|
||||
replacing::matches_to_edit(
|
||||
self.sema.db,
|
||||
&matches,
|
||||
&self.sema.db.file_text(file_id).text(self.sema.db),
|
||||
self.sema.db.file_text(file_id).text(self.sema.db),
|
||||
&self.rules,
|
||||
),
|
||||
)
|
||||
@ -228,7 +228,7 @@ impl<'db> MatchFinder<'db> {
|
||||
let file = self.sema.parse(file_id);
|
||||
let mut res = Vec::new();
|
||||
let file_text = self.sema.db.file_text(file_id.file_id(self.sema.db)).text(self.sema.db);
|
||||
let mut remaining_text = &*file_text;
|
||||
let mut remaining_text = &**file_text;
|
||||
let mut base = 0;
|
||||
let len = snippet.len() as u32;
|
||||
while let Some(offset) = remaining_text.find(snippet) {
|
||||
|
@ -299,7 +299,7 @@ impl Analysis {
|
||||
|
||||
/// Gets the text of the source file.
|
||||
pub fn file_text(&self, file_id: FileId) -> Cancellable<Arc<str>> {
|
||||
self.with_db(|db| SourceDatabase::file_text(db, file_id).text(db))
|
||||
self.with_db(|db| SourceDatabase::file_text(db, file_id).text(db).clone())
|
||||
}
|
||||
|
||||
/// Gets the syntax tree of the file.
|
||||
|
@ -13,8 +13,11 @@ use ide_db::{
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use std::fmt::Write;
|
||||
use stdx::{always, never};
|
||||
use syntax::{AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize, ast};
|
||||
use stdx::{always, format_to, never};
|
||||
use syntax::{
|
||||
AstNode, SyntaxKind, SyntaxNode, TextRange, TextSize,
|
||||
ast::{self, HasArgList, prec::ExprPrecedence},
|
||||
};
|
||||
|
||||
use ide_db::text_edit::TextEdit;
|
||||
|
||||
@ -331,6 +334,85 @@ fn find_definitions(
|
||||
}
|
||||
}
|
||||
|
||||
fn transform_assoc_fn_into_method_call(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
source_change: &mut SourceChange,
|
||||
f: hir::Function,
|
||||
) {
|
||||
let calls = Definition::Function(f).usages(sema).all();
|
||||
for (file_id, calls) in calls {
|
||||
for call in calls {
|
||||
let Some(fn_name) = call.name.as_name_ref() else { continue };
|
||||
let Some(path) = fn_name.syntax().parent().and_then(ast::PathSegment::cast) else {
|
||||
continue;
|
||||
};
|
||||
let path = path.parent_path();
|
||||
// The `PathExpr` is the direct parent, above it is the `CallExpr`.
|
||||
let Some(call) =
|
||||
path.syntax().parent().and_then(|it| ast::CallExpr::cast(it.parent()?))
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let Some(arg_list) = call.arg_list() else { continue };
|
||||
let mut args = arg_list.args();
|
||||
let Some(mut self_arg) = args.next() else { continue };
|
||||
let second_arg = args.next();
|
||||
|
||||
// Strip (de)references, as they will be taken automatically by auto(de)ref.
|
||||
loop {
|
||||
let self_ = match &self_arg {
|
||||
ast::Expr::RefExpr(self_) => self_.expr(),
|
||||
ast::Expr::ParenExpr(self_) => self_.expr(),
|
||||
ast::Expr::PrefixExpr(self_)
|
||||
if self_.op_kind() == Some(ast::UnaryOp::Deref) =>
|
||||
{
|
||||
self_.expr()
|
||||
}
|
||||
_ => break,
|
||||
};
|
||||
self_arg = match self_ {
|
||||
Some(it) => it,
|
||||
None => break,
|
||||
};
|
||||
}
|
||||
|
||||
let self_needs_parens =
|
||||
self_arg.precedence().needs_parentheses_in(ExprPrecedence::Postfix);
|
||||
|
||||
let replace_start = path.syntax().text_range().start();
|
||||
let replace_end = match second_arg {
|
||||
Some(second_arg) => second_arg.syntax().text_range().start(),
|
||||
None => arg_list
|
||||
.r_paren_token()
|
||||
.map(|it| it.text_range().start())
|
||||
.unwrap_or_else(|| arg_list.syntax().text_range().end()),
|
||||
};
|
||||
let replace_range = TextRange::new(replace_start, replace_end);
|
||||
|
||||
let Some(macro_mapped_self) = sema.original_range_opt(self_arg.syntax()) else {
|
||||
continue;
|
||||
};
|
||||
let mut replacement = String::new();
|
||||
if self_needs_parens {
|
||||
replacement.push('(');
|
||||
}
|
||||
replacement.push_str(macro_mapped_self.text(sema.db));
|
||||
if self_needs_parens {
|
||||
replacement.push(')');
|
||||
}
|
||||
replacement.push('.');
|
||||
format_to!(replacement, "{fn_name}");
|
||||
replacement.push('(');
|
||||
|
||||
source_change.insert_source_edit(
|
||||
file_id.file_id(sema.db),
|
||||
TextEdit::replace(replace_range, replacement),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn rename_to_self(
|
||||
sema: &Semantics<'_, RootDatabase>,
|
||||
local: hir::Local,
|
||||
@ -408,6 +490,7 @@ fn rename_to_self(
|
||||
file_id.original_file(sema.db).file_id(sema.db),
|
||||
TextEdit::replace(param_source.syntax().text_range(), String::from(self_param)),
|
||||
);
|
||||
transform_assoc_fn_into_method_call(sema, &mut source_change, fn_def);
|
||||
Ok(source_change)
|
||||
}
|
||||
|
||||
@ -3412,4 +3495,78 @@ fn other_place() { Quux::Bar$0; }
|
||||
"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rename_to_self_callers() {
|
||||
check(
|
||||
"self",
|
||||
r#"
|
||||
//- minicore: add
|
||||
struct Foo;
|
||||
impl core::ops::Add for Foo {
|
||||
type Target = Foo;
|
||||
fn add(self, _: Self) -> Foo { Foo }
|
||||
}
|
||||
|
||||
impl Foo {
|
||||
fn foo(th$0is: &Self) {}
|
||||
}
|
||||
|
||||
fn bar(v: &Foo) {
|
||||
Foo::foo(v);
|
||||
}
|
||||
|
||||
fn baz() {
|
||||
Foo::foo(&Foo);
|
||||
Foo::foo(Foo + Foo);
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct Foo;
|
||||
impl core::ops::Add for Foo {
|
||||
type Target = Foo;
|
||||
fn add(self, _: Self) -> Foo { Foo }
|
||||
}
|
||||
|
||||
impl Foo {
|
||||
fn foo(&self) {}
|
||||
}
|
||||
|
||||
fn bar(v: &Foo) {
|
||||
v.foo();
|
||||
}
|
||||
|
||||
fn baz() {
|
||||
Foo.foo();
|
||||
(Foo + Foo).foo();
|
||||
}
|
||||
"#,
|
||||
);
|
||||
// Multiple arguments:
|
||||
check(
|
||||
"self",
|
||||
r#"
|
||||
struct Foo;
|
||||
|
||||
impl Foo {
|
||||
fn foo(th$0is: &Self, v: i32) {}
|
||||
}
|
||||
|
||||
fn bar(v: Foo) {
|
||||
Foo::foo(&v, 123);
|
||||
}
|
||||
"#,
|
||||
r#"
|
||||
struct Foo;
|
||||
|
||||
impl Foo {
|
||||
fn foo(&self, v: i32) {}
|
||||
}
|
||||
|
||||
fn bar(v: Foo) {
|
||||
v.foo(123);
|
||||
}
|
||||
"#,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -448,7 +448,7 @@ impl GlobalState {
|
||||
tracing::info!(%vfs_path, ?change_kind, "Processing rust-analyzer.toml changes");
|
||||
if vfs_path.as_path() == user_config_abs_path {
|
||||
tracing::info!(%vfs_path, ?change_kind, "Use config rust-analyzer.toml changes");
|
||||
change.change_user_config(Some(db.file_text(file_id).text(db)));
|
||||
change.change_user_config(Some(db.file_text(file_id).text(db).clone()));
|
||||
}
|
||||
|
||||
// If change has been made to a ratoml file that
|
||||
@ -462,14 +462,14 @@ impl GlobalState {
|
||||
change.change_workspace_ratoml(
|
||||
source_root_id,
|
||||
vfs_path.clone(),
|
||||
Some(db.file_text(file_id).text(db)),
|
||||
Some(db.file_text(file_id).text(db).clone()),
|
||||
)
|
||||
} else {
|
||||
tracing::info!(%vfs_path, ?source_root_id, "crate rust-analyzer.toml changes");
|
||||
change.change_ratoml(
|
||||
source_root_id,
|
||||
vfs_path.clone(),
|
||||
Some(db.file_text(file_id).text(db)),
|
||||
Some(db.file_text(file_id).text(db).clone()),
|
||||
)
|
||||
};
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user