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(sema.db).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::{EditionedFileId, FilePosition, Semantics};
118 use span::Edition;
119 use syntax::ast::{self, AstNode};
120 use test_fixture::ChangeFixture;
121
122 use crate::RootDatabase;
123
124 pub(crate) fn position(
126 #[rust_analyzer::rust_fixture] ra_fixture: &str,
127 ) -> (RootDatabase, FilePosition) {
128 let mut database = RootDatabase::default();
129 let change_fixture = ChangeFixture::parse(ra_fixture);
130 database.apply_change(change_fixture.change);
131 let (file_id, range_or_offset) =
132 change_fixture.file_position.expect("expected a marker ($0)");
133
134 let file_id = EditionedFileId::from_span_file_id(&database, file_id);
135 let offset = range_or_offset.expect_offset();
136 (database, FilePosition { file_id, offset })
137 }
138
139 fn check_trait(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
140 let (db, position) = position(ra_fixture);
141 let sema = Semantics::new(&db);
142
143 let file = sema.parse(position.file_id);
144 let impl_block: ast::Impl =
145 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
146 let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block);
147 let actual = match trait_ {
148 Some(trait_) => trait_.name(&db).display(&db, Edition::CURRENT).to_string(),
149 None => String::new(),
150 };
151 expect.assert_eq(&actual);
152 }
153
154 fn check_missing_assoc(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
155 let (db, position) = position(ra_fixture);
156 let sema = Semantics::new(&db);
157
158 let file = sema.parse(position.file_id);
159 let impl_block: ast::Impl =
160 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
161 let items = crate::traits::get_missing_assoc_items(&sema, &impl_block);
162 let actual = items
163 .into_iter()
164 .map(|item| item.name(&db).unwrap().display(&db, Edition::CURRENT).to_string())
165 .collect::<Vec<_>>()
166 .join("\n");
167 expect.assert_eq(&actual);
168 }
169
170 #[test]
171 fn resolve_trait() {
172 check_trait(
173 r#"
174pub trait Foo {
175 fn bar();
176}
177impl Foo for u8 {
178 $0
179}
180 "#,
181 expect![["Foo"]],
182 );
183 check_trait(
184 r#"
185pub trait Foo {
186 fn bar();
187}
188impl Foo for u8 {
189 fn bar() {
190 fn baz() {
191 $0
192 }
193 baz();
194 }
195}
196 "#,
197 expect![["Foo"]],
198 );
199 check_trait(
200 r#"
201pub trait Foo {
202 fn bar();
203}
204pub struct Bar;
205impl Bar {
206 $0
207}
208 "#,
209 expect![[""]],
210 );
211 }
212
213 #[test]
214 fn missing_assoc_items() {
215 check_missing_assoc(
216 r#"
217pub trait Foo {
218 const FOO: u8;
219 fn bar();
220}
221impl Foo for u8 {
222 $0
223}"#,
224 expect![[r#"
225 FOO
226 bar"#]],
227 );
228
229 check_missing_assoc(
230 r#"
231pub trait Foo {
232 const FOO: u8;
233 fn bar();
234}
235impl Foo for u8 {
236 const FOO: u8 = 10;
237 $0
238}"#,
239 expect![[r#"
240 bar"#]],
241 );
242
243 check_missing_assoc(
244 r#"
245pub trait Foo {
246 const FOO: u8;
247 fn bar();
248}
249impl Foo for u8 {
250 const FOO: u8 = 10;
251 fn bar() {$0}
252}"#,
253 expect![[r#""#]],
254 );
255
256 check_missing_assoc(
257 r#"
258pub struct Foo;
259impl Foo {
260 fn bar() {$0}
261}"#,
262 expect![[r#""#]],
263 );
264
265 check_missing_assoc(
266 r#"
267trait Tr {
268 fn required();
269}
270macro_rules! m {
271 () => { fn required() {} };
272}
273impl Tr for () {
274 m!();
275 $0
276}
277
278 "#,
279 expect![[r#""#]],
280 );
281 }
282}