1 //! Functionality for obtaining data related to traits from the DB.
3 use crate::{defs::Definition, RootDatabase};
4 use hir::{db::HirDatabase, AsAssocItem, Semantics};
5 use rustc_hash::FxHashSet;
6 use syntax::{ast, AstNode};
8 /// Given the `impl` block, attempts to find the trait this `impl` corresponds to.
9 pub fn resolve_target_trait(
10 sema: &Semantics<'_, RootDatabase>,
12 ) -> Option<hir::Trait> {
14 impl_def.trait_().map(|it| it.syntax().clone()).and_then(ast::PathType::cast)?.path()?;
16 match sema.resolve_path(&ast_path) {
17 Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def),
22 /// Given the `impl` block, returns the list of associated items (e.g. functions or types) that are
23 /// missing in this `impl` block.
24 pub fn get_missing_assoc_items(
25 sema: &Semantics<'_, RootDatabase>,
27 ) -> Vec<hir::AssocItem> {
28 let imp = match sema.to_def(impl_def) {
30 None => return vec![],
33 // Names must be unique between constants and functions. However, type aliases
34 // may share the same name as a function or constant.
35 let mut impl_fns_consts = FxHashSet::default();
36 let mut impl_type = FxHashSet::default();
38 for item in imp.items(sema.db) {
40 hir::AssocItem::Function(it) => {
41 impl_fns_consts.insert(it.name(sema.db).to_string());
43 hir::AssocItem::Const(it) => {
44 if let Some(name) = it.name(sema.db) {
45 impl_fns_consts.insert(name.to_string());
48 hir::AssocItem::TypeAlias(it) => {
49 impl_type.insert(it.name(sema.db).to_string());
54 resolve_target_trait(sema, impl_def).map_or(vec![], |target_trait| {
59 hir::AssocItem::Function(f) => {
60 !impl_fns_consts.contains(&f.name(sema.db).to_string())
62 hir::AssocItem::TypeAlias(t) => !impl_type.contains(&t.name(sema.db).to_string()),
63 hir::AssocItem::Const(c) => c
65 .map(|n| !impl_fns_consts.contains(&n.to_string()))
72 /// Converts associated trait impl items to their trait definition counterpart
73 pub(crate) fn convert_to_def_in_trait(db: &dyn HirDatabase, def: Definition) -> Definition {
75 let assoc = def.as_assoc_item(db)?;
76 let trait_ = assoc.containing_trait_impl(db)?;
77 assoc_item_of_trait(db, assoc, trait_)
82 /// If this is an trait (impl) assoc item, returns the assoc item of the corresponding trait definition.
83 pub(crate) fn as_trait_assoc_def(db: &dyn HirDatabase, def: Definition) -> Option<Definition> {
84 let assoc = def.as_assoc_item(db)?;
85 let trait_ = match assoc.container(db) {
86 hir::AssocItemContainer::Trait(_) => return Some(def),
87 hir::AssocItemContainer::Impl(i) => i.trait_(db),
89 assoc_item_of_trait(db, assoc, trait_)
92 fn assoc_item_of_trait(
94 assoc: hir::AssocItem,
96 ) -> Option<Definition> {
97 use hir::AssocItem::*;
98 let name = match assoc {
99 Function(it) => it.name(db),
100 Const(it) => it.name(db)?,
101 TypeAlias(it) => it.name(db),
103 let item = trait_.items(db).into_iter().find(|it| match (it, assoc) {
104 (Function(trait_func), Function(_)) => trait_func.name(db) == name,
105 (Const(trait_konst), Const(_)) => trait_konst.name(db).map_or(false, |it| it == name),
106 (TypeAlias(trait_type_alias), TypeAlias(_)) => trait_type_alias.name(db) == name,
109 Some(Definition::from(item))
114 use base_db::{fixture::ChangeFixture, FilePosition};
115 use expect_test::{expect, Expect};
117 use syntax::ast::{self, AstNode};
119 use crate::RootDatabase;
121 /// Creates analysis from a multi-file fixture, returns positions marked with $0.
122 pub(crate) fn position(ra_fixture: &str) -> (RootDatabase, FilePosition) {
123 let change_fixture = ChangeFixture::parse(ra_fixture);
124 let mut database = RootDatabase::default();
125 database.apply_change(change_fixture.change);
126 let (file_id, range_or_offset) =
127 change_fixture.file_position.expect("expected a marker ($0)");
128 let offset = range_or_offset.expect_offset();
129 (database, FilePosition { file_id, offset })
132 fn check_trait(ra_fixture: &str, expect: Expect) {
133 let (db, position) = position(ra_fixture);
134 let sema = Semantics::new(&db);
135 let file = sema.parse(position.file_id);
136 let impl_block: ast::Impl =
137 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
138 let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block);
139 let actual = match trait_ {
140 Some(trait_) => trait_.name(&db).to_string(),
141 None => String::new(),
143 expect.assert_eq(&actual);
146 fn check_missing_assoc(ra_fixture: &str, expect: Expect) {
147 let (db, position) = position(ra_fixture);
148 let sema = Semantics::new(&db);
149 let file = sema.parse(position.file_id);
150 let impl_block: ast::Impl =
151 sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
152 let items = crate::traits::get_missing_assoc_items(&sema, &impl_block);
155 .map(|item| item.name(&db).unwrap().to_string())
158 expect.assert_eq(&actual);
205 fn missing_assoc_items() {
262 () => { fn required() {} };