Skip to content

Commit dfd7513

Browse files
committed
Move find_struct_impl to assist utils
1 parent 13d663d commit dfd7513

File tree

3 files changed

+85
-154
lines changed

3 files changed

+85
-154
lines changed

crates/assists/src/handlers/generate_enum_match_method.rs

Lines changed: 10 additions & 90 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,9 @@
1-
use hir::Adt;
2-
use stdx::format_to;
1+
use stdx::{format_to, to_lower_snake_case};
32
use syntax::ast::{self, AstNode, NameOwner};
43
use syntax::{ast::VisibilityOwner, T};
54
use test_utils::mark;
65

7-
use crate::{AssistContext, AssistId, AssistKind, Assists};
6+
use crate::{utils::find_struct_impl, AssistContext, AssistId, AssistKind, Assists};
87

98
// Assist: generate_enum_match_method
109
//
@@ -40,10 +39,14 @@ pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext)
4039
return None;
4140
}
4241

43-
let fn_name = to_lower_snake_case(&format!("{}", variant_name));
42+
let fn_name = to_lower_snake_case(&variant_name.to_string());
4443

4544
// Return early if we've found an existing new fn
46-
let impl_def = find_struct_impl(&ctx, &parent_enum, format!("is_{}", fn_name).as_str())?;
45+
let impl_def = find_struct_impl(
46+
&ctx,
47+
&ast::AdtDef::Enum(parent_enum.clone()),
48+
format!("is_{}", fn_name).as_str(),
49+
)?;
4750

4851
let target = variant.syntax().text_range();
4952
acc.add(
@@ -95,104 +98,22 @@ pub(crate) fn generate_enum_match_method(acc: &mut Assists, ctx: &AssistContext)
9598
// parameters
9699
fn generate_impl_text(strukt: &ast::Enum, code: &str) -> String {
97100
let mut buf = String::with_capacity(code.len());
98-
buf.push_str("\n\nimpl");
99-
buf.push_str(" ");
101+
buf.push_str("\n\nimpl ");
100102
buf.push_str(strukt.name().unwrap().text());
101103
format_to!(buf, " {{\n{}\n}}", code);
102104
buf
103105
}
104106

105-
fn to_lower_snake_case(s: &str) -> String {
106-
let mut buf = String::with_capacity(s.len());
107-
let mut prev = false;
108-
for c in s.chars() {
109-
if c.is_ascii_uppercase() && prev {
110-
buf.push('_')
111-
}
112-
prev = true;
113-
114-
buf.push(c.to_ascii_lowercase());
115-
}
116-
buf
117-
}
118-
119-
// Uses a syntax-driven approach to find any impl blocks for the struct that
120-
// exist within the module/file
121-
//
122-
// Returns `None` if we've found an existing `new` fn
123-
//
124-
// FIXME: change the new fn checking to a more semantic approach when that's more
125-
// viable (e.g. we process proc macros, etc)
126-
fn find_struct_impl(
127-
ctx: &AssistContext,
128-
strukt: &ast::Enum,
129-
name: &str,
130-
) -> Option<Option<ast::Impl>> {
131-
let db = ctx.db();
132-
let module = strukt.syntax().ancestors().find(|node| {
133-
ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind())
134-
})?;
135-
136-
let struct_def = ctx.sema.to_def(strukt)?;
137-
138-
let block = module.descendants().filter_map(ast::Impl::cast).find_map(|impl_blk| {
139-
let blk = ctx.sema.to_def(&impl_blk)?;
140-
141-
// FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}`
142-
// (we currently use the wrong type parameter)
143-
// also we wouldn't want to use e.g. `impl S<u32>`
144-
let same_ty = match blk.target_ty(db).as_adt() {
145-
Some(def) => def == Adt::Enum(struct_def),
146-
None => false,
147-
};
148-
let not_trait_impl = blk.target_trait(db).is_none();
149-
150-
if !(same_ty && not_trait_impl) {
151-
None
152-
} else {
153-
Some(impl_blk)
154-
}
155-
});
156-
157-
if let Some(ref impl_blk) = block {
158-
if has_fn(impl_blk, name) {
159-
mark::hit!(test_gen_enum_match_impl_already_exists);
160-
return None;
161-
}
162-
}
163-
164-
Some(block)
165-
}
166-
167-
fn has_fn(imp: &ast::Impl, rhs_name: &str) -> bool {
168-
if let Some(il) = imp.assoc_item_list() {
169-
for item in il.assoc_items() {
170-
if let ast::AssocItem::Fn(f) = item {
171-
if let Some(name) = f.name() {
172-
if name.text().eq_ignore_ascii_case(rhs_name) {
173-
return true;
174-
}
175-
}
176-
}
177-
}
178-
}
179-
180-
false
181-
}
182-
183107
#[cfg(test)]
184108
mod tests {
185-
use ide_db::helpers::FamousDefs;
186109
use test_utils::mark;
187110

188111
use crate::tests::{check_assist, check_assist_not_applicable};
189112

190113
use super::*;
191114

192115
fn check_not_applicable(ra_fixture: &str) {
193-
let fixture =
194-
format!("//- /main.rs crate:main deps:core\n{}\n{}", ra_fixture, FamousDefs::FIXTURE);
195-
check_assist_not_applicable(generate_enum_match_method, &fixture)
116+
check_assist_not_applicable(generate_enum_match_method, ra_fixture)
196117
}
197118

198119
#[test]
@@ -221,7 +142,6 @@ impl Variant {
221142

222143
#[test]
223144
fn test_generate_enum_match_already_implemented() {
224-
mark::check!(test_gen_enum_match_impl_already_exists);
225145
check_not_applicable(
226146
r#"
227147
enum Variant {

crates/assists/src/handlers/generate_new.rs

Lines changed: 2 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
use hir::Adt;
21
use itertools::Itertools;
32
use stdx::format_to;
43
use syntax::{
54
ast::{self, AstNode, GenericParamsOwner, NameOwner, StructKind, VisibilityOwner},
65
SmolStr, T,
76
};
87

9-
use crate::{AssistContext, AssistId, AssistKind, Assists};
8+
use crate::{utils::find_struct_impl, AssistContext, AssistId, AssistKind, Assists};
109

1110
// Assist: generate_new
1211
//
@@ -38,7 +37,7 @@ pub(crate) fn generate_new(acc: &mut Assists, ctx: &AssistContext) -> Option<()>
3837
};
3938

4039
// Return early if we've found an existing new fn
41-
let impl_def = find_struct_impl(&ctx, &strukt)?;
40+
let impl_def = find_struct_impl(&ctx, &ast::AdtDef::Struct(strukt.clone()), "new")?;
4241

4342
let target = strukt.syntax().text_range();
4443
acc.add(AssistId("generate_new", AssistKind::Generate), "Generate `new`", target, |builder| {
@@ -111,65 +110,6 @@ fn generate_impl_text(strukt: &ast::Struct, code: &str) -> String {
111110
buf
112111
}
113112

114-
// Uses a syntax-driven approach to find any impl blocks for the struct that
115-
// exist within the module/file
116-
//
117-
// Returns `None` if we've found an existing `new` fn
118-
//
119-
// FIXME: change the new fn checking to a more semantic approach when that's more
120-
// viable (e.g. we process proc macros, etc)
121-
fn find_struct_impl(ctx: &AssistContext, strukt: &ast::Struct) -> Option<Option<ast::Impl>> {
122-
let db = ctx.db();
123-
let module = strukt.syntax().ancestors().find(|node| {
124-
ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind())
125-
})?;
126-
127-
let struct_def = ctx.sema.to_def(strukt)?;
128-
129-
let block = module.descendants().filter_map(ast::Impl::cast).find_map(|impl_blk| {
130-
let blk = ctx.sema.to_def(&impl_blk)?;
131-
132-
// FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}`
133-
// (we currently use the wrong type parameter)
134-
// also we wouldn't want to use e.g. `impl S<u32>`
135-
let same_ty = match blk.target_ty(db).as_adt() {
136-
Some(def) => def == Adt::Struct(struct_def),
137-
None => false,
138-
};
139-
let not_trait_impl = blk.target_trait(db).is_none();
140-
141-
if !(same_ty && not_trait_impl) {
142-
None
143-
} else {
144-
Some(impl_blk)
145-
}
146-
});
147-
148-
if let Some(ref impl_blk) = block {
149-
if has_new_fn(impl_blk) {
150-
return None;
151-
}
152-
}
153-
154-
Some(block)
155-
}
156-
157-
fn has_new_fn(imp: &ast::Impl) -> bool {
158-
if let Some(il) = imp.assoc_item_list() {
159-
for item in il.assoc_items() {
160-
if let ast::AssocItem::Fn(f) = item {
161-
if let Some(name) = f.name() {
162-
if name.text().eq_ignore_ascii_case("new") {
163-
return true;
164-
}
165-
}
166-
}
167-
}
168-
}
169-
170-
false
171-
}
172-
173113
#[cfg(test)]
174114
mod tests {
175115
use crate::tests::{check_assist, check_assist_not_applicable, check_assist_target};

crates/assists/src/utils.rs

Lines changed: 73 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
33
use std::ops;
44

5-
use hir::HasSource;
5+
use hir::{Adt, HasSource};
66
use ide_db::{helpers::SnippetCap, RootDatabase};
77
use itertools::Itertools;
88
use syntax::{
@@ -15,7 +15,10 @@ use syntax::{
1515
SyntaxNode, TextSize, T,
1616
};
1717

18-
use crate::ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams};
18+
use crate::{
19+
assist_context::AssistContext,
20+
ast_transform::{self, AstTransform, QualifyPaths, SubstituteTypeParams},
21+
};
1922

2023
pub(crate) fn unwrap_trivial_block(block: ast::BlockExpr) -> ast::Expr {
2124
extract_trivial_expression(&block)
@@ -267,3 +270,71 @@ pub(crate) fn does_pat_match_variant(pat: &ast::Pat, var: &ast::Pat) -> bool {
267270

268271
pat_head == var_head
269272
}
273+
274+
// Uses a syntax-driven approach to find any impl blocks for the struct that
275+
// exist within the module/file
276+
//
277+
// Returns `None` if we've found an existing `new` fn
278+
//
279+
// FIXME: change the new fn checking to a more semantic approach when that's more
280+
// viable (e.g. we process proc macros, etc)
281+
pub(crate) fn find_struct_impl(
282+
ctx: &AssistContext,
283+
strukt: &ast::AdtDef,
284+
name: &str,
285+
) -> Option<Option<ast::Impl>> {
286+
let db = ctx.db();
287+
let module = strukt.syntax().ancestors().find(|node| {
288+
ast::Module::can_cast(node.kind()) || ast::SourceFile::can_cast(node.kind())
289+
})?;
290+
291+
let struct_def = match strukt {
292+
ast::AdtDef::Enum(e) => Adt::Enum(ctx.sema.to_def(e)?),
293+
ast::AdtDef::Struct(s) => Adt::Struct(ctx.sema.to_def(s)?),
294+
ast::AdtDef::Union(u) => Adt::Union(ctx.sema.to_def(u)?),
295+
};
296+
297+
let block = module.descendants().filter_map(ast::Impl::cast).find_map(|impl_blk| {
298+
let blk = ctx.sema.to_def(&impl_blk)?;
299+
300+
// FIXME: handle e.g. `struct S<T>; impl<U> S<U> {}`
301+
// (we currently use the wrong type parameter)
302+
// also we wouldn't want to use e.g. `impl S<u32>`
303+
304+
let same_ty = match blk.target_ty(db).as_adt() {
305+
Some(def) => def == struct_def,
306+
None => false,
307+
};
308+
let not_trait_impl = blk.target_trait(db).is_none();
309+
310+
if !(same_ty && not_trait_impl) {
311+
None
312+
} else {
313+
Some(impl_blk)
314+
}
315+
});
316+
317+
if let Some(ref impl_blk) = block {
318+
if has_fn(impl_blk, name) {
319+
return None;
320+
}
321+
}
322+
323+
Some(block)
324+
}
325+
326+
fn has_fn(imp: &ast::Impl, rhs_name: &str) -> bool {
327+
if let Some(il) = imp.assoc_item_list() {
328+
for item in il.assoc_items() {
329+
if let ast::AssocItem::Fn(f) = item {
330+
if let Some(name) = f.name() {
331+
if name.text().eq_ignore_ascii_case(rhs_name) {
332+
return true;
333+
}
334+
}
335+
}
336+
}
337+
}
338+
339+
false
340+
}

0 commit comments

Comments
 (0)