mirror of
				https://github.com/rust-lang/rust-analyzer.git
				synced 2025-10-30 19:49:36 +00:00 
			
		
		
		
	 c58ddafe90
			
		
	
	
		c58ddafe90
		
	
	
	
	
		
			
			And make more queries non-interned. Also flip the default for queries, now the default is to not intern and to intern a query you need to say `invoke_interned`.
		
			
				
	
	
		
			281 lines
		
	
	
	
		
			7.9 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			281 lines
		
	
	
	
		
			7.9 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| //! Functionality for obtaining data related to traits from the DB.
 | |
| 
 | |
| use crate::{RootDatabase, defs::Definition};
 | |
| use hir::{AsAssocItem, Semantics, db::HirDatabase};
 | |
| use rustc_hash::FxHashSet;
 | |
| use syntax::{AstNode, ast};
 | |
| 
 | |
| /// Given the `impl` block, attempts to find the trait this `impl` corresponds to.
 | |
| pub fn resolve_target_trait(
 | |
|     sema: &Semantics<'_, RootDatabase>,
 | |
|     impl_def: &ast::Impl,
 | |
| ) -> Option<hir::Trait> {
 | |
|     let ast_path =
 | |
|         impl_def.trait_().map(|it| it.syntax().clone()).and_then(ast::PathType::cast)?.path()?;
 | |
| 
 | |
|     match sema.resolve_path(&ast_path) {
 | |
|         Some(hir::PathResolution::Def(hir::ModuleDef::Trait(def))) => Some(def),
 | |
|         _ => None,
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// Given the `impl` block, returns the list of associated items (e.g. functions or types) that are
 | |
| /// missing in this `impl` block.
 | |
| pub fn get_missing_assoc_items(
 | |
|     sema: &Semantics<'_, RootDatabase>,
 | |
|     impl_def: &ast::Impl,
 | |
| ) -> Vec<hir::AssocItem> {
 | |
|     let imp = match sema.to_def(impl_def) {
 | |
|         Some(it) => it,
 | |
|         None => return vec![],
 | |
|     };
 | |
| 
 | |
|     // Names must be unique between constants and functions. However, type aliases
 | |
|     // may share the same name as a function or constant.
 | |
|     let mut impl_fns_consts = FxHashSet::default();
 | |
|     let mut impl_type = FxHashSet::default();
 | |
|     let edition = imp.module(sema.db).krate().edition(sema.db);
 | |
| 
 | |
|     for item in imp.items(sema.db) {
 | |
|         match item {
 | |
|             hir::AssocItem::Function(it) => {
 | |
|                 impl_fns_consts.insert(it.name(sema.db).display(sema.db, edition).to_string());
 | |
|             }
 | |
|             hir::AssocItem::Const(it) => {
 | |
|                 if let Some(name) = it.name(sema.db) {
 | |
|                     impl_fns_consts.insert(name.display(sema.db, edition).to_string());
 | |
|                 }
 | |
|             }
 | |
|             hir::AssocItem::TypeAlias(it) => {
 | |
|                 impl_type.insert(it.name(sema.db).display(sema.db, edition).to_string());
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     resolve_target_trait(sema, impl_def).map_or(vec![], |target_trait| {
 | |
|         target_trait
 | |
|             .items(sema.db)
 | |
|             .into_iter()
 | |
|             .filter(|i| match i {
 | |
|                 hir::AssocItem::Function(f) => !impl_fns_consts
 | |
|                     .contains(&f.name(sema.db).display(sema.db, edition).to_string()),
 | |
|                 hir::AssocItem::TypeAlias(t) => {
 | |
|                     !impl_type.contains(&t.name(sema.db).display(sema.db, edition).to_string())
 | |
|                 }
 | |
|                 hir::AssocItem::Const(c) => c
 | |
|                     .name(sema.db)
 | |
|                     .map(|n| !impl_fns_consts.contains(&n.display(sema.db, edition).to_string()))
 | |
|                     .unwrap_or_default(),
 | |
|             })
 | |
|             .collect()
 | |
|     })
 | |
| }
 | |
| 
 | |
| /// Converts associated trait impl items to their trait definition counterpart
 | |
| pub(crate) fn convert_to_def_in_trait(db: &dyn HirDatabase, def: Definition) -> Definition {
 | |
|     (|| {
 | |
|         let assoc = def.as_assoc_item(db)?;
 | |
|         let trait_ = assoc.implemented_trait(db)?;
 | |
|         assoc_item_of_trait(db, assoc, trait_)
 | |
|     })()
 | |
|     .unwrap_or(def)
 | |
| }
 | |
| 
 | |
| /// If this is an trait (impl) assoc item, returns the assoc item of the corresponding trait definition.
 | |
| pub(crate) fn as_trait_assoc_def(db: &dyn HirDatabase, def: Definition) -> Option<Definition> {
 | |
|     let assoc = def.as_assoc_item(db)?;
 | |
|     let trait_ = match assoc.container(db) {
 | |
|         hir::AssocItemContainer::Trait(_) => return Some(def),
 | |
|         hir::AssocItemContainer::Impl(i) => i.trait_(db),
 | |
|     }?;
 | |
|     assoc_item_of_trait(db, assoc, trait_)
 | |
| }
 | |
| 
 | |
| fn assoc_item_of_trait(
 | |
|     db: &dyn HirDatabase,
 | |
|     assoc: hir::AssocItem,
 | |
|     trait_: hir::Trait,
 | |
| ) -> Option<Definition> {
 | |
|     use hir::AssocItem::*;
 | |
|     let name = match assoc {
 | |
|         Function(it) => it.name(db),
 | |
|         Const(it) => it.name(db)?,
 | |
|         TypeAlias(it) => it.name(db),
 | |
|     };
 | |
|     let item = trait_.items(db).into_iter().find(|it| match (it, assoc) {
 | |
|         (Function(trait_func), Function(_)) => trait_func.name(db) == name,
 | |
|         (Const(trait_konst), Const(_)) => trait_konst.name(db).map_or(false, |it| it == name),
 | |
|         (TypeAlias(trait_type_alias), TypeAlias(_)) => trait_type_alias.name(db) == name,
 | |
|         _ => false,
 | |
|     })?;
 | |
|     Some(Definition::from(item))
 | |
| }
 | |
| 
 | |
| #[cfg(test)]
 | |
| mod tests {
 | |
|     use expect_test::{Expect, expect};
 | |
|     use hir::FilePosition;
 | |
|     use hir::Semantics;
 | |
|     use span::Edition;
 | |
|     use syntax::ast::{self, AstNode};
 | |
|     use test_fixture::ChangeFixture;
 | |
| 
 | |
|     use crate::RootDatabase;
 | |
| 
 | |
|     /// Creates analysis from a multi-file fixture, returns positions marked with $0.
 | |
|     pub(crate) fn position(
 | |
|         #[rust_analyzer::rust_fixture] ra_fixture: &str,
 | |
|     ) -> (RootDatabase, FilePosition) {
 | |
|         let mut database = RootDatabase::default();
 | |
|         let change_fixture = ChangeFixture::parse(&database, ra_fixture);
 | |
|         database.apply_change(change_fixture.change);
 | |
|         let (file_id, range_or_offset) =
 | |
|             change_fixture.file_position.expect("expected a marker ($0)");
 | |
|         let offset = range_or_offset.expect_offset();
 | |
|         (database, FilePosition { file_id, offset })
 | |
|     }
 | |
| 
 | |
|     fn check_trait(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
 | |
|         let (db, position) = position(ra_fixture);
 | |
|         let sema = Semantics::new(&db);
 | |
| 
 | |
|         let file = sema.parse(position.file_id);
 | |
|         let impl_block: ast::Impl =
 | |
|             sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
 | |
|         let trait_ = crate::traits::resolve_target_trait(&sema, &impl_block);
 | |
|         let actual = match trait_ {
 | |
|             Some(trait_) => trait_.name(&db).display(&db, Edition::CURRENT).to_string(),
 | |
|             None => String::new(),
 | |
|         };
 | |
|         expect.assert_eq(&actual);
 | |
|     }
 | |
| 
 | |
|     fn check_missing_assoc(#[rust_analyzer::rust_fixture] ra_fixture: &str, expect: Expect) {
 | |
|         let (db, position) = position(ra_fixture);
 | |
|         let sema = Semantics::new(&db);
 | |
| 
 | |
|         let file = sema.parse(position.file_id);
 | |
|         let impl_block: ast::Impl =
 | |
|             sema.find_node_at_offset_with_descend(file.syntax(), position.offset).unwrap();
 | |
|         let items = crate::traits::get_missing_assoc_items(&sema, &impl_block);
 | |
|         let actual = items
 | |
|             .into_iter()
 | |
|             .map(|item| item.name(&db).unwrap().display(&db, Edition::CURRENT).to_string())
 | |
|             .collect::<Vec<_>>()
 | |
|             .join("\n");
 | |
|         expect.assert_eq(&actual);
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn resolve_trait() {
 | |
|         check_trait(
 | |
|             r#"
 | |
| pub trait Foo {
 | |
|     fn bar();
 | |
| }
 | |
| impl Foo for u8 {
 | |
|     $0
 | |
| }
 | |
|             "#,
 | |
|             expect![["Foo"]],
 | |
|         );
 | |
|         check_trait(
 | |
|             r#"
 | |
| pub trait Foo {
 | |
|     fn bar();
 | |
| }
 | |
| impl Foo for u8 {
 | |
|     fn bar() {
 | |
|         fn baz() {
 | |
|             $0
 | |
|         }
 | |
|         baz();
 | |
|     }
 | |
| }
 | |
|             "#,
 | |
|             expect![["Foo"]],
 | |
|         );
 | |
|         check_trait(
 | |
|             r#"
 | |
| pub trait Foo {
 | |
|     fn bar();
 | |
| }
 | |
| pub struct Bar;
 | |
| impl Bar {
 | |
|     $0
 | |
| }
 | |
|             "#,
 | |
|             expect![[""]],
 | |
|         );
 | |
|     }
 | |
| 
 | |
|     #[test]
 | |
|     fn missing_assoc_items() {
 | |
|         check_missing_assoc(
 | |
|             r#"
 | |
| pub trait Foo {
 | |
|     const FOO: u8;
 | |
|     fn bar();
 | |
| }
 | |
| impl Foo for u8 {
 | |
|     $0
 | |
| }"#,
 | |
|             expect![[r#"
 | |
|                 FOO
 | |
|                 bar"#]],
 | |
|         );
 | |
| 
 | |
|         check_missing_assoc(
 | |
|             r#"
 | |
| pub trait Foo {
 | |
|     const FOO: u8;
 | |
|     fn bar();
 | |
| }
 | |
| impl Foo for u8 {
 | |
|     const FOO: u8 = 10;
 | |
|     $0
 | |
| }"#,
 | |
|             expect![[r#"
 | |
|                 bar"#]],
 | |
|         );
 | |
| 
 | |
|         check_missing_assoc(
 | |
|             r#"
 | |
| pub trait Foo {
 | |
|     const FOO: u8;
 | |
|     fn bar();
 | |
| }
 | |
| impl Foo for u8 {
 | |
|     const FOO: u8 = 10;
 | |
|     fn bar() {$0}
 | |
| }"#,
 | |
|             expect![[r#""#]],
 | |
|         );
 | |
| 
 | |
|         check_missing_assoc(
 | |
|             r#"
 | |
| pub struct Foo;
 | |
| impl Foo {
 | |
|     fn bar() {$0}
 | |
| }"#,
 | |
|             expect![[r#""#]],
 | |
|         );
 | |
| 
 | |
|         check_missing_assoc(
 | |
|             r#"
 | |
| trait Tr {
 | |
|     fn required();
 | |
| }
 | |
| macro_rules! m {
 | |
|     () => { fn required() {} };
 | |
| }
 | |
| impl Tr for () {
 | |
|     m!();
 | |
|     $0
 | |
| }
 | |
| 
 | |
|             "#,
 | |
|             expect![[r#""#]],
 | |
|         );
 | |
|     }
 | |
| }
 |