1use crate::{RootDatabase, defs::Definition};
4use hir::{AsAssocItem, HasCrate, Semantics, db::HirDatabase, sym};
5use rustc_hash::FxHashSet;
6use syntax::{AstNode, ast};
7
8pub fn resolve_target_trait(
10 sema: &Semantics<'_, RootDatabase>,
11 impl_def: &ast::Impl,
12) -> Option<hir::Trait> {
13 let ast_path =
14 impl_def.trait_().map(|it| it.syntax().clone()).and_then(ast::PathType::cast)?.path()?;
15
16 match sema.resolve_path(&ast_path) {
17 Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def),
18 _ => None,
19 }
20}
21
22pub fn get_missing_assoc_items(
25 sema: &Semantics<'_, RootDatabase>,
26 impl_def: &ast::Impl,
27) -> Vec<hir::AssocItem> {
28 let imp = match sema.to_def(impl_def) {
29 Some(it) => it,
30 None => return vec![],
31 };
32
33 let mut impl_fns_consts = FxHashSet::default();
36 let mut impl_type = FxHashSet::default();
37
38 for item in imp.items(sema.db) {
39 match item {
40 hir::AssocItem::Function(it) => {
41 impl_fns_consts.insert(it.name(sema.db));
42 }
43 hir::AssocItem::Const(it) => {
44 if let Some(name) = it.name(sema.db) {
45 impl_fns_consts.insert(name);
46 }
47 }
48 hir::AssocItem::TypeAlias(it) => {
49 impl_type.insert(it.name(sema.db));
50 }
51 }
52 }
53
54 let Some(target_trait) = imp.trait_(sema.db) else { return Vec::new() };
55
56 let drop_trait = hir::Trait::lang(sema.db, imp.krate(sema.db), hir::LangItem::Drop);
59 if let Some(drop_trait) = drop_trait
60 && target_trait == drop_trait
61 {
62 return if impl_fns_consts.is_empty() {
63 let drop_drop = drop_trait.function(sema.db, sym::drop);
65 match drop_drop {
66 Some(drop_drop) => vec![hir::AssocItem::Function(drop_drop)],
67 None => Vec::new(),
68 }
69 } else {
70 Vec::new()
72 };
73 }
74
75 target_trait
76 .items(sema.db)
77 .into_iter()
78 .filter(|i| match i {
79 hir::AssocItem::Function(f) => !impl_fns_consts.contains(&f.name(sema.db)),
80 hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db)),
81 hir::AssocItem::Const(c) => {
82 c.name(sema.db).map(|n| !impl_fns_consts.contains(&n)).unwrap_or_default()
83 }
84 })
85 .collect()
86}
87
88pub(crate) fn convert_to_def_in_trait<'db>(
90 db: &'db dyn HirDatabase,
91 def: Definition<'db>,
92) -> Definition<'db> {
93 (|| {
94 let assoc = def.as_assoc_item(db)?;
95 let trait_ = assoc.implemented_trait(db)?;
96 assoc_item_of_trait(db, assoc, trait_)
97 })()
98 .unwrap_or(def)
99}
100
101pub(crate) fn as_trait_assoc_def<'db>(
103 db: &dyn HirDatabase,
104 def: Definition<'db>,
105) -> Option<Definition<'db>> {
106 let assoc = def.as_assoc_item(db)?;
107 let trait_ = match assoc.container(db) {
108 hir::AssocItemContainer::Trait(_) => return Some(def),
109 hir::AssocItemContainer::Impl(i) => i.trait_(db),
110 }?;
111 assoc_item_of_trait(db, assoc, trait_)
112}
113
114fn assoc_item_of_trait<'db>(
115 db: &dyn HirDatabase,
116 assoc: hir::AssocItem,
117 trait_: hir::Trait,
118) -> Option<Definition<'db>> {
119 use hir::AssocItem::*;
120 let name = match assoc {
121 Function(it) => it.name(db),
122 Const(it) => it.name(db)?,
123 TypeAlias(it) => it.name(db),
124 };
125 let item = trait_.items(db).into_iter().find(|it| match (it, assoc) {
126 (Function(trait_func), Function(_)) => trait_func.name(db) == name,
127 (Const(trait_konst), Const(_)) => trait_konst.name(db).map_or(false, |it| it == name),
128 (TypeAlias(trait_type_alias), TypeAlias(_)) => trait_type_alias.name(db) == name,
129 _ => false,
130 })?;
131 Some(Definition::from(item))
132}
133
134#[cfg(test)]
135mod tests {
136 use expect_test::{Expect, expect};
137 use hir::{EditionedFileId, FilePosition, Semantics};
138 use span::Edition;
139 use syntax::ast::{self, AstNode};
140 use test_fixture::ChangeFixture;
141
142 use crate::RootDatabase;
143
144 pub(crate) fn position(
146 #[rust_analyzer::rust_fixture] ra_fixture: &str,
147 ) -> (RootDatabase, FilePosition) {
148 let mut database = RootDatabase::default();
149 let change_fixture = ChangeFixture::parse(ra_fixture);
150 database.apply_change(change_fixture.change);
151 let (file_id, range_or_offset) =
152 change_fixture.file_position.expect("expected a marker ($0)");
153
154 let file_id = EditionedFileId::from_span_file_id(&database, file_id);
155 let offset = range_or_offset.expect_offset();
156 (database, FilePosition { file_id, offset })
157 }
158
159 fn check_trait(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
160 let (db, position) = position(ra_fixture);
161 let sema = Semantics::new(&db);
162
163 let file = sema.parse(position.file_id);
164 let impl_block: ast::Impl =
165 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
166 let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block);
167 let actual = match trait_ {
168 Some(trait_) => trait_.name(&db).display(&db, Edition::CURRENT).to_string(),
169 None => String::new(),
170 };
171 expect.assert_eq(&actual);
172 }
173
174 fn check_missing_assoc(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
175 let (db, position) = position(ra_fixture);
176 let sema = Semantics::new(&db);
177
178 let file = sema.parse(position.file_id);
179 let impl_block: ast::Impl =
180 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
181 let items =
182 hir::attach_db(&db, || crate::traits::get_missing_assoc_items(&sema, &impl_block));
183 let actual = items
184 .into_iter()
185 .map(|item| item.name(&db).unwrap().display(&db, Edition::CURRENT).to_string())
186 .collect::<Vec<_>>()
187 .join("\n");
188 expect.assert_eq(&actual);
189 }
190
191 #[test]
192 fn resolve_trait() {
193 check_trait(
194 r#"
195pub trait Foo {
196 fn bar();
197}
198impl Foo for u8 {
199 $0
200}
201 "#,
202 expect![["Foo"]],
203 );
204 check_trait(
205 r#"
206pub trait Foo {
207 fn bar();
208}
209impl Foo for u8 {
210 fn bar() {
211 fn baz() {
212 $0
213 }
214 baz();
215 }
216}
217 "#,
218 expect![["Foo"]],
219 );
220 check_trait(
221 r#"
222pub trait Foo {
223 fn bar();
224}
225pub struct Bar;
226impl Bar {
227 $0
228}
229 "#,
230 expect![[""]],
231 );
232 }
233
234 #[test]
235 fn missing_assoc_items() {
236 check_missing_assoc(
237 r#"
238pub trait Foo {
239 const FOO: u8;
240 fn bar();
241}
242impl Foo for u8 {
243 $0
244}"#,
245 expect![[r#"
246 FOO
247 bar"#]],
248 );
249
250 check_missing_assoc(
251 r#"
252pub trait Foo {
253 const FOO: u8;
254 fn bar();
255}
256impl Foo for u8 {
257 const FOO: u8 = 10;
258 $0
259}"#,
260 expect![[r#"
261 bar"#]],
262 );
263
264 check_missing_assoc(
265 r#"
266pub trait Foo {
267 const FOO: u8;
268 fn bar();
269}
270impl Foo for u8 {
271 const FOO: u8 = 10;
272 fn bar() {$0}
273}"#,
274 expect![[r#""#]],
275 );
276
277 check_missing_assoc(
278 r#"
279pub struct Foo;
280impl Foo {
281 fn bar() {$0}
282}"#,
283 expect![[r#""#]],
284 );
285
286 check_missing_assoc(
287 r#"
288trait Tr {
289 fn required();
290}
291macro_rules! m {
292 () => { fn required() {} };
293}
294impl Tr for () {
295 m!();
296 $0
297}
298
299 "#,
300 expect![[r#""#]],
301 );
302 }
303}