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