Merge pull request #20389 from Veykril/push-ssyssvnrywvy
Slim down compile time artifact progress reports
diff --git a/crates/base-db/src/lib.rs b/crates/base-db/src/lib.rs
index ad17f17..b8eadb6 100644
--- a/crates/base-db/src/lib.rs
+++ b/crates/base-db/src/lib.rs
@@ -206,6 +206,7 @@
#[salsa_macros::input(debug)]
pub struct FileText {
+ #[returns(ref)]
pub text: Arc<str>,
pub file_id: vfs::FileId,
}
@@ -357,7 +358,7 @@
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]> {
diff --git a/crates/hir-expand/src/builtin/fn_macro.rs b/crates/hir-expand/src/builtin/fn_macro.rs
index 58ab7f4..ec34461 100644
--- a/crates/hir-expand/src/builtin/fn_macro.rs
+++ b/crates/hir-expand/src/builtin/fn_macro.rs
@@ -890,7 +890,7 @@
};
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))
}
diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs
index 6730b33..a7f3e27 100644
--- a/crates/hir-expand/src/files.rs
+++ b/crates/hir-expand/src/files.rs
@@ -99,6 +99,16 @@
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.
diff --git a/crates/hir-ty/src/test_db.rs b/crates/hir-ty/src/test_db.rs
index b5de0e5..775136d 100644
--- a/crates/hir-ty/src/test_db.rs
+++ b/crates/hir-ty/src/test_db.rs
@@ -149,7 +149,7 @@
.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;
}
diff --git a/crates/ide-assists/src/handlers/add_missing_impl_members.rs b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
index 7f1e7cc..11201af 100644
--- a/crates/ide-assists/src/handlers/add_missing_impl_members.rs
+++ b/crates/ide-assists/src/handlers/add_missing_impl_members.rs
@@ -183,13 +183,11 @@
.clone()
.into_iter()
.chain(other_items.iter().cloned())
- .map(either::Either::Right)
.collect::<Vec<_>>();
let mut editor = edit.make_editor(impl_def.syntax());
if let Some(assoc_item_list) = impl_def.assoc_item_list() {
- let items = new_assoc_items.into_iter().filter_map(either::Either::right).collect();
- assoc_item_list.add_items(&mut editor, items);
+ assoc_item_list.add_items(&mut editor, new_assoc_items);
} else {
let assoc_item_list = make::assoc_item_list(Some(new_assoc_items)).clone_for_update();
editor.insert_all(
diff --git a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
index a4742bc..f1cc3d9 100644
--- a/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
+++ b/crates/ide-assists/src/handlers/convert_from_to_tryfrom.rs
@@ -1,9 +1,7 @@
use ide_db::{famous_defs::FamousDefs, traits::resolve_target_trait};
-use itertools::Itertools;
-use syntax::{
- ast::{self, AstNode, HasGenericArgs, HasName, make},
- ted,
-};
+use syntax::ast::edit::IndentLevel;
+use syntax::ast::{self, AstNode, HasGenericArgs, HasName, make};
+use syntax::syntax_editor::{Element, Position};
use crate::{AssistContext, AssistId, Assists};
@@ -49,6 +47,7 @@
};
let associated_items = impl_.assoc_item_list()?;
+ let associated_l_curly = associated_items.l_curly_token()?;
let from_fn = associated_items.assoc_items().find_map(|item| {
if let ast::AssocItem::Fn(f) = item
&& f.name()?.text() == "from"
@@ -75,30 +74,25 @@
"Convert From to TryFrom",
impl_.syntax().text_range(),
|builder| {
- let trait_ty = builder.make_mut(trait_ty);
- let from_fn_return_type = builder.make_mut(from_fn_return_type);
- let from_fn_name = builder.make_mut(from_fn_name);
- let tail_expr = builder.make_mut(tail_expr);
- let return_exprs = return_exprs.map(|r| builder.make_mut(r)).collect_vec();
- let associated_items = builder.make_mut(associated_items);
-
- ted::replace(
+ let mut editor = builder.make_editor(impl_.syntax());
+ editor.replace(
trait_ty.syntax(),
make::ty(&format!("TryFrom<{from_type}>")).syntax().clone_for_update(),
);
- ted::replace(
+ editor.replace(
from_fn_return_type.syntax(),
make::ty("Result<Self, Self::Error>").syntax().clone_for_update(),
);
- ted::replace(from_fn_name.syntax(), make::name("try_from").syntax().clone_for_update());
- ted::replace(
+ editor
+ .replace(from_fn_name.syntax(), make::name("try_from").syntax().clone_for_update());
+ editor.replace(
tail_expr.syntax(),
wrap_ok(tail_expr.clone()).syntax().clone_for_update(),
);
for r in return_exprs {
let t = r.expr().unwrap_or_else(make::ext::expr_unit);
- ted::replace(t.syntax(), wrap_ok(t.clone()).syntax().clone_for_update());
+ editor.replace(t.syntax(), wrap_ok(t.clone()).syntax().clone_for_update());
}
let error_type = ast::AssocItem::TypeAlias(make::ty_alias(
@@ -114,10 +108,20 @@
&& let ast::AssocItem::TypeAlias(type_alias) = &error_type
&& let Some(ty) = type_alias.ty()
{
- builder.add_placeholder_snippet(cap, ty);
+ let placeholder = builder.make_placeholder_snippet(cap);
+ editor.add_annotation(ty.syntax(), placeholder);
}
- associated_items.add_item_at_start(error_type);
+ let indent = IndentLevel::from_token(&associated_l_curly) + 1;
+ editor.insert_all(
+ Position::after(associated_l_curly),
+ vec![
+ make::tokens::whitespace(&format!("\n{indent}")).syntax_element(),
+ error_type.syntax().syntax_element(),
+ make::tokens::whitespace("\n").syntax_element(),
+ ],
+ );
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
)
}
diff --git a/crates/ide-assists/src/handlers/generate_delegate_methods.rs b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
index 6063898..2c81e28 100644
--- a/crates/ide-assists/src/handlers/generate_delegate_methods.rs
+++ b/crates/ide-assists/src/handlers/generate_delegate_methods.rs
@@ -2,9 +2,11 @@
use ide_db::{FxHashSet, path_transform::PathTransform};
use syntax::{
ast::{
- self, AstNode, HasGenericParams, HasName, HasVisibility as _, edit_in_place::Indent, make,
+ self, AstNode, HasGenericParams, HasName, HasVisibility as _,
+ edit::{AstNodeEdit, IndentLevel},
+ make,
},
- ted,
+ syntax_editor::Position,
};
use crate::{
@@ -165,54 +167,66 @@
is_unsafe,
is_gen,
)
- .clone_for_update();
+ .indent(IndentLevel(1));
+ let item = ast::AssocItem::Fn(f.clone());
- // Get the impl to update, or create one if we need to.
- let impl_def = match impl_def {
- Some(impl_def) => edit.make_mut(impl_def),
+ let mut editor = edit.make_editor(strukt.syntax());
+ let fn_: Option<ast::AssocItem> = match impl_def {
+ Some(impl_def) => match impl_def.assoc_item_list() {
+ Some(assoc_item_list) => {
+ let item = item.indent(IndentLevel::from_node(impl_def.syntax()));
+ assoc_item_list.add_items(&mut editor, vec![item.clone()]);
+ Some(item)
+ }
+ None => {
+ let assoc_item_list = make::assoc_item_list(Some(vec![item]));
+ editor.insert(
+ Position::last_child_of(impl_def.syntax()),
+ assoc_item_list.syntax(),
+ );
+ assoc_item_list.assoc_items().next()
+ }
+ },
None => {
let name = &strukt_name.to_string();
let ty_params = strukt.generic_param_list();
let ty_args = ty_params.as_ref().map(|it| it.to_generic_args());
let where_clause = strukt.where_clause();
+ let assoc_item_list = make::assoc_item_list(Some(vec![item]));
let impl_def = make::impl_(
ty_params,
ty_args,
make::ty_path(make::ext::ident_path(name)),
where_clause,
- None,
+ Some(assoc_item_list),
)
.clone_for_update();
// Fixup impl_def indentation
let indent = strukt.indent_level();
- impl_def.reindent_to(indent);
+ let impl_def = impl_def.indent(indent);
// Insert the impl block.
let strukt = edit.make_mut(strukt.clone());
- ted::insert_all(
- ted::Position::after(strukt.syntax()),
+ editor.insert_all(
+ Position::after(strukt.syntax()),
vec![
make::tokens::whitespace(&format!("\n\n{indent}")).into(),
impl_def.syntax().clone().into(),
],
);
-
- impl_def
+ impl_def.assoc_item_list().and_then(|list| list.assoc_items().next())
}
};
- // Fixup function indentation.
- // FIXME: Should really be handled by `AssocItemList::add_item`
- f.reindent_to(impl_def.indent_level() + 1);
-
- let assoc_items = impl_def.get_or_create_assoc_item_list();
- assoc_items.add_item(f.clone().into());
-
- if let Some(cap) = ctx.config.snippet_cap {
- edit.add_tabstop_before(cap, f)
+ if let Some(cap) = ctx.config.snippet_cap
+ && let Some(fn_) = fn_
+ {
+ let tabstop = edit.make_tabstop_before(cap);
+ editor.add_annotation(fn_.syntax(), tabstop);
}
+ edit.add_file_edits(ctx.vfs_file_id(), editor);
},
)?;
}
diff --git a/crates/ide-assists/src/handlers/generate_impl.rs b/crates/ide-assists/src/handlers/generate_impl.rs
index fcb81d2..b38ee6f 100644
--- a/crates/ide-assists/src/handlers/generate_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_impl.rs
@@ -201,7 +201,6 @@
&impl_,
&target_scope,
);
- let assoc_items = assoc_items.into_iter().map(either::Either::Right).collect();
let assoc_item_list = make::assoc_item_list(Some(assoc_items));
make_impl_(Some(assoc_item_list))
};
diff --git a/crates/ide-assists/src/handlers/generate_new.rs b/crates/ide-assists/src/handlers/generate_new.rs
index 5bda122..351f134 100644
--- a/crates/ide-assists/src/handlers/generate_new.rs
+++ b/crates/ide-assists/src/handlers/generate_new.rs
@@ -168,7 +168,7 @@
);
fn_.syntax().clone()
} else {
- let items = vec![either::Either::Right(ast::AssocItem::Fn(fn_))];
+ let items = vec![ast::AssocItem::Fn(fn_)];
let list = make::assoc_item_list(Some(items));
editor.insert(Position::after(impl_def.syntax()), list.syntax());
list.syntax().clone()
@@ -176,7 +176,7 @@
} else {
// Generate a new impl to add the method to
let indent_level = strukt.indent_level();
- let body = vec![either::Either::Right(ast::AssocItem::Fn(fn_))];
+ let body = vec![ast::AssocItem::Fn(fn_)];
let list = make::assoc_item_list(Some(body));
let impl_def = generate_impl_with_item(&ast::Adt::Struct(strukt.clone()), Some(list));
diff --git a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
index dc3dc73..56500cf 100644
--- a/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
+++ b/crates/ide-assists/src/handlers/generate_trait_from_impl.rs
@@ -2,12 +2,8 @@
use ide_db::assists::AssistId;
use syntax::{
AstNode, SyntaxKind, T,
- ast::{
- self, HasGenericParams, HasName, HasVisibility,
- edit_in_place::{HasVisibilityEdit, Indent},
- make,
- },
- ted::{self, Position},
+ ast::{self, HasGenericParams, HasName, HasVisibility, edit_in_place::Indent, make},
+ syntax_editor::{Position, SyntaxEditor},
};
// NOTES :
@@ -88,8 +84,8 @@
return None;
}
- let assoc_items = impl_ast.assoc_item_list()?;
- let first_element = assoc_items.assoc_items().next();
+ let impl_assoc_items = impl_ast.assoc_item_list()?;
+ let first_element = impl_assoc_items.assoc_items().next();
first_element.as_ref()?;
let impl_name = impl_ast.self_ty()?;
@@ -99,20 +95,16 @@
"Generate trait from impl",
impl_ast.syntax().text_range(),
|builder| {
- let impl_ast = builder.make_mut(impl_ast);
- let trait_items = assoc_items.clone_for_update();
- let impl_items = builder.make_mut(assoc_items);
- let impl_name = builder.make_mut(impl_name);
+ let trait_items: ast::AssocItemList = {
+ let trait_items = impl_assoc_items.clone_subtree();
+ let mut trait_items_editor = SyntaxEditor::new(trait_items.syntax().clone());
- trait_items.assoc_items().for_each(|item| {
- strip_body(&item);
- remove_items_visibility(&item);
- });
-
- impl_items.assoc_items().for_each(|item| {
- remove_items_visibility(&item);
- });
-
+ trait_items.assoc_items().for_each(|item| {
+ strip_body(&mut trait_items_editor, &item);
+ remove_items_visibility(&mut trait_items_editor, &item);
+ });
+ ast::AssocItemList::cast(trait_items_editor.finish().new_root().clone()).unwrap()
+ };
let trait_ast = make::trait_(
false,
"NewTrait",
@@ -130,6 +122,7 @@
trait_name_ref.syntax().clone().into(),
make::tokens::single_space().into(),
make::token(T![for]).into(),
+ make::tokens::single_space().into(),
];
if let Some(params) = impl_ast.generic_param_list() {
@@ -137,10 +130,15 @@
elements.insert(1, gen_args.syntax().clone().into());
}
- ted::insert_all(Position::before(impl_name.syntax()), elements);
+ let mut editor = builder.make_editor(impl_ast.syntax());
+ impl_assoc_items.assoc_items().for_each(|item| {
+ remove_items_visibility(&mut editor, &item);
+ });
+
+ editor.insert_all(Position::before(impl_name.syntax()), elements);
// Insert trait before TraitImpl
- ted::insert_all_raw(
+ editor.insert_all(
Position::before(impl_ast.syntax()),
vec![
trait_ast.syntax().clone().into(),
@@ -150,11 +148,12 @@
// Link the trait name & trait ref names together as a placeholder snippet group
if let Some(cap) = ctx.config.snippet_cap {
- builder.add_placeholder_snippet_group(
- cap,
- vec![trait_name.syntax().clone(), trait_name_ref.syntax().clone()],
- );
+ let placeholder = builder.make_placeholder_snippet(cap);
+ editor.add_annotation(trait_name.syntax(), placeholder);
+ editor.add_annotation(trait_name_ref.syntax(), placeholder);
}
+
+ builder.add_file_edits(ctx.vfs_file_id(), editor);
},
);
@@ -162,19 +161,21 @@
}
/// `E0449` Trait items always share the visibility of their trait
-fn remove_items_visibility(item: &ast::AssocItem) {
+fn remove_items_visibility(editor: &mut SyntaxEditor, item: &ast::AssocItem) {
if let Some(has_vis) = ast::AnyHasVisibility::cast(item.syntax().clone()) {
if let Some(vis) = has_vis.visibility()
&& let Some(token) = vis.syntax().next_sibling_or_token()
&& token.kind() == SyntaxKind::WHITESPACE
{
- ted::remove(token);
+ editor.delete(token);
}
- has_vis.set_visibility(None);
+ if let Some(vis) = has_vis.visibility() {
+ editor.delete(vis.syntax());
+ }
}
}
-fn strip_body(item: &ast::AssocItem) {
+fn strip_body(editor: &mut SyntaxEditor, item: &ast::AssocItem) {
if let ast::AssocItem::Fn(f) = item
&& let Some(body) = f.body()
{
@@ -183,10 +184,10 @@
if let Some(prev) = body.syntax().prev_sibling_or_token()
&& prev.kind() == SyntaxKind::WHITESPACE
{
- ted::remove(prev);
+ editor.delete(prev);
}
- ted::replace(body.syntax(), make::tokens::semicolon());
+ editor.replace(body.syntax(), make::tokens::semicolon());
};
}
diff --git a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
index 45bb6ce..175f261 100644
--- a/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
+++ b/crates/ide-assists/src/handlers/replace_derive_with_manual_impl.rs
@@ -221,11 +221,7 @@
} else {
Some(first.clone())
};
- let items = first_item
- .into_iter()
- .chain(other.iter().cloned())
- .map(either::Either::Right)
- .collect();
+ let items = first_item.into_iter().chain(other.iter().cloned()).collect();
make::assoc_item_list(Some(items))
} else {
make::assoc_item_list(None)
diff --git a/crates/ide-db/src/lib.rs b/crates/ide-db/src/lib.rs
index c94be7e..49f7f63 100644
--- a/crates/ide-db/src/lib.rs
+++ b/crates/ide-db/src/lib.rs
@@ -244,7 +244,7 @@
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)]
diff --git a/crates/ide-db/src/search.rs b/crates/ide-db/src/search.rs
index 4dd6422..abd4dc8 100644
--- a/crates/ide-db/src/search.rs
+++ b/crates/ide-db/src/search.rs
@@ -487,9 +487,9 @@
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 @@
&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 @@
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)
{
diff --git a/crates/ide-diagnostics/src/tests.rs b/crates/ide-diagnostics/src/tests.rs
index 4e4bd47..1819931 100644
--- a/crates/ide-diagnostics/src/tests.rs
+++ b/crates/ide-diagnostics/src/tests.rs
@@ -229,7 +229,7 @@
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
diff --git a/crates/ide-ssr/src/lib.rs b/crates/ide-ssr/src/lib.rs
index 138af22..43ad12c1 100644
--- a/crates/ide-ssr/src/lib.rs
+++ b/crates/ide-ssr/src/lib.rs
@@ -186,7 +186,7 @@
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 @@
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) {
diff --git a/crates/ide/src/folding_ranges.rs b/crates/ide/src/folding_ranges.rs
old mode 100755
new mode 100644
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 84e4127..f768d4b 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -83,14 +83,14 @@
return Some(RangeInfo::new(original_token.text_range(), navs));
}
- if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &original_token) {
- return Some(RangeInfo::new(original_token.text_range(), navs));
- }
-
let navs = sema
.descend_into_macros_no_opaque(original_token.clone(), false)
.into_iter()
.filter_map(|token| {
+ if let Some(navs) = find_definition_for_known_blanket_dual_impls(sema, &token.value) {
+ return Some(navs);
+ }
+
let parent = token.value.parent()?;
let token_file_id = token.file_id;
@@ -3284,6 +3284,32 @@
}
#[test]
+ fn into_call_to_from_definition_within_macro() {
+ check(
+ r#"
+//- proc_macros: identity
+//- minicore: from
+struct A;
+
+struct B;
+
+impl From<A> for B {
+ fn from(value: A) -> Self {
+ //^^^^
+ B
+ }
+}
+
+#[proc_macros::identity]
+fn f() {
+ let a = A;
+ let b: B = a.into$0();
+}
+ "#,
+ );
+ }
+
+ #[test]
fn into_call_to_from_definition_with_trait_bounds() {
check(
r#"
diff --git a/crates/ide/src/lib.rs b/crates/ide/src/lib.rs
index b3b8deb..9887748 100644
--- a/crates/ide/src/lib.rs
+++ b/crates/ide/src/lib.rs
@@ -299,7 +299,7 @@
/// 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.
diff --git a/crates/ide/src/rename.rs b/crates/ide/src/rename.rs
index 634edaa..aea4ae0 100644
--- a/crates/ide/src/rename.rs
+++ b/crates/ide/src/rename.rs
@@ -13,8 +13,11 @@
};
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 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 @@
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 @@
"#,
);
}
+
+ #[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);
+}
+ "#,
+ );
+ }
}
diff --git a/crates/rust-analyzer/src/config.rs b/crates/rust-analyzer/src/config.rs
index 70d0448..1a00295 100644
--- a/crates/rust-analyzer/src/config.rs
+++ b/crates/rust-analyzer/src/config.rs
@@ -726,7 +726,9 @@
/// ```bash
/// cargo check --quiet --workspace --message-format=json --all-targets --keep-going
/// ```
- /// .
+ ///
+ /// Note: The option must be specified as an array of command line arguments, with
+ /// the first argument being the name of the command to run.
cargo_buildScripts_overrideCommand: Option<Vec<String>> = None,
/// Rerun proc-macros building/build-scripts running when proc-macro
/// or build-script sources change and are saved.
@@ -840,7 +842,9 @@
/// ```bash
/// cargo check --workspace --message-format=json --all-targets
/// ```
- /// .
+ ///
+ /// Note: The option must be specified as an array of command line arguments, with
+ /// the first argument being the name of the command to run.
check_overrideCommand | checkOnSave_overrideCommand: Option<Vec<String>> = None,
/// Check for specific targets. Defaults to `#rust-analyzer.cargo.target#` if empty.
///
@@ -890,6 +894,9 @@
/// not that of `cargo fmt`. The file contents will be passed on the
/// standard input and the formatted result will be read from the
/// standard output.
+ ///
+ /// Note: The option must be specified as an array of command line arguments, with
+ /// the first argument being the name of the command to run.
rustfmt_overrideCommand: Option<Vec<String>> = None,
/// Enables the use of rustfmt's unstable range formatting command for the
/// `textDocument/rangeFormatting` request. The rustfmt option is unstable and only
diff --git a/crates/rust-analyzer/src/global_state.rs b/crates/rust-analyzer/src/global_state.rs
index 3171bdd..2f1afba 100644
--- a/crates/rust-analyzer/src/global_state.rs
+++ b/crates/rust-analyzer/src/global_state.rs
@@ -448,7 +448,7 @@
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 @@
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()),
)
};
diff --git a/crates/syntax/src/ast/edit_in_place.rs b/crates/syntax/src/ast/edit_in_place.rs
index f01ac08..b50ce64 100644
--- a/crates/syntax/src/ast/edit_in_place.rs
+++ b/crates/syntax/src/ast/edit_in_place.rs
@@ -9,11 +9,11 @@
SyntaxKind::{ATTR, COMMENT, WHITESPACE},
SyntaxNode, SyntaxToken,
algo::{self, neighbor},
- ast::{self, HasGenericArgs, HasGenericParams, edit::IndentLevel, make},
+ ast::{self, HasGenericParams, edit::IndentLevel, make},
ted::{self, Position},
};
-use super::{GenericParam, HasArgList, HasName};
+use super::{GenericParam, HasName};
pub trait GenericParamsOwnerEdit: ast::HasGenericParams {
fn get_or_create_generic_param_list(&self) -> ast::GenericParamList;
@@ -419,34 +419,6 @@
}
}
-impl ast::PathSegment {
- pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList {
- if self.generic_arg_list().is_none() {
- let arg_list = make::generic_arg_list(empty()).clone_for_update();
- ted::append_child(self.syntax(), arg_list.syntax());
- }
- self.generic_arg_list().unwrap()
- }
-}
-
-impl ast::MethodCallExpr {
- pub fn get_or_create_generic_arg_list(&self) -> ast::GenericArgList {
- if self.generic_arg_list().is_none() {
- let generic_arg_list = make::turbofish_generic_arg_list(empty()).clone_for_update();
-
- if let Some(arg_list) = self.arg_list() {
- ted::insert_raw(
- ted::Position::before(arg_list.syntax()),
- generic_arg_list.syntax(),
- );
- } else {
- ted::append_child(self.syntax(), generic_arg_list.syntax());
- }
- }
- self.generic_arg_list().unwrap()
- }
-}
-
impl Removable for ast::UseTree {
fn remove(&self) {
for dir in [Direction::Next, Direction::Prev] {
@@ -677,106 +649,6 @@
];
ted::insert_all(position, elements);
}
-
- /// Adds a new associated item at the start of the associated item list.
- ///
- /// Attention! This function does align the first line of `item` with respect to `self`,
- /// but it does _not_ change indentation of other lines (if any).
- pub fn add_item_at_start(&self, item: ast::AssocItem) {
- match self.assoc_items().next() {
- Some(first_item) => {
- let indent = IndentLevel::from_node(first_item.syntax());
- let before = Position::before(first_item.syntax());
-
- ted::insert_all(
- before,
- vec![
- item.syntax().clone().into(),
- make::tokens::whitespace(&format!("\n\n{indent}")).into(),
- ],
- )
- }
- None => {
- let (indent, position, whitespace) = match self.l_curly_token() {
- Some(l_curly) => {
- normalize_ws_between_braces(self.syntax());
- (IndentLevel::from_token(&l_curly) + 1, Position::after(&l_curly), "\n")
- }
- None => (IndentLevel::single(), Position::first_child_of(self.syntax()), ""),
- };
-
- let mut elements = vec![];
-
- // Avoid pushing an empty whitespace token
- if !indent.is_zero() || !whitespace.is_empty() {
- elements.push(make::tokens::whitespace(&format!("{whitespace}{indent}")).into())
- }
- elements.push(item.syntax().clone().into());
-
- ted::insert_all(position, elements)
- }
- };
- }
-}
-
-impl ast::Fn {
- pub fn get_or_create_body(&self) -> ast::BlockExpr {
- if self.body().is_none() {
- let body = make::ext::empty_block_expr().clone_for_update();
- match self.semicolon_token() {
- Some(semi) => {
- ted::replace(semi, body.syntax());
- ted::insert(Position::before(body.syntax), make::tokens::single_space());
- }
- None => ted::append_child(self.syntax(), body.syntax()),
- }
- }
- self.body().unwrap()
- }
-}
-
-impl ast::LetStmt {
- pub fn set_ty(&self, ty: Option<ast::Type>) {
- match ty {
- None => {
- if let Some(colon_token) = self.colon_token() {
- ted::remove(colon_token);
- }
-
- if let Some(existing_ty) = self.ty() {
- if let Some(sibling) = existing_ty.syntax().prev_sibling_or_token()
- && sibling.kind() == SyntaxKind::WHITESPACE
- {
- ted::remove(sibling);
- }
-
- ted::remove(existing_ty.syntax());
- }
-
- // Remove any trailing ws
- if let Some(last) = self.syntax().last_token().filter(|it| it.kind() == WHITESPACE)
- {
- last.detach();
- }
- }
- Some(new_ty) => {
- if self.colon_token().is_none() {
- ted::insert_raw(
- Position::after(
- self.pat().expect("let stmt should have a pattern").syntax(),
- ),
- make::token(T![:]),
- );
- }
-
- if let Some(old_ty) = self.ty() {
- ted::replace(old_ty.syntax(), new_ty.syntax());
- } else {
- ted::insert(Position::after(self.colon_token().unwrap()), new_ty.syntax());
- }
- }
- }
- }
}
impl ast::RecordExprFieldList {
@@ -1091,35 +963,4 @@
check("let a @ ()", "let a", None);
check("let a @ ", "let a", None);
}
-
- #[test]
- fn test_let_stmt_set_ty() {
- #[track_caller]
- fn check(before: &str, expected: &str, ty: Option<ast::Type>) {
- let ty = ty.map(|it| it.clone_for_update());
-
- let let_stmt = ast_mut_from_text::<ast::LetStmt>(&format!("fn f() {{ {before} }}"));
- let_stmt.set_ty(ty);
-
- let after = ast_mut_from_text::<ast::LetStmt>(&format!("fn f() {{ {expected} }}"));
- assert_eq!(let_stmt.to_string(), after.to_string(), "{let_stmt:#?}\n!=\n{after:#?}");
- }
-
- // adding
- check("let a;", "let a: ();", Some(make::ty_tuple([])));
- // no semicolon due to it being eaten during error recovery
- check("let a:", "let a: ()", Some(make::ty_tuple([])));
-
- // replacing
- check("let a: u8;", "let a: ();", Some(make::ty_tuple([])));
- check("let a: u8 = 3;", "let a: () = 3;", Some(make::ty_tuple([])));
- check("let a: = 3;", "let a: () = 3;", Some(make::ty_tuple([])));
-
- // removing
- check("let a: u8;", "let a;", None);
- check("let a:;", "let a;", None);
-
- check("let a: u8 = 3;", "let a = 3;", None);
- check("let a: = 3;", "let a = 3;", None);
- }
}
diff --git a/crates/syntax/src/ast/make.rs b/crates/syntax/src/ast/make.rs
index 2a7b51c..daeb79c 100644
--- a/crates/syntax/src/ast/make.rs
+++ b/crates/syntax/src/ast/make.rs
@@ -229,9 +229,7 @@
}
}
-pub fn assoc_item_list(
- body: Option<Vec<either::Either<ast::Attr, ast::AssocItem>>>,
-) -> ast::AssocItemList {
+pub fn assoc_item_list(body: Option<Vec<ast::AssocItem>>) -> ast::AssocItemList {
let is_break_braces = body.is_some();
let body_newline = if is_break_braces { "\n".to_owned() } else { String::new() };
let body_indent = if is_break_braces { " ".to_owned() } else { String::new() };
diff --git a/docs/book/src/configuration_generated.md b/docs/book/src/configuration_generated.md
index 05299f1..99a30d8 100644
--- a/docs/book/src/configuration_generated.md
+++ b/docs/book/src/configuration_generated.md
@@ -104,7 +104,9 @@
```bash
cargo check --quiet --workspace --message-format=json --all-targets --keep-going
```
-.
+
+Note: The option must be specified as an array of command line arguments, with
+the first argument being the name of the command to run.
## rust-analyzer.cargo.buildScripts.rebuildOnSave {#cargo.buildScripts.rebuildOnSave}
@@ -331,7 +333,9 @@
```bash
cargo check --workspace --message-format=json --all-targets
```
-.
+
+Note: The option must be specified as an array of command line arguments, with
+the first argument being the name of the command to run.
## rust-analyzer.check.targets {#check.targets}
@@ -1343,6 +1347,9 @@
standard input and the formatted result will be read from the
standard output.
+Note: The option must be specified as an array of command line arguments, with
+the first argument being the name of the command to run.
+
## rust-analyzer.rustfmt.rangeFormatting.enable {#rustfmt.rangeFormatting.enable}
diff --git a/docs/book/src/contributing/style.md b/docs/book/src/contributing/style.md
index 5654e37..746f3eb 100644
--- a/docs/book/src/contributing/style.md
+++ b/docs/book/src/contributing/style.md
@@ -49,8 +49,8 @@
Changes of the third group should be pretty rare, so we don't specify any specific process for them.
That said, adding an innocent-looking `pub use` is a very simple way to break encapsulation, keep an eye on it!
-Note: if you enjoyed this abstract hand-waving about boundaries, you might appreciate
-https://www.tedinski.com/2018/02/06/system-boundaries.html
+Note: if you enjoyed this abstract hand-waving about boundaries, you might appreciate [this post](https://www.tedinski.com/2018/02/06/system-boundaries.html).
+
## Crates.io Dependencies
@@ -231,7 +231,7 @@
}
```
-In the "Not as good" version, the precondition that `1` is a valid char boundary is checked in `is_string_literal` and used in `foo`.
+In the "Bad" version, the precondition that `1` and `s.len() - 1` are valid string literal boundaries is checked in `is_string_literal` but used in `main`.
In the "Good" version, the precondition check and usage are checked in the same block, and then encoded in the types.
**Rationale:** non-local code properties degrade under change.
@@ -271,6 +271,8 @@
}
```
+See also [this post](https://matklad.github.io/2023/11/15/push-ifs-up-and-fors-down.html)
+
## Assertions
Assert liberally.
@@ -608,7 +610,7 @@
```rust
// GOOD
-fn frobnicate(f: impl FnMut()) {
+fn frobnicate(mut f: impl FnMut()) {
frobnicate_impl(&mut f)
}
fn frobnicate_impl(f: &mut dyn FnMut()) {
@@ -616,7 +618,7 @@
}
// BAD
-fn frobnicate(f: impl FnMut()) {
+fn frobnicate(mut f: impl FnMut()) {
// lots of code
}
```
@@ -975,7 +977,7 @@
**Rationale:** consistency & simplicity.
`ref` was required before [match ergonomics](https://github.com/rust-lang/rfcs/blob/master/text/2005-match-ergonomics.md).
Today, it is redundant.
-Between `ref` and mach ergonomics, the latter is more ergonomic in most cases, and is simpler (does not require a keyword).
+Between `ref` and match ergonomics, the latter is more ergonomic in most cases, and is simpler (does not require a keyword).
## Empty Match Arms
diff --git a/editors/code/package.json b/editors/code/package.json
index 8953a30..470db24 100644
--- a/editors/code/package.json
+++ b/editors/code/package.json
@@ -887,7 +887,7 @@
"title": "Cargo",
"properties": {
"rust-analyzer.cargo.buildScripts.overrideCommand": {
- "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets --keep-going\n```\n.",
+ "markdownDescription": "Override the command rust-analyzer uses to run build scripts and\nbuild procedural macros. The command is required to output json\nand should therefore include `--message-format=json` or a similar\noption.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.cargo.buildScripts.invocationStrategy#`.\n\nBy default, a cargo invocation will be constructed for the configured\ntargets and features, with the following base command line:\n\n```bash\ncargo check --quiet --workspace --message-format=json --all-targets --keep-going\n```\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.",
"default": null,
"type": [
"null",
@@ -1207,7 +1207,7 @@
"title": "Check",
"properties": {
"rust-analyzer.check.overrideCommand": {
- "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#`.\n\nIf `$saved_file` is part of the command, rust-analyzer will pass\nthe absolute path of the saved file to the provided command. This is\nintended to be used with non-Cargo build systems.\nNote that `$saved_file` is experimental and may be removed in the future.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n.",
+ "markdownDescription": "Override the command rust-analyzer uses instead of `cargo check` for\ndiagnostics on save. The command is required to output json and\nshould therefore include `--message-format=json` or a similar option\n(if your client supports the `colorDiagnosticOutput` experimental\ncapability, you can use `--message-format=json-diagnostic-rendered-ansi`).\n\nIf you're changing this because you're using some tool wrapping\nCargo, you might also want to change\n`#rust-analyzer.cargo.buildScripts.overrideCommand#`.\n\nIf there are multiple linked projects/workspaces, this command is invoked for\neach of them, with the working directory being the workspace root\n(i.e., the folder containing the `Cargo.toml`). This can be overwritten\nby changing `#rust-analyzer.check.invocationStrategy#`.\n\nIf `$saved_file` is part of the command, rust-analyzer will pass\nthe absolute path of the saved file to the provided command. This is\nintended to be used with non-Cargo build systems.\nNote that `$saved_file` is experimental and may be removed in the future.\n\nAn example command would be:\n\n```bash\ncargo check --workspace --message-format=json --all-targets\n```\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.",
"default": null,
"type": [
"null",
@@ -2808,7 +2808,7 @@
"title": "Rustfmt",
"properties": {
"rust-analyzer.rustfmt.overrideCommand": {
- "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting. This should be the equivalent of `rustfmt` here, and\nnot that of `cargo fmt`. The file contents will be passed on the\nstandard input and the formatted result will be read from the\nstandard output.",
+ "markdownDescription": "Advanced option, fully override the command rust-analyzer uses for\nformatting. This should be the equivalent of `rustfmt` here, and\nnot that of `cargo fmt`. The file contents will be passed on the\nstandard input and the formatted result will be read from the\nstandard output.\n\nNote: The option must be specified as an array of command line arguments, with\nthe first argument being the name of the command to run.",
"default": null,
"type": [
"null",