Skip to content

Commit 3f550d2

Browse files
bors[bot]Veykril
andauthored
Merge #11000
11000: fix: insert whitespaces into assoc items for assist when macro generated r=Veykril a=Veykril This is obviously only a temporary hack which still produces ugly looking items, but as long as the syntax is valid one can at least have rustfmt fix the formatting again. Fixes #6588 bors r+ Co-authored-by: Lukas Wirth <[email protected]>
2 parents db2a708 + 749eeef commit 3f550d2

File tree

12 files changed

+205
-103
lines changed

12 files changed

+205
-103
lines changed

crates/hir/src/semantics.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1164,8 +1164,7 @@ impl<'a> SemanticsScope<'a> {
11641164
}
11651165

11661166
/// Note: `FxHashSet<TraitId>` should be treated as an opaque type, passed into `Type
1167-
// FIXME: rename to visible_traits to not repeat scope?
1168-
pub fn traits_in_scope(&self) -> FxHashSet<TraitId> {
1167+
pub fn visible_traits(&self) -> FxHashSet<TraitId> {
11691168
let resolver = &self.resolver;
11701169
resolver.traits_in_scope(self.db.upcast())
11711170
}

crates/ide/src/expand_macro.rs

Lines changed: 7 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
1-
use std::iter;
2-
31
use hir::Semantics;
4-
use ide_db::{helpers::pick_best_token, RootDatabase};
2+
use ide_db::{
3+
helpers::{insert_whitespace_into_node::insert_ws_into, pick_best_token},
4+
RootDatabase,
5+
};
56
use itertools::Itertools;
6-
use syntax::{ast, ted, AstNode, NodeOrToken, SyntaxKind, SyntaxNode, WalkEvent, T};
7+
use syntax::{ast, ted, AstNode, SyntaxKind, SyntaxNode};
78

89
use crate::FilePosition;
910

@@ -49,7 +50,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
4950
let expansions = sema.expand_derive_macro(&attr)?;
5051
Some(ExpandedMacro {
5152
name: tt,
52-
expansion: expansions.into_iter().map(insert_whitespaces).join(""),
53+
expansion: expansions.into_iter().map(insert_ws_into).join(""),
5354
})
5455
} else {
5556
None
@@ -82,7 +83,7 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
8283
// FIXME:
8384
// macro expansion may lose all white space information
8485
// But we hope someday we can use ra_fmt for that
85-
let expansion = insert_whitespaces(expanded?);
86+
let expansion = insert_ws_into(expanded?).to_string();
8687
Some(ExpandedMacro { name: name.unwrap_or_else(|| "???".to_owned()), expansion })
8788
}
8889

@@ -122,84 +123,6 @@ fn expand<T: AstNode>(
122123
Some(expanded)
123124
}
124125

125-
// FIXME: It would also be cool to share logic here and in the mbe tests,
126-
// which are pretty unreadable at the moment.
127-
fn insert_whitespaces(syn: SyntaxNode) -> String {
128-
use SyntaxKind::*;
129-
let mut res = String::new();
130-
131-
let mut indent = 0;
132-
let mut last: Option<SyntaxKind> = None;
133-
134-
for event in syn.preorder_with_tokens() {
135-
let token = match event {
136-
WalkEvent::Enter(NodeOrToken::Token(token)) => token,
137-
WalkEvent::Leave(NodeOrToken::Node(node))
138-
if matches!(node.kind(), ATTR | MATCH_ARM | STRUCT | ENUM | UNION | FN | IMPL) =>
139-
{
140-
res.push('\n');
141-
res.extend(iter::repeat(" ").take(2 * indent));
142-
continue;
143-
}
144-
_ => continue,
145-
};
146-
let is_next = |f: fn(SyntaxKind) -> bool, default| -> bool {
147-
token.next_token().map(|it| f(it.kind())).unwrap_or(default)
148-
};
149-
let is_last =
150-
|f: fn(SyntaxKind) -> bool, default| -> bool { last.map(f).unwrap_or(default) };
151-
152-
match token.kind() {
153-
k if is_text(k) && is_next(|it| !it.is_punct(), true) => {
154-
res.push_str(token.text());
155-
res.push(' ');
156-
}
157-
L_CURLY if is_next(|it| it != R_CURLY, true) => {
158-
indent += 1;
159-
if is_last(is_text, false) {
160-
res.push(' ');
161-
}
162-
res.push_str("{\n");
163-
res.extend(iter::repeat(" ").take(2 * indent));
164-
}
165-
R_CURLY if is_last(|it| it != L_CURLY, true) => {
166-
indent = indent.saturating_sub(1);
167-
res.push('\n');
168-
res.extend(iter::repeat(" ").take(2 * indent));
169-
res.push_str("}");
170-
}
171-
R_CURLY => {
172-
res.push_str("}\n");
173-
res.extend(iter::repeat(" ").take(2 * indent));
174-
}
175-
LIFETIME_IDENT if is_next(|it| it == IDENT || it == MUT_KW, true) => {
176-
res.push_str(token.text());
177-
res.push(' ');
178-
}
179-
AS_KW => {
180-
res.push_str(token.text());
181-
res.push(' ');
182-
}
183-
T![;] => {
184-
res.push_str(";\n");
185-
res.extend(iter::repeat(" ").take(2 * indent));
186-
}
187-
T![->] => res.push_str(" -> "),
188-
T![=] => res.push_str(" = "),
189-
T![=>] => res.push_str(" => "),
190-
_ => res.push_str(token.text()),
191-
}
192-
193-
last = Some(token.kind());
194-
}
195-
196-
return res;
197-
198-
fn is_text(k: SyntaxKind) -> bool {
199-
k.is_keyword() || k.is_literal() || k == IDENT
200-
}
201-
}
202-
203126
#[cfg(test)]
204127
mod tests {
205128
use expect_test::{expect, Expect};

crates/ide_assists/src/handlers/add_missing_impl_members.rs

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use hir::HasSource;
2-
use ide_db::traits::resolve_target_trait;
2+
use ide_db::{helpers::insert_whitespace_into_node::insert_ws_into, traits::resolve_target_trait};
33
use syntax::ast::{self, make, AstNode};
44

55
use crate::{
@@ -105,7 +105,7 @@ fn add_missing_impl_members_inner(
105105
let trait_ = resolve_target_trait(&ctx.sema, &impl_def)?;
106106

107107
let missing_items = filter_assoc_items(
108-
ctx.db(),
108+
&ctx.sema,
109109
&ide_db::traits::get_missing_assoc_items(&ctx.sema, &impl_def),
110110
mode,
111111
);
@@ -117,6 +117,17 @@ fn add_missing_impl_members_inner(
117117
let target = impl_def.syntax().text_range();
118118
acc.add(AssistId(assist_id, AssistKind::QuickFix), label, target, |builder| {
119119
let target_scope = ctx.sema.scope(impl_def.syntax());
120+
let missing_items = missing_items
121+
.into_iter()
122+
.map(|it| {
123+
if ctx.sema.hir_file_for(it.syntax()).is_macro() {
124+
if let Some(it) = ast::AssocItem::cast(insert_ws_into(it.syntax().clone())) {
125+
return it;
126+
}
127+
}
128+
it.clone_for_update()
129+
})
130+
.collect();
120131
let (new_impl_def, first_new_item) = add_trait_assoc_items_to_impl(
121132
&ctx.sema,
122133
missing_items,
@@ -890,6 +901,44 @@ impl Default for Foo {
890901
Self(Default::default())
891902
}
892903
}
904+
"#,
905+
)
906+
}
907+
908+
#[test]
909+
fn test_from_macro() {
910+
check_assist(
911+
add_missing_default_members,
912+
r#"
913+
macro_rules! foo {
914+
() => {
915+
trait FooB {
916+
fn foo<'lt>(&'lt self) {}
917+
}
918+
}
919+
}
920+
foo!();
921+
struct Foo(usize);
922+
923+
impl FooB for Foo {
924+
$0
925+
}
926+
"#,
927+
r#"
928+
macro_rules! foo {
929+
() => {
930+
trait FooB {
931+
fn foo<'lt>(&'lt self) {}
932+
}
933+
}
934+
}
935+
foo!();
936+
struct Foo(usize);
937+
938+
impl FooB for Foo {
939+
$0fn foo< 'lt>(& 'lt self){}
940+
941+
}
893942
"#,
894943
)
895944
}

crates/ide_assists/src/handlers/convert_iter_for_each_to_for.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ fn is_ref_and_impls_iter_method(
148148
let ty = sema.type_of_expr(&expr_behind_ref)?.adjusted();
149149
let scope = sema.scope(iterable.syntax());
150150
let krate = scope.module()?.krate();
151-
let traits_in_scope = scope.traits_in_scope();
151+
let traits_in_scope = scope.visible_traits();
152152
let iter_trait = FamousDefs(sema, Some(krate)).core_iter_Iterator()?;
153153

154154
let has_wanted_method = ty

crates/ide_assists/src/handlers/generate_is_empty_from_len.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@ fn get_impl_method(
9292
let scope = ctx.sema.scope(impl_.syntax());
9393
let krate = impl_def.module(db).krate();
9494
let ty = impl_def.self_ty(db);
95-
let traits_in_scope = scope.traits_in_scope();
95+
let traits_in_scope = scope.visible_traits();
9696
ty.iterate_method_candidates(db, krate, &traits_in_scope, Some(fn_name), |_, func| Some(func))
9797
}
9898

crates/ide_assists/src/handlers/replace_derive_with_manual_impl.rs

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use hir::ModuleDef;
2+
use ide_db::helpers::insert_whitespace_into_node::insert_ws_into;
23
use ide_db::helpers::{
34
get_path_at_cursor_in_tt, import_assets::NameToImport, mod_path_to_ast,
45
parse_tt_as_comma_sep_paths,
@@ -170,7 +171,7 @@ fn impl_def_from_trait(
170171
) -> Option<(ast::Impl, ast::AssocItem)> {
171172
let trait_ = trait_?;
172173
let target_scope = sema.scope(annotated_name.syntax());
173-
let trait_items = filter_assoc_items(sema.db, &trait_.items(sema.db), DefaultMethods::No);
174+
let trait_items = filter_assoc_items(sema, &trait_.items(sema.db), DefaultMethods::No);
174175
if trait_items.is_empty() {
175176
return None;
176177
}
@@ -193,6 +194,17 @@ fn impl_def_from_trait(
193194
node
194195
};
195196

197+
let trait_items = trait_items
198+
.into_iter()
199+
.map(|it| {
200+
if sema.hir_file_for(it.syntax()).is_macro() {
201+
if let Some(it) = ast::AssocItem::cast(insert_ws_into(it.syntax().clone())) {
202+
return it;
203+
}
204+
}
205+
it.clone_for_update()
206+
})
207+
.collect();
196208
let (impl_def, first_assoc_item) =
197209
add_trait_assoc_items_to_impl(sema, trait_items, trait_, impl_def, target_scope);
198210

crates/ide_assists/src/utils.rs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ use std::ops;
55
use itertools::Itertools;
66

77
pub(crate) use gen_trait_fn_body::gen_trait_fn_body;
8-
use hir::{db::HirDatabase, HasSource, HirDisplay};
8+
use hir::{db::HirDatabase, HirDisplay, Semantics};
99
use ide_db::{
1010
helpers::FamousDefs, helpers::SnippetCap, path_transform::PathTransform, RootDatabase,
1111
};
@@ -92,7 +92,7 @@ pub enum DefaultMethods {
9292
}
9393

9494
pub fn filter_assoc_items(
95-
db: &RootDatabase,
95+
sema: &Semantics<RootDatabase>,
9696
items: &[hir::AssocItem],
9797
default_methods: DefaultMethods,
9898
) -> Vec<ast::AssocItem> {
@@ -109,11 +109,11 @@ pub fn filter_assoc_items(
109109
items
110110
.iter()
111111
// Note: This throws away items with no source.
112-
.filter_map(|i| {
112+
.filter_map(|&i| {
113113
let item = match i {
114-
hir::AssocItem::Function(i) => ast::AssocItem::Fn(i.source(db)?.value),
115-
hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(i.source(db)?.value),
116-
hir::AssocItem::Const(i) => ast::AssocItem::Const(i.source(db)?.value),
114+
hir::AssocItem::Function(i) => ast::AssocItem::Fn(sema.source(i)?.value),
115+
hir::AssocItem::TypeAlias(i) => ast::AssocItem::TypeAlias(sema.source(i)?.value),
116+
hir::AssocItem::Const(i) => ast::AssocItem::Const(sema.source(i)?.value),
117117
};
118118
Some(item)
119119
})
@@ -129,7 +129,7 @@ pub fn filter_assoc_items(
129129
}
130130

131131
pub fn add_trait_assoc_items_to_impl(
132-
sema: &hir::Semantics<ide_db::RootDatabase>,
132+
sema: &Semantics<RootDatabase>,
133133
items: Vec<ast::AssocItem>,
134134
trait_: hir::Trait,
135135
impl_: ast::Impl,
@@ -140,7 +140,6 @@ pub fn add_trait_assoc_items_to_impl(
140140
let transform = PathTransform::trait_impl(&target_scope, &source_scope, trait_, impl_.clone());
141141

142142
let items = items.into_iter().map(|assoc_item| {
143-
let assoc_item = assoc_item.clone_for_update();
144143
transform.apply(assoc_item.syntax());
145144
assoc_item.remove_attrs_and_docs();
146145
assoc_item

crates/ide_completion/src/completions/dot.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ fn complete_methods(
7979
) {
8080
if let Some(krate) = ctx.krate {
8181
let mut seen_methods = FxHashSet::default();
82-
let traits_in_scope = ctx.scope.traits_in_scope();
82+
let traits_in_scope = ctx.scope.visible_traits();
8383
receiver.iterate_method_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, func| {
8484
if func.self_param(ctx.db).is_some() && seen_methods.insert(func.name(ctx.db)) {
8585
f(func);

crates/ide_completion/src/completions/qualified_path.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -187,7 +187,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
187187

188188
let krate = ctx.krate;
189189
if let Some(krate) = krate {
190-
let traits_in_scope = ctx.scope.traits_in_scope();
190+
let traits_in_scope = ctx.scope.visible_traits();
191191
ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
192192
add_assoc_item(acc, ctx, item);
193193
None::<()>
@@ -220,7 +220,7 @@ pub(crate) fn complete_qualified_path(acc: &mut Completions, ctx: &CompletionCon
220220
add_enum_variants(acc, ctx, e);
221221
}
222222

223-
let traits_in_scope = ctx.scope.traits_in_scope();
223+
let traits_in_scope = ctx.scope.visible_traits();
224224
let mut seen = FxHashSet::default();
225225
ty.iterate_path_candidates(ctx.db, krate, &traits_in_scope, None, |_ty, item| {
226226
// We might iterate candidates of a trait multiple times here, so deduplicate

crates/ide_db/src/helpers.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ pub mod generated_lints;
44
pub mod import_assets;
55
pub mod insert_use;
66
pub mod merge_imports;
7+
pub mod insert_whitespace_into_node;
78
pub mod node_ext;
89
pub mod rust_doc;
910

0 commit comments

Comments
 (0)