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: &dyn HirDatabase, def: Definition) -> Definition {
90 (|| {
91 let assoc = def.as_assoc_item(db)?;
92 let trait_ = assoc.implemented_trait(db)?;
93 assoc_item_of_trait(db, assoc, trait_)
94 })()
95 .unwrap_or(def)
96}
97
98pub(crate) fn as_trait_assoc_def(db: &dyn HirDatabase, def: Definition) -> Option<Definition> {
100 let assoc = def.as_assoc_item(db)?;
101 let trait_ = match assoc.container(db) {
102 hir::AssocItemContainer::Trait(_) => return Some(def),
103 hir::AssocItemContainer::Impl(i) => i.trait_(db),
104 }?;
105 assoc_item_of_trait(db, assoc, trait_)
106}
107
108fn assoc_item_of_trait(
109 db: &dyn HirDatabase,
110 assoc: hir::AssocItem,
111 trait_: hir::Trait,
112) -> Option<Definition> {
113 use hir::AssocItem::*;
114 let name = match assoc {
115 Function(it) => it.name(db),
116 Const(it) => it.name(db)?,
117 TypeAlias(it) => it.name(db),
118 };
119 let item = trait_.items(db).into_iter().find(|it| match (it, assoc) {
120 (Function(trait_func), Function(_)) => trait_func.name(db) == name,
121 (Const(trait_konst), Const(_)) => trait_konst.name(db).map_or(false, |it| it == name),
122 (TypeAlias(trait_type_alias), TypeAlias(_)) => trait_type_alias.name(db) == name,
123 _ => false,
124 })?;
125 Some(Definition::from(item))
126}
127
128#[cfg(test)]
129mod tests {
130 use expect_test::{Expect, expect};
131 use hir::{EditionedFileId, FilePosition, Semantics};
132 use span::Edition;
133 use syntax::ast::{self, AstNode};
134 use test_fixture::ChangeFixture;
135
136 use crate::RootDatabase;
137
138 pub(crate) fn position(
140 #[rust_analyzer::rust_fixture] ra_fixture: &str,
141 ) -> (RootDatabase, FilePosition) {
142 let mut database = RootDatabase::default();
143 let change_fixture = ChangeFixture::parse(ra_fixture);
144 database.apply_change(change_fixture.change);
145 let (file_id, range_or_offset) =
146 change_fixture.file_position.expect("expected a marker ($0)");
147
148 let file_id = EditionedFileId::from_span_file_id(&database, file_id);
149 let offset = range_or_offset.expect_offset();
150 (database, FilePosition { file_id, offset })
151 }
152
153 fn check_trait(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
154 let (db, position) = position(ra_fixture);
155 let sema = Semantics::new(&db);
156
157 let file = sema.parse(position.file_id);
158 let impl_block: ast::Impl =
159 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
160 let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block);
161 let actual = match trait_ {
162 Some(trait_) => trait_.name(&db).display(&db, Edition::CURRENT).to_string(),
163 None => String::new(),
164 };
165 expect.assert_eq(&actual);
166 }
167
168 fn check_missing_assoc(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
169 let (db, position) = position(ra_fixture);
170 let sema = Semantics::new(&db);
171
172 let file = sema.parse(position.file_id);
173 let impl_block: ast::Impl =
174 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
175 let items =
176 hir::attach_db(&db, || crate::traits::get_missing_assoc_items(&sema, &impl_block));
177 let actual = items
178 .into_iter()
179 .map(|item| item.name(&db).unwrap().display(&db, Edition::CURRENT).to_string())
180 .collect::<Vec<_>>()
181 .join("\n");
182 expect.assert_eq(&actual);
183 }
184
185 #[test]
186 fn resolve_trait() {
187 check_trait(
188 r#"
189pub trait Foo {
190 fn bar();
191}
192impl Foo for u8 {
193 $0
194}
195 "#,
196 expect![["Foo"]],
197 );
198 check_trait(
199 r#"
200pub trait Foo {
201 fn bar();
202}
203impl Foo for u8 {
204 fn bar() {
205 fn baz() {
206 $0
207 }
208 baz();
209 }
210}
211 "#,
212 expect![["Foo"]],
213 );
214 check_trait(
215 r#"
216pub trait Foo {
217 fn bar();
218}
219pub struct Bar;
220impl Bar {
221 $0
222}
223 "#,
224 expect![[""]],
225 );
226 }
227
228 #[test]
229 fn missing_assoc_items() {
230 check_missing_assoc(
231 r#"
232pub trait Foo {
233 const FOO: u8;
234 fn bar();
235}
236impl Foo for u8 {
237 $0
238}"#,
239 expect![[r#"
240 FOO
241 bar"#]],
242 );
243
244 check_missing_assoc(
245 r#"
246pub trait Foo {
247 const FOO: u8;
248 fn bar();
249}
250impl Foo for u8 {
251 const FOO: u8 = 10;
252 $0
253}"#,
254 expect![[r#"
255 bar"#]],
256 );
257
258 check_missing_assoc(
259 r#"
260pub trait Foo {
261 const FOO: u8;
262 fn bar();
263}
264impl Foo for u8 {
265 const FOO: u8 = 10;
266 fn bar() {$0}
267}"#,
268 expect![[r#""#]],
269 );
270
271 check_missing_assoc(
272 r#"
273pub struct Foo;
274impl Foo {
275 fn bar() {$0}
276}"#,
277 expect![[r#""#]],
278 );
279
280 check_missing_assoc(
281 r#"
282trait Tr {
283 fn required();
284}
285macro_rules! m {
286 () => { fn required() {} };
287}
288impl Tr for () {
289 m!();
290 $0
291}
292
293 "#,
294 expect![[r#""#]],
295 );
296 }
297}