mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-10-01 11:31:15 +00:00
Merge #370
370: Self params & type r=matklad a=flodiebold This implements type inference for `self`, so field completion for methods taking `self` works now. - rename `IMPL_ITEM` to `IMPL_BLOCK` -- rustc calls the methods etc. inside an impl `ImplItem`s, and the impl itself doesn't define an item, so I thought this name was clearer. - add HIR for impl blocks -- we collect all impls in a crate at once, so we can go from methods to containing impls, and since we will later also need to find all impls for a certain type (which may be anywhere in the crate, I think?). We could be more lazy here, but I don't know if it's worth the complexity. - resolve `self` and `Self` during type inference - refactor a bit in ty.rs as well Co-authored-by: Florian Diebold <flodiebold@gmail.com>
This commit is contained in:
commit
4a3ef8fe63
@ -72,6 +72,21 @@ mod tests {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_struct_field_completion_self() {
|
||||||
|
check_ref_completion(
|
||||||
|
r"
|
||||||
|
struct A { the_field: u32 }
|
||||||
|
impl A {
|
||||||
|
fn foo(self) {
|
||||||
|
self.<|>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
",
|
||||||
|
r#"the_field"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_no_struct_field_completion_for_method_call() {
|
fn test_no_struct_field_completion_for_method_call() {
|
||||||
check_ref_completion(
|
check_ref_completion(
|
||||||
|
@ -105,6 +105,7 @@ salsa::database_storage! {
|
|||||||
fn type_for_field() for hir::db::TypeForFieldQuery;
|
fn type_for_field() for hir::db::TypeForFieldQuery;
|
||||||
fn struct_data() for hir::db::StructDataQuery;
|
fn struct_data() for hir::db::StructDataQuery;
|
||||||
fn enum_data() for hir::db::EnumDataQuery;
|
fn enum_data() for hir::db::EnumDataQuery;
|
||||||
|
fn impls_in_module() for hir::db::ImplsInModuleQuery;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,7 +4,7 @@ use relative_path::RelativePathBuf;
|
|||||||
use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER};
|
use test_utils::{extract_offset, extract_range, parse_fixture, CURSOR_MARKER};
|
||||||
use ra_db::mock::FileMap;
|
use ra_db::mock::FileMap;
|
||||||
|
|
||||||
use crate::{Analysis, AnalysisChange, AnalysisHost, FileId, FilePosition, FileRange, SourceRootId};
|
use crate::{Analysis, AnalysisChange, AnalysisHost, CrateGraph, FileId, FilePosition, FileRange, SourceRootId};
|
||||||
|
|
||||||
/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
|
/// Mock analysis is used in test to bootstrap an AnalysisHost/Analysis
|
||||||
/// from a set of in-memory files.
|
/// from a set of in-memory files.
|
||||||
@ -87,12 +87,17 @@ impl MockAnalysis {
|
|||||||
let source_root = SourceRootId(0);
|
let source_root = SourceRootId(0);
|
||||||
let mut change = AnalysisChange::new();
|
let mut change = AnalysisChange::new();
|
||||||
change.add_root(source_root, true);
|
change.add_root(source_root, true);
|
||||||
|
let mut crate_graph = CrateGraph::default();
|
||||||
for (path, contents) in self.files.into_iter() {
|
for (path, contents) in self.files.into_iter() {
|
||||||
assert!(path.starts_with('/'));
|
assert!(path.starts_with('/'));
|
||||||
let path = RelativePathBuf::from_path(&path[1..]).unwrap();
|
let path = RelativePathBuf::from_path(&path[1..]).unwrap();
|
||||||
let file_id = file_map.add(path.clone());
|
let file_id = file_map.add(path.clone());
|
||||||
|
if path == "/lib.rs" || path == "/main.rs" {
|
||||||
|
crate_graph.add_crate_root(file_id);
|
||||||
|
}
|
||||||
change.add_file(source_root, file_id, path, Arc::new(contents));
|
change.add_file(source_root, file_id, path, Arc::new(contents));
|
||||||
}
|
}
|
||||||
|
change.set_crate_graph(crate_graph);
|
||||||
// change.set_file_resolver(Arc::new(file_map));
|
// change.set_file_resolver(Arc::new(file_map));
|
||||||
host.apply_change(change);
|
host.apply_change(change);
|
||||||
host
|
host
|
||||||
|
@ -138,14 +138,14 @@ fn test_resolve_parent_module_for_inline() {
|
|||||||
fn test_resolve_crate_root() {
|
fn test_resolve_crate_root() {
|
||||||
let mock = MockAnalysis::with_files(
|
let mock = MockAnalysis::with_files(
|
||||||
"
|
"
|
||||||
//- /lib.rs
|
//- /bar.rs
|
||||||
mod foo;
|
mod foo;
|
||||||
//- /foo.rs
|
//- /bar/foo.rs
|
||||||
// emtpy <|>
|
// emtpy <|>
|
||||||
",
|
",
|
||||||
);
|
);
|
||||||
let root_file = mock.id_of("/lib.rs");
|
let root_file = mock.id_of("/bar.rs");
|
||||||
let mod_file = mock.id_of("/foo.rs");
|
let mod_file = mock.id_of("/bar/foo.rs");
|
||||||
let mut host = mock.analysis_host();
|
let mut host = mock.analysis_host();
|
||||||
assert!(host.analysis().crate_for(mod_file).unwrap().is_empty());
|
assert!(host.analysis().crate_for(mod_file).unwrap().is_empty());
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ use crate::{
|
|||||||
nameres::{ItemMap, InputModuleItems}},
|
nameres::{ItemMap, InputModuleItems}},
|
||||||
ty::{InferenceResult, Ty},
|
ty::{InferenceResult, Ty},
|
||||||
adt::{StructData, EnumData},
|
adt::{StructData, EnumData},
|
||||||
|
impl_block::ModuleImplBlocks,
|
||||||
};
|
};
|
||||||
|
|
||||||
salsa::query_group! {
|
salsa::query_group! {
|
||||||
@ -87,6 +88,11 @@ pub trait HirDatabase: SyntaxDatabase
|
|||||||
type ModuleTreeQuery;
|
type ModuleTreeQuery;
|
||||||
use fn crate::module::imp::module_tree;
|
use fn crate::module::imp::module_tree;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn impls_in_module(source_root_id: SourceRootId, module_id: ModuleId) -> Cancelable<Arc<ModuleImplBlocks>> {
|
||||||
|
type ImplsInModuleQuery;
|
||||||
|
use fn crate::impl_block::impls_in_module;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -11,11 +11,11 @@ use ra_syntax::{
|
|||||||
ast::{self, AstNode, DocCommentsOwner, NameOwner},
|
ast::{self, AstNode, DocCommentsOwner, NameOwner},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module};
|
use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock};
|
||||||
|
|
||||||
pub use self::scope::FnScopes;
|
pub use self::scope::FnScopes;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct Function {
|
pub struct Function {
|
||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
}
|
}
|
||||||
@ -25,6 +25,10 @@ impl Function {
|
|||||||
Function { def_id }
|
Function { def_id }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn def_id(&self) -> DefId {
|
||||||
|
self.def_id
|
||||||
|
}
|
||||||
|
|
||||||
pub fn syntax(&self, db: &impl HirDatabase) -> ast::FnDefNode {
|
pub fn syntax(&self, db: &impl HirDatabase) -> ast::FnDefNode {
|
||||||
let def_loc = self.def_id.loc(db);
|
let def_loc = self.def_id.loc(db);
|
||||||
assert!(def_loc.kind == DefKind::Function);
|
assert!(def_loc.kind == DefKind::Function);
|
||||||
@ -48,6 +52,15 @@ impl Function {
|
|||||||
pub fn module(&self, db: &impl HirDatabase) -> Cancelable<Module> {
|
pub fn module(&self, db: &impl HirDatabase) -> Cancelable<Module> {
|
||||||
self.def_id.module(db)
|
self.def_id.module(db)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn krate(&self, db: &impl HirDatabase) -> Cancelable<Option<Crate>> {
|
||||||
|
self.def_id.krate(db)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The containing impl block, if this is a method.
|
||||||
|
pub fn impl_block(&self, db: &impl HirDatabase) -> Cancelable<Option<ImplBlock>> {
|
||||||
|
self.def_id.impl_block(db)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
|
@ -2,7 +2,7 @@ use ra_db::{SourceRootId, LocationIntener, Cancelable, FileId};
|
|||||||
use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast};
|
use ra_syntax::{SourceFileNode, SyntaxKind, SyntaxNode, SyntaxNodeRef, SourceFile, AstNode, ast};
|
||||||
use ra_arena::{Arena, RawId, impl_arena_id};
|
use ra_arena::{Arena, RawId, impl_arena_id};
|
||||||
|
|
||||||
use crate::{HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum};
|
use crate::{HirDatabase, PerNs, ModuleId, Module, Def, Function, Struct, Enum, ImplBlock, Crate};
|
||||||
|
|
||||||
/// hir makes a heavy use of ids: integer (u32) handlers to various things. You
|
/// hir makes a heavy use of ids: integer (u32) handlers to various things. You
|
||||||
/// can think of id as a pointer (but without a lifetime) or a file descriptor
|
/// can think of id as a pointer (but without a lifetime) or a file descriptor
|
||||||
@ -177,6 +177,18 @@ impl DefId {
|
|||||||
let loc = self.loc(db);
|
let loc = self.loc(db);
|
||||||
Module::new(db, loc.source_root_id, loc.module_id)
|
Module::new(db, loc.source_root_id, loc.module_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the containing crate.
|
||||||
|
pub fn krate(&self, db: &impl HirDatabase) -> Cancelable<Option<Crate>> {
|
||||||
|
Ok(self.module(db)?.krate(db))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns the containing impl block, if this is an impl item.
|
||||||
|
pub fn impl_block(self, db: &impl HirDatabase) -> Cancelable<Option<ImplBlock>> {
|
||||||
|
let loc = self.loc(db);
|
||||||
|
let module_impls = db.impls_in_module(loc.source_root_id, loc.module_id)?;
|
||||||
|
Ok(ImplBlock::containing(module_impls, self))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefLoc {
|
impl DefLoc {
|
||||||
|
180
crates/ra_hir/src/impl_block.rs
Normal file
180
crates/ra_hir/src/impl_block.rs
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
use rustc_hash::FxHashMap;
|
||||||
|
|
||||||
|
use ra_arena::{Arena, RawId, impl_arena_id};
|
||||||
|
use ra_syntax::ast::{self, AstNode};
|
||||||
|
use ra_db::{LocationIntener, Cancelable, SourceRootId};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
DefId, DefLoc, DefKind, SourceItemId, SourceFileItems,
|
||||||
|
Module, Function,
|
||||||
|
db::HirDatabase,
|
||||||
|
type_ref::TypeRef,
|
||||||
|
module::{ModuleSourceNode, ModuleId},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct ImplBlock {
|
||||||
|
module_impl_blocks: Arc<ModuleImplBlocks>,
|
||||||
|
impl_id: ImplId,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImplBlock {
|
||||||
|
pub(crate) fn containing(
|
||||||
|
module_impl_blocks: Arc<ModuleImplBlocks>,
|
||||||
|
def_id: DefId,
|
||||||
|
) -> Option<ImplBlock> {
|
||||||
|
let impl_id = *module_impl_blocks.impls_by_def.get(&def_id)?;
|
||||||
|
Some(ImplBlock {
|
||||||
|
module_impl_blocks,
|
||||||
|
impl_id,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn impl_data(&self) -> &ImplData {
|
||||||
|
&self.module_impl_blocks.impls[self.impl_id]
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn target_trait(&self) -> Option<&TypeRef> {
|
||||||
|
self.impl_data().target_trait.as_ref()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn target_type(&self) -> &TypeRef {
|
||||||
|
&self.impl_data().target_type
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn items(&self) -> &[ImplItem] {
|
||||||
|
&self.impl_data().items
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub struct ImplData {
|
||||||
|
target_trait: Option<TypeRef>,
|
||||||
|
target_type: TypeRef,
|
||||||
|
items: Vec<ImplItem>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImplData {
|
||||||
|
pub(crate) fn from_ast(
|
||||||
|
db: &impl AsRef<LocationIntener<DefLoc, DefId>>,
|
||||||
|
file_items: &SourceFileItems,
|
||||||
|
module: &Module,
|
||||||
|
node: ast::ImplBlock,
|
||||||
|
) -> Self {
|
||||||
|
let target_trait = node.target_type().map(TypeRef::from_ast);
|
||||||
|
let target_type = TypeRef::from_ast_opt(node.target_type());
|
||||||
|
let file_id = module.source().file_id();
|
||||||
|
let items = if let Some(item_list) = node.item_list() {
|
||||||
|
item_list
|
||||||
|
.impl_items()
|
||||||
|
.map(|item_node| {
|
||||||
|
let kind = match item_node {
|
||||||
|
ast::ImplItem::FnDef(..) => DefKind::Function,
|
||||||
|
ast::ImplItem::ConstDef(..) => DefKind::Item,
|
||||||
|
ast::ImplItem::TypeDef(..) => DefKind::Item,
|
||||||
|
};
|
||||||
|
let item_id = file_items.id_of_unchecked(item_node.syntax());
|
||||||
|
let def_loc = DefLoc {
|
||||||
|
kind,
|
||||||
|
source_root_id: module.source_root_id,
|
||||||
|
module_id: module.module_id,
|
||||||
|
source_item_id: SourceItemId {
|
||||||
|
file_id,
|
||||||
|
item_id: Some(item_id),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let def_id = def_loc.id(db);
|
||||||
|
match item_node {
|
||||||
|
ast::ImplItem::FnDef(..) => ImplItem::Method(Function::new(def_id)),
|
||||||
|
ast::ImplItem::ConstDef(..) => ImplItem::Const(def_id),
|
||||||
|
ast::ImplItem::TypeDef(..) => ImplItem::Type(def_id),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
Vec::new()
|
||||||
|
};
|
||||||
|
ImplData {
|
||||||
|
target_trait,
|
||||||
|
target_type,
|
||||||
|
items,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
|
pub enum ImplItem {
|
||||||
|
Method(Function),
|
||||||
|
// these don't have their own types yet
|
||||||
|
Const(DefId),
|
||||||
|
Type(DefId),
|
||||||
|
// Existential
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImplItem {
|
||||||
|
pub fn def_id(&self) -> DefId {
|
||||||
|
match self {
|
||||||
|
ImplItem::Method(f) => f.def_id(),
|
||||||
|
ImplItem::Const(def_id) => *def_id,
|
||||||
|
ImplItem::Type(def_id) => *def_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
pub struct ImplId(pub RawId);
|
||||||
|
impl_arena_id!(ImplId);
|
||||||
|
|
||||||
|
/// Collection of impl blocks is a two-step process: First we collect the blocks
|
||||||
|
/// per-module; then we build an index of all impl blocks in the crate. This
|
||||||
|
/// way, we avoid having to do this process for the whole crate whenever someone
|
||||||
|
/// types in any file; as long as the impl blocks in the file don't change, we
|
||||||
|
/// don't need to do the second step again.
|
||||||
|
///
|
||||||
|
/// (The second step does not yet exist currently.)
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub struct ModuleImplBlocks {
|
||||||
|
impls: Arena<ImplId, ImplData>,
|
||||||
|
impls_by_def: FxHashMap<DefId, ImplId>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ModuleImplBlocks {
|
||||||
|
fn new() -> Self {
|
||||||
|
ModuleImplBlocks {
|
||||||
|
impls: Arena::default(),
|
||||||
|
impls_by_def: FxHashMap::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn collect(&mut self, db: &impl HirDatabase, module: Module) -> Cancelable<()> {
|
||||||
|
let module_source_node = module.source().resolve(db);
|
||||||
|
let node = match &module_source_node {
|
||||||
|
ModuleSourceNode::SourceFile(node) => node.borrowed().syntax(),
|
||||||
|
ModuleSourceNode::Module(node) => node.borrowed().syntax(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let source_file_items = db.file_items(module.source().file_id());
|
||||||
|
|
||||||
|
for impl_block_ast in node.children().filter_map(ast::ImplBlock::cast) {
|
||||||
|
let impl_block = ImplData::from_ast(db, &source_file_items, &module, impl_block_ast);
|
||||||
|
let id = self.impls.alloc(impl_block);
|
||||||
|
for impl_item in &self.impls[id].items {
|
||||||
|
self.impls_by_def.insert(impl_item.def_id(), id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn impls_in_module(
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
source_root_id: SourceRootId,
|
||||||
|
module_id: ModuleId,
|
||||||
|
) -> Cancelable<Arc<ModuleImplBlocks>> {
|
||||||
|
let mut result = ModuleImplBlocks::new();
|
||||||
|
let module = Module::new(db, source_root_id, module_id)?;
|
||||||
|
result.collect(db, module)?;
|
||||||
|
Ok(Arc::new(result))
|
||||||
|
}
|
@ -5,7 +5,7 @@ use crate::{HirDatabase, Module, Name, AsName, HirFileId};
|
|||||||
/// hir::Crate describes a single crate. It's the main inteface with which
|
/// hir::Crate describes a single crate. It's the main inteface with which
|
||||||
/// crate's dependencies interact. Mostly, it should be just a proxy for the
|
/// crate's dependencies interact. Mostly, it should be just a proxy for the
|
||||||
/// root module.
|
/// root module.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct Crate {
|
pub struct Crate {
|
||||||
crate_id: CrateId,
|
crate_id: CrateId,
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ mod function;
|
|||||||
mod adt;
|
mod adt;
|
||||||
mod type_ref;
|
mod type_ref;
|
||||||
mod ty;
|
mod ty;
|
||||||
|
mod impl_block;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
@ -48,6 +49,7 @@ pub use self::{
|
|||||||
function::{Function, FnScopes},
|
function::{Function, FnScopes},
|
||||||
adt::{Struct, Enum},
|
adt::{Struct, Enum},
|
||||||
ty::Ty,
|
ty::Ty,
|
||||||
|
impl_block::{ImplBlock, ImplItem},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub use self::function::FnSignatureInfo;
|
pub use self::function::FnSignatureInfo;
|
||||||
|
@ -30,6 +30,10 @@ impl MockDatabase {
|
|||||||
let file_id = db.add_file(&mut source_root, "/main.rs", text);
|
let file_id = db.add_file(&mut source_root, "/main.rs", text);
|
||||||
db.query_mut(ra_db::SourceRootQuery)
|
db.query_mut(ra_db::SourceRootQuery)
|
||||||
.set(WORKSPACE, Arc::new(source_root.clone()));
|
.set(WORKSPACE, Arc::new(source_root.clone()));
|
||||||
|
|
||||||
|
let mut crate_graph = CrateGraph::default();
|
||||||
|
crate_graph.add_crate_root(file_id);
|
||||||
|
db.set_crate_graph(crate_graph);
|
||||||
(db, source_root, file_id)
|
(db, source_root, file_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -203,6 +207,7 @@ salsa::database_storage! {
|
|||||||
fn type_for_field() for db::TypeForFieldQuery;
|
fn type_for_field() for db::TypeForFieldQuery;
|
||||||
fn struct_data() for db::StructDataQuery;
|
fn struct_data() for db::StructDataQuery;
|
||||||
fn enum_data() for db::EnumDataQuery;
|
fn enum_data() for db::EnumDataQuery;
|
||||||
|
fn impls_in_module() for db::ImplsInModuleQuery;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -71,6 +71,21 @@ impl Module {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns an iterator of all children of this module.
|
||||||
|
pub fn children<'a>(&'a self) -> impl Iterator<Item = (Name, Module)> + 'a {
|
||||||
|
self.module_id
|
||||||
|
.children(&self.tree)
|
||||||
|
.map(move |(name, module_id)| {
|
||||||
|
(
|
||||||
|
name,
|
||||||
|
Module {
|
||||||
|
module_id,
|
||||||
|
..self.clone()
|
||||||
|
},
|
||||||
|
)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the crate this module is part of.
|
/// Returns the crate this module is part of.
|
||||||
pub fn krate(&self, db: &impl HirDatabase) -> Option<Crate> {
|
pub fn krate(&self, db: &impl HirDatabase) -> Option<Crate> {
|
||||||
let root_id = self.module_id.crate_root(&self.tree);
|
let root_id = self.module_id.crate_root(&self.tree);
|
||||||
|
@ -51,6 +51,7 @@ impl Name {
|
|||||||
"u128" => KnownName::U128,
|
"u128" => KnownName::U128,
|
||||||
"f32" => KnownName::F32,
|
"f32" => KnownName::F32,
|
||||||
"f64" => KnownName::F64,
|
"f64" => KnownName::F64,
|
||||||
|
"Self" => KnownName::Self_,
|
||||||
_ => return None,
|
_ => return None,
|
||||||
};
|
};
|
||||||
Some(name)
|
Some(name)
|
||||||
@ -84,7 +85,7 @@ impl AsName for ra_db::Dependency {
|
|||||||
// const ISIZE: Name = Name::new("isize")
|
// const ISIZE: Name = Name::new("isize")
|
||||||
// ```
|
// ```
|
||||||
// but const-fn is not that powerful yet.
|
// but const-fn is not that powerful yet.
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
pub(crate) enum KnownName {
|
pub(crate) enum KnownName {
|
||||||
Isize,
|
Isize,
|
||||||
I8,
|
I8,
|
||||||
@ -102,4 +103,6 @@ pub(crate) enum KnownName {
|
|||||||
|
|
||||||
F32,
|
F32,
|
||||||
F64,
|
F64,
|
||||||
|
|
||||||
|
Self_,
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,11 @@ impl Path {
|
|||||||
self.kind == PathKind::Plain && self.segments.len() == 1
|
self.kind == PathKind::Plain && self.segments.len() == 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// `true` if this path is just a standalone `self`
|
||||||
|
pub fn is_self(&self) -> bool {
|
||||||
|
self.kind == PathKind::Self_ && self.segments.len() == 0
|
||||||
|
}
|
||||||
|
|
||||||
/// If this path is a single identifier, like `foo`, return its name.
|
/// If this path is a single identifier, like `foo`, return its name.
|
||||||
pub fn as_ident(&self) -> Option<&Name> {
|
pub fn as_ident(&self) -> Option<&Name> {
|
||||||
if self.kind != PathKind::Plain || self.segments.len() > 1 {
|
if self.kind != PathKind::Plain || self.segments.len() > 1 {
|
||||||
|
@ -31,9 +31,10 @@ use ra_syntax::{
|
|||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, Name, AsName,
|
Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, Name, AsName, ImplBlock,
|
||||||
db::HirDatabase,
|
db::HirDatabase,
|
||||||
type_ref::{TypeRef, Mutability},
|
type_ref::{TypeRef, Mutability},
|
||||||
|
name::KnownName,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// The ID of a type variable.
|
/// The ID of a type variable.
|
||||||
@ -235,6 +236,7 @@ impl Ty {
|
|||||||
pub(crate) fn from_hir(
|
pub(crate) fn from_hir(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
|
impl_block: Option<&ImplBlock>,
|
||||||
type_ref: &TypeRef,
|
type_ref: &TypeRef,
|
||||||
) -> Cancelable<Self> {
|
) -> Cancelable<Self> {
|
||||||
Ok(match type_ref {
|
Ok(match type_ref {
|
||||||
@ -242,29 +244,29 @@ impl Ty {
|
|||||||
TypeRef::Tuple(inner) => {
|
TypeRef::Tuple(inner) => {
|
||||||
let inner_tys = inner
|
let inner_tys = inner
|
||||||
.iter()
|
.iter()
|
||||||
.map(|tr| Ty::from_hir(db, module, tr))
|
.map(|tr| Ty::from_hir(db, module, impl_block, tr))
|
||||||
.collect::<Cancelable<Vec<_>>>()?;
|
.collect::<Cancelable<Vec<_>>>()?;
|
||||||
Ty::Tuple(inner_tys.into())
|
Ty::Tuple(inner_tys.into())
|
||||||
}
|
}
|
||||||
TypeRef::Path(path) => Ty::from_hir_path(db, module, path)?,
|
TypeRef::Path(path) => Ty::from_hir_path(db, module, impl_block, path)?,
|
||||||
TypeRef::RawPtr(inner, mutability) => {
|
TypeRef::RawPtr(inner, mutability) => {
|
||||||
let inner_ty = Ty::from_hir(db, module, inner)?;
|
let inner_ty = Ty::from_hir(db, module, impl_block, inner)?;
|
||||||
Ty::RawPtr(Arc::new(inner_ty), *mutability)
|
Ty::RawPtr(Arc::new(inner_ty), *mutability)
|
||||||
}
|
}
|
||||||
TypeRef::Array(_inner) => Ty::Unknown, // TODO
|
TypeRef::Array(_inner) => Ty::Unknown, // TODO
|
||||||
TypeRef::Slice(inner) => {
|
TypeRef::Slice(inner) => {
|
||||||
let inner_ty = Ty::from_hir(db, module, inner)?;
|
let inner_ty = Ty::from_hir(db, module, impl_block, inner)?;
|
||||||
Ty::Slice(Arc::new(inner_ty))
|
Ty::Slice(Arc::new(inner_ty))
|
||||||
}
|
}
|
||||||
TypeRef::Reference(inner, mutability) => {
|
TypeRef::Reference(inner, mutability) => {
|
||||||
let inner_ty = Ty::from_hir(db, module, inner)?;
|
let inner_ty = Ty::from_hir(db, module, impl_block, inner)?;
|
||||||
Ty::Ref(Arc::new(inner_ty), *mutability)
|
Ty::Ref(Arc::new(inner_ty), *mutability)
|
||||||
}
|
}
|
||||||
TypeRef::Placeholder => Ty::Unknown,
|
TypeRef::Placeholder => Ty::Unknown,
|
||||||
TypeRef::Fn(params) => {
|
TypeRef::Fn(params) => {
|
||||||
let mut inner_tys = params
|
let mut inner_tys = params
|
||||||
.iter()
|
.iter()
|
||||||
.map(|tr| Ty::from_hir(db, module, tr))
|
.map(|tr| Ty::from_hir(db, module, impl_block, tr))
|
||||||
.collect::<Cancelable<Vec<_>>>()?;
|
.collect::<Cancelable<Vec<_>>>()?;
|
||||||
let return_ty = inner_tys
|
let return_ty = inner_tys
|
||||||
.pop()
|
.pop()
|
||||||
@ -279,9 +281,21 @@ impl Ty {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn from_hir_opt(
|
||||||
|
db: &impl HirDatabase,
|
||||||
|
module: &Module,
|
||||||
|
impl_block: Option<&ImplBlock>,
|
||||||
|
type_ref: Option<&TypeRef>,
|
||||||
|
) -> Cancelable<Self> {
|
||||||
|
type_ref
|
||||||
|
.map(|t| Ty::from_hir(db, module, impl_block, t))
|
||||||
|
.unwrap_or(Ok(Ty::Unknown))
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn from_hir_path(
|
pub(crate) fn from_hir_path(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
|
impl_block: Option<&ImplBlock>,
|
||||||
path: &Path,
|
path: &Path,
|
||||||
) -> Cancelable<Self> {
|
) -> Cancelable<Self> {
|
||||||
if let Some(name) = path.as_ident() {
|
if let Some(name) = path.as_ident() {
|
||||||
@ -291,6 +305,8 @@ impl Ty {
|
|||||||
return Ok(Ty::Uint(uint_ty));
|
return Ok(Ty::Uint(uint_ty));
|
||||||
} else if let Some(float_ty) = primitive::FloatTy::from_name(name) {
|
} else if let Some(float_ty) = primitive::FloatTy::from_name(name) {
|
||||||
return Ok(Ty::Float(float_ty));
|
return Ok(Ty::Float(float_ty));
|
||||||
|
} else if name.as_known_name() == Some(KnownName::Self_) {
|
||||||
|
return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -308,18 +324,20 @@ impl Ty {
|
|||||||
pub(crate) fn from_ast_opt(
|
pub(crate) fn from_ast_opt(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
|
impl_block: Option<&ImplBlock>,
|
||||||
node: Option<ast::TypeRef>,
|
node: Option<ast::TypeRef>,
|
||||||
) -> Cancelable<Self> {
|
) -> Cancelable<Self> {
|
||||||
node.map(|n| Ty::from_ast(db, module, n))
|
node.map(|n| Ty::from_ast(db, module, impl_block, n))
|
||||||
.unwrap_or(Ok(Ty::Unknown))
|
.unwrap_or(Ok(Ty::Unknown))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_ast(
|
pub(crate) fn from_ast(
|
||||||
db: &impl HirDatabase,
|
db: &impl HirDatabase,
|
||||||
module: &Module,
|
module: &Module,
|
||||||
|
impl_block: Option<&ImplBlock>,
|
||||||
node: ast::TypeRef,
|
node: ast::TypeRef,
|
||||||
) -> Cancelable<Self> {
|
) -> Cancelable<Self> {
|
||||||
Ty::from_hir(db, module, &TypeRef::from_ast(node))
|
Ty::from_hir(db, module, impl_block, &TypeRef::from_ast(node))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unit() -> Self {
|
pub fn unit() -> Self {
|
||||||
@ -402,18 +420,19 @@ impl fmt::Display for Ty {
|
|||||||
fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
|
fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
|
||||||
let syntax = f.syntax(db);
|
let syntax = f.syntax(db);
|
||||||
let module = f.module(db)?;
|
let module = f.module(db)?;
|
||||||
|
let impl_block = f.impl_block(db)?;
|
||||||
let node = syntax.borrowed();
|
let node = syntax.borrowed();
|
||||||
// TODO we ignore type parameters for now
|
// TODO we ignore type parameters for now
|
||||||
let input = node
|
let input = node
|
||||||
.param_list()
|
.param_list()
|
||||||
.map(|pl| {
|
.map(|pl| {
|
||||||
pl.params()
|
pl.params()
|
||||||
.map(|p| Ty::from_ast_opt(db, &module, p.type_ref()))
|
.map(|p| Ty::from_ast_opt(db, &module, impl_block.as_ref(), p.type_ref()))
|
||||||
.collect()
|
.collect()
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| Ok(Vec::new()))?;
|
.unwrap_or_else(|| Ok(Vec::new()))?;
|
||||||
let output = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) {
|
let output = if let Some(type_ref) = node.ret_type().and_then(|rt| rt.type_ref()) {
|
||||||
Ty::from_ast(db, &module, type_ref)?
|
Ty::from_ast(db, &module, impl_block.as_ref(), type_ref)?
|
||||||
} else {
|
} else {
|
||||||
Ty::unit()
|
Ty::unit()
|
||||||
};
|
};
|
||||||
@ -467,12 +486,13 @@ pub(super) fn type_for_field(db: &impl HirDatabase, def_id: DefId, field: Name)
|
|||||||
),
|
),
|
||||||
};
|
};
|
||||||
let module = def_id.module(db)?;
|
let module = def_id.module(db)?;
|
||||||
|
let impl_block = def_id.impl_block(db)?;
|
||||||
let type_ref = if let Some(tr) = variant_data.get_field_type_ref(&field) {
|
let type_ref = if let Some(tr) = variant_data.get_field_type_ref(&field) {
|
||||||
tr
|
tr
|
||||||
} else {
|
} else {
|
||||||
return Ok(Ty::Unknown);
|
return Ok(Ty::Unknown);
|
||||||
};
|
};
|
||||||
Ty::from_hir(db, &module, &type_ref)
|
Ty::from_hir(db, &module, impl_block.as_ref(), &type_ref)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The result of type inference: A mapping from expressions and patterns to types.
|
/// The result of type inference: A mapping from expressions and patterns to types.
|
||||||
@ -496,19 +516,32 @@ impl InferenceResult {
|
|||||||
struct InferenceContext<'a, D: HirDatabase> {
|
struct InferenceContext<'a, D: HirDatabase> {
|
||||||
db: &'a D,
|
db: &'a D,
|
||||||
scopes: Arc<FnScopes>,
|
scopes: Arc<FnScopes>,
|
||||||
|
/// The self param for the current method, if it exists.
|
||||||
|
self_param: Option<LocalSyntaxPtr>,
|
||||||
module: Module,
|
module: Module,
|
||||||
|
impl_block: Option<ImplBlock>,
|
||||||
var_unification_table: InPlaceUnificationTable<TypeVarId>,
|
var_unification_table: InPlaceUnificationTable<TypeVarId>,
|
||||||
type_of: FxHashMap<LocalSyntaxPtr, Ty>,
|
type_of: FxHashMap<LocalSyntaxPtr, Ty>,
|
||||||
|
/// The return type of the function being inferred.
|
||||||
|
return_ty: Ty,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
fn new(db: &'a D, scopes: Arc<FnScopes>, module: Module) -> Self {
|
fn new(
|
||||||
|
db: &'a D,
|
||||||
|
scopes: Arc<FnScopes>,
|
||||||
|
module: Module,
|
||||||
|
impl_block: Option<ImplBlock>,
|
||||||
|
) -> Self {
|
||||||
InferenceContext {
|
InferenceContext {
|
||||||
type_of: FxHashMap::default(),
|
type_of: FxHashMap::default(),
|
||||||
var_unification_table: InPlaceUnificationTable::new(),
|
var_unification_table: InPlaceUnificationTable::new(),
|
||||||
|
self_param: None, // set during parameter typing
|
||||||
|
return_ty: Ty::Unknown, // set in collect_fn_signature
|
||||||
db,
|
db,
|
||||||
scopes,
|
scopes,
|
||||||
module,
|
module,
|
||||||
|
impl_block,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -525,6 +558,14 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||||||
self.type_of.insert(LocalSyntaxPtr::new(node), ty);
|
self.type_of.insert(LocalSyntaxPtr::new(node), ty);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn make_ty(&self, type_ref: &TypeRef) -> Cancelable<Ty> {
|
||||||
|
Ty::from_hir(self.db, &self.module, self.impl_block.as_ref(), type_ref)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn make_ty_opt(&self, type_ref: Option<&TypeRef>) -> Cancelable<Ty> {
|
||||||
|
Ty::from_hir_opt(self.db, &self.module, self.impl_block.as_ref(), type_ref)
|
||||||
|
}
|
||||||
|
|
||||||
fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
|
fn unify(&mut self, ty1: &Ty, ty2: &Ty) -> bool {
|
||||||
match (ty1, ty2) {
|
match (ty1, ty2) {
|
||||||
(Ty::Unknown, ..) => true,
|
(Ty::Unknown, ..) => true,
|
||||||
@ -628,6 +669,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||||||
let ty = self.resolve_ty_as_possible(ty.clone());
|
let ty = self.resolve_ty_as_possible(ty.clone());
|
||||||
return Ok(Some(ty));
|
return Ok(Some(ty));
|
||||||
};
|
};
|
||||||
|
} else if path.is_self() {
|
||||||
|
// resolve `self` param
|
||||||
|
let self_param = ctry!(self.self_param);
|
||||||
|
let ty = ctry!(self.type_of.get(&self_param));
|
||||||
|
let ty = self.resolve_ty_as_possible(ty.clone());
|
||||||
|
return Ok(Some(ty));
|
||||||
};
|
};
|
||||||
|
|
||||||
// resolve in module
|
// resolve in module
|
||||||
@ -826,7 +873,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||||||
}
|
}
|
||||||
ast::Expr::CastExpr(e) => {
|
ast::Expr::CastExpr(e) => {
|
||||||
let _inner_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?;
|
let _inner_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?;
|
||||||
let cast_ty = Ty::from_ast_opt(self.db, &self.module, e.type_ref())?;
|
let cast_ty = Ty::from_ast_opt(
|
||||||
|
self.db,
|
||||||
|
&self.module,
|
||||||
|
self.impl_block.as_ref(),
|
||||||
|
e.type_ref(),
|
||||||
|
)?;
|
||||||
let cast_ty = self.insert_type_vars(cast_ty);
|
let cast_ty = self.insert_type_vars(cast_ty);
|
||||||
// TODO do the coercion...
|
// TODO do the coercion...
|
||||||
cast_ty
|
cast_ty
|
||||||
@ -880,7 +932,12 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||||||
for stmt in node.statements() {
|
for stmt in node.statements() {
|
||||||
match stmt {
|
match stmt {
|
||||||
ast::Stmt::LetStmt(stmt) => {
|
ast::Stmt::LetStmt(stmt) => {
|
||||||
let decl_ty = Ty::from_ast_opt(self.db, &self.module, stmt.type_ref())?;
|
let decl_ty = Ty::from_ast_opt(
|
||||||
|
self.db,
|
||||||
|
&self.module,
|
||||||
|
self.impl_block.as_ref(),
|
||||||
|
stmt.type_ref(),
|
||||||
|
)?;
|
||||||
let decl_ty = self.insert_type_vars(decl_ty);
|
let decl_ty = self.insert_type_vars(decl_ty);
|
||||||
let ty = if let Some(expr) = stmt.initializer() {
|
let ty = if let Some(expr) = stmt.initializer() {
|
||||||
let expr_ty = self.infer_expr(expr, &Expectation::has_type(decl_ty))?;
|
let expr_ty = self.infer_expr(expr, &Expectation::has_type(decl_ty))?;
|
||||||
@ -906,46 +963,71 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
|||||||
self.write_ty(node.syntax(), ty.clone());
|
self.write_ty(node.syntax(), ty.clone());
|
||||||
Ok(ty)
|
Ok(ty)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<InferenceResult>> {
|
|
||||||
let function = Function::new(def_id); // TODO: consts also need inference
|
|
||||||
let scopes = function.scopes(db);
|
|
||||||
let module = function.module(db)?;
|
|
||||||
let mut ctx = InferenceContext::new(db, scopes, module);
|
|
||||||
|
|
||||||
let syntax = function.syntax(db);
|
|
||||||
let node = syntax.borrowed();
|
|
||||||
|
|
||||||
|
fn collect_fn_signature(&mut self, node: ast::FnDef) -> Cancelable<()> {
|
||||||
if let Some(param_list) = node.param_list() {
|
if let Some(param_list) = node.param_list() {
|
||||||
|
if let Some(self_param) = param_list.self_param() {
|
||||||
|
let self_type = if let Some(type_ref) = self_param.type_ref() {
|
||||||
|
let ty = self.make_ty(&TypeRef::from_ast(type_ref))?;
|
||||||
|
self.insert_type_vars(ty)
|
||||||
|
} else {
|
||||||
|
// TODO this should be handled by desugaring during HIR conversion
|
||||||
|
let ty = self.make_ty_opt(self.impl_block.as_ref().map(|i| i.target_type()))?;
|
||||||
|
let ty = match self_param.flavor() {
|
||||||
|
ast::SelfParamFlavor::Owned => ty,
|
||||||
|
ast::SelfParamFlavor::Ref => Ty::Ref(Arc::new(ty), Mutability::Shared),
|
||||||
|
ast::SelfParamFlavor::MutRef => Ty::Ref(Arc::new(ty), Mutability::Mut),
|
||||||
|
};
|
||||||
|
self.insert_type_vars(ty)
|
||||||
|
};
|
||||||
|
if let Some(self_kw) = self_param.self_kw() {
|
||||||
|
let self_param = LocalSyntaxPtr::new(self_kw.syntax());
|
||||||
|
self.self_param = Some(self_param);
|
||||||
|
self.type_of.insert(self_param, self_type);
|
||||||
|
}
|
||||||
|
}
|
||||||
for param in param_list.params() {
|
for param in param_list.params() {
|
||||||
let pat = if let Some(pat) = param.pat() {
|
let pat = if let Some(pat) = param.pat() {
|
||||||
pat
|
pat
|
||||||
} else {
|
} else {
|
||||||
continue;
|
continue;
|
||||||
};
|
};
|
||||||
if let Some(type_ref) = param.type_ref() {
|
let ty = if let Some(type_ref) = param.type_ref() {
|
||||||
let ty = Ty::from_ast(db, &ctx.module, type_ref)?;
|
let ty = self.make_ty(&TypeRef::from_ast(type_ref))?;
|
||||||
let ty = ctx.insert_type_vars(ty);
|
self.insert_type_vars(ty)
|
||||||
ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty);
|
|
||||||
} else {
|
} else {
|
||||||
// TODO self param
|
// missing type annotation
|
||||||
let type_var = ctx.new_type_var();
|
self.new_type_var()
|
||||||
ctx.type_of
|
|
||||||
.insert(LocalSyntaxPtr::new(pat.syntax()), type_var);
|
|
||||||
};
|
};
|
||||||
|
self.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let ret_ty = if let Some(type_ref) = node.ret_type().and_then(|n| n.type_ref()) {
|
self.return_ty = if let Some(type_ref) = node.ret_type().and_then(|n| n.type_ref()) {
|
||||||
let ty = Ty::from_ast(db, &ctx.module, type_ref)?;
|
let ty = self.make_ty(&TypeRef::from_ast(type_ref))?;
|
||||||
ctx.insert_type_vars(ty)
|
self.insert_type_vars(ty)
|
||||||
} else {
|
} else {
|
||||||
Ty::unit()
|
Ty::unit()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn infer(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<InferenceResult>> {
|
||||||
|
let function = Function::new(def_id); // TODO: consts also need inference
|
||||||
|
let scopes = function.scopes(db);
|
||||||
|
let module = function.module(db)?;
|
||||||
|
let impl_block = function.impl_block(db)?;
|
||||||
|
let mut ctx = InferenceContext::new(db, scopes, module, impl_block);
|
||||||
|
|
||||||
|
let syntax = function.syntax(db);
|
||||||
|
let node = syntax.borrowed();
|
||||||
|
|
||||||
|
ctx.collect_fn_signature(node)?;
|
||||||
|
|
||||||
if let Some(block) = node.body() {
|
if let Some(block) = node.body() {
|
||||||
ctx.infer_block(block, &Expectation::has_type(ret_ty))?;
|
ctx.infer_block(block, &Expectation::has_type(ctx.return_ty.clone()))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Arc::new(ctx.resolve_all()))
|
Ok(Arc::new(ctx.resolve_all()))
|
||||||
|
@ -134,6 +134,25 @@ fn test() -> &mut &f64 {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn infer_self() {
|
||||||
|
check_inference(
|
||||||
|
r#"
|
||||||
|
struct S;
|
||||||
|
|
||||||
|
impl S {
|
||||||
|
fn test(&self) {
|
||||||
|
self;
|
||||||
|
}
|
||||||
|
fn test2(self: &Self) {
|
||||||
|
self;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
"0007_self.txt",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn infer(content: &str) -> String {
|
fn infer(content: &str) -> String {
|
||||||
let (db, _, file_id) = MockDatabase::with_single_file(content);
|
let (db, _, file_id) = MockDatabase::with_single_file(content);
|
||||||
let source_file = db.source_file(file_id);
|
let source_file = db.source_file(file_id);
|
||||||
|
6
crates/ra_hir/src/ty/tests/data/0007_self.txt
Normal file
6
crates/ra_hir/src/ty/tests/data/0007_self.txt
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[50; 54) 'self': &S
|
||||||
|
[34; 38) 'self': &S
|
||||||
|
[40; 61) '{ ... }': ()
|
||||||
|
[88; 109) '{ ... }': ()
|
||||||
|
[98; 102) 'self': &S
|
||||||
|
[75; 79) 'self': &S
|
@ -482,6 +482,37 @@ impl<'a> PrefixExpr<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub enum SelfParamFlavor {
|
||||||
|
/// self
|
||||||
|
Owned,
|
||||||
|
/// &self
|
||||||
|
Ref,
|
||||||
|
/// &mut self
|
||||||
|
MutRef,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> SelfParam<'a> {
|
||||||
|
pub fn flavor(&self) -> SelfParamFlavor {
|
||||||
|
let borrowed = self.syntax().children().any(|n| n.kind() == AMP);
|
||||||
|
if borrowed {
|
||||||
|
// check for a `mut` coming after the & -- `mut &self` != `&mut self`
|
||||||
|
if self
|
||||||
|
.syntax()
|
||||||
|
.children()
|
||||||
|
.skip_while(|n| n.kind() != AMP)
|
||||||
|
.any(|n| n.kind() == MUT_KW)
|
||||||
|
{
|
||||||
|
SelfParamFlavor::MutRef
|
||||||
|
} else {
|
||||||
|
SelfParamFlavor::Ref
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
SelfParamFlavor::Owned
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_doc_comment_of_items() {
|
fn test_doc_comment_of_items() {
|
||||||
let file = SourceFileNode::parse(
|
let file = SourceFileNode::parse(
|
||||||
|
@ -1442,7 +1442,39 @@ impl<R: TreeRoot<RaTypes>> ImplBlockNode<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a> ImplBlock<'a> {}
|
impl<'a> ImplBlock<'a> {
|
||||||
|
pub fn item_list(self) -> Option<ItemList<'a>> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ImplItem
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||||
|
pub enum ImplItem<'a> {
|
||||||
|
FnDef(FnDef<'a>),
|
||||||
|
TypeDef(TypeDef<'a>),
|
||||||
|
ConstDef(ConstDef<'a>),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AstNode<'a> for ImplItem<'a> {
|
||||||
|
fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
|
||||||
|
match syntax.kind() {
|
||||||
|
FN_DEF => Some(ImplItem::FnDef(FnDef { syntax })),
|
||||||
|
TYPE_DEF => Some(ImplItem::TypeDef(TypeDef { syntax })),
|
||||||
|
CONST_DEF => Some(ImplItem::ConstDef(ConstDef { syntax })),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn syntax(self) -> SyntaxNodeRef<'a> {
|
||||||
|
match self {
|
||||||
|
ImplItem::FnDef(inner) => inner.syntax(),
|
||||||
|
ImplItem::TypeDef(inner) => inner.syntax(),
|
||||||
|
ImplItem::ConstDef(inner) => inner.syntax(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> ImplItem<'a> {}
|
||||||
|
|
||||||
// ImplTraitType
|
// ImplTraitType
|
||||||
#[derive(Debug, Clone, Copy,)]
|
#[derive(Debug, Clone, Copy,)]
|
||||||
@ -1555,7 +1587,11 @@ impl<R: TreeRoot<RaTypes>> ItemListNode<R> {
|
|||||||
|
|
||||||
impl<'a> ast::FnDefOwner<'a> for ItemList<'a> {}
|
impl<'a> ast::FnDefOwner<'a> for ItemList<'a> {}
|
||||||
impl<'a> ast::ModuleItemOwner<'a> for ItemList<'a> {}
|
impl<'a> ast::ModuleItemOwner<'a> for ItemList<'a> {}
|
||||||
impl<'a> ItemList<'a> {}
|
impl<'a> ItemList<'a> {
|
||||||
|
pub fn impl_items(self) -> impl Iterator<Item = ImplItem<'a>> + 'a {
|
||||||
|
super::children(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Label
|
// Label
|
||||||
#[derive(Debug, Clone, Copy,)]
|
#[derive(Debug, Clone, Copy,)]
|
||||||
@ -3452,6 +3488,43 @@ impl<'a> ReturnExpr<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SelfKw
|
||||||
|
#[derive(Debug, Clone, Copy,)]
|
||||||
|
pub struct SelfKwNode<R: TreeRoot<RaTypes> = OwnedRoot> {
|
||||||
|
pub(crate) syntax: SyntaxNode<R>,
|
||||||
|
}
|
||||||
|
pub type SelfKw<'a> = SelfKwNode<RefRoot<'a>>;
|
||||||
|
|
||||||
|
impl<R1: TreeRoot<RaTypes>, R2: TreeRoot<RaTypes>> PartialEq<SelfKwNode<R1>> for SelfKwNode<R2> {
|
||||||
|
fn eq(&self, other: &SelfKwNode<R1>) -> bool { self.syntax == other.syntax }
|
||||||
|
}
|
||||||
|
impl<R: TreeRoot<RaTypes>> Eq for SelfKwNode<R> {}
|
||||||
|
impl<R: TreeRoot<RaTypes>> Hash for SelfKwNode<R> {
|
||||||
|
fn hash<H: Hasher>(&self, state: &mut H) { self.syntax.hash(state) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> AstNode<'a> for SelfKw<'a> {
|
||||||
|
fn cast(syntax: SyntaxNodeRef<'a>) -> Option<Self> {
|
||||||
|
match syntax.kind() {
|
||||||
|
SELF_KW => Some(SelfKw { syntax }),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn syntax(self) -> SyntaxNodeRef<'a> { self.syntax }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<R: TreeRoot<RaTypes>> SelfKwNode<R> {
|
||||||
|
pub fn borrowed(&self) -> SelfKw {
|
||||||
|
SelfKwNode { syntax: self.syntax.borrowed() }
|
||||||
|
}
|
||||||
|
pub fn owned(&self) -> SelfKwNode {
|
||||||
|
SelfKwNode { syntax: self.syntax.owned() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
impl<'a> SelfKw<'a> {}
|
||||||
|
|
||||||
// SelfParam
|
// SelfParam
|
||||||
#[derive(Debug, Clone, Copy,)]
|
#[derive(Debug, Clone, Copy,)]
|
||||||
pub struct SelfParamNode<R: TreeRoot<RaTypes> = OwnedRoot> {
|
pub struct SelfParamNode<R: TreeRoot<RaTypes> = OwnedRoot> {
|
||||||
@ -3487,7 +3560,15 @@ impl<R: TreeRoot<RaTypes>> SelfParamNode<R> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
impl<'a> SelfParam<'a> {}
|
impl<'a> SelfParam<'a> {
|
||||||
|
pub fn type_ref(self) -> Option<TypeRef<'a>> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn self_kw(self) -> Option<SelfKw<'a>> {
|
||||||
|
super::child_opt(self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// SlicePat
|
// SlicePat
|
||||||
#[derive(Debug, Clone, Copy,)]
|
#[derive(Debug, Clone, Copy,)]
|
||||||
|
@ -284,6 +284,7 @@ Grammar(
|
|||||||
options: [ "ItemList" ]
|
options: [ "ItemList" ]
|
||||||
),
|
),
|
||||||
"ItemList": (
|
"ItemList": (
|
||||||
|
collections: [["impl_items", "ImplItem"]],
|
||||||
traits: [ "FnDefOwner", "ModuleItemOwner" ],
|
traits: [ "FnDefOwner", "ModuleItemOwner" ],
|
||||||
),
|
),
|
||||||
"ConstDef": ( traits: [
|
"ConstDef": ( traits: [
|
||||||
@ -307,7 +308,7 @@ Grammar(
|
|||||||
"AttrsOwner",
|
"AttrsOwner",
|
||||||
"DocCommentsOwner"
|
"DocCommentsOwner"
|
||||||
] ),
|
] ),
|
||||||
"ImplBlock": (collections: []),
|
"ImplBlock": (options: ["ItemList"]),
|
||||||
|
|
||||||
"ParenType": (options: ["TypeRef"]),
|
"ParenType": (options: ["TypeRef"]),
|
||||||
"TupleType": ( collections: [["fields", "TypeRef"]] ),
|
"TupleType": ( collections: [["fields", "TypeRef"]] ),
|
||||||
@ -351,6 +352,9 @@ Grammar(
|
|||||||
enum: ["StructDef", "EnumDef", "FnDef", "TraitDef", "TypeDef", "ImplBlock",
|
enum: ["StructDef", "EnumDef", "FnDef", "TraitDef", "TypeDef", "ImplBlock",
|
||||||
"UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ]
|
"UseItem", "ExternCrateItem", "ConstDef", "StaticDef", "Module" ]
|
||||||
),
|
),
|
||||||
|
"ImplItem": (
|
||||||
|
enum: ["FnDef", "TypeDef", "ConstDef"]
|
||||||
|
),
|
||||||
|
|
||||||
"TupleExpr": (),
|
"TupleExpr": (),
|
||||||
"ArrayExpr": (),
|
"ArrayExpr": (),
|
||||||
@ -530,7 +534,8 @@ Grammar(
|
|||||||
["params", "Param"]
|
["params", "Param"]
|
||||||
]
|
]
|
||||||
),
|
),
|
||||||
"SelfParam": (),
|
"SelfParam": (options: ["TypeRef", "SelfKw"]),
|
||||||
|
"SelfKw": (),
|
||||||
"Param": (
|
"Param": (
|
||||||
options: [ "Pat", "TypeRef" ],
|
options: [ "Pat", "TypeRef" ],
|
||||||
),
|
),
|
||||||
|
@ -151,7 +151,7 @@ pub(super) fn maybe_item(p: &mut Parser, flavor: ItemFlavor) -> MaybeItem {
|
|||||||
// test unsafe_default_impl
|
// test unsafe_default_impl
|
||||||
// unsafe default impl Foo {}
|
// unsafe default impl Foo {}
|
||||||
IMPL_KW => {
|
IMPL_KW => {
|
||||||
traits::impl_item(p);
|
traits::impl_block(p);
|
||||||
IMPL_BLOCK
|
IMPL_BLOCK
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
|
@ -40,9 +40,9 @@ pub(crate) fn trait_item_list(p: &mut Parser) {
|
|||||||
m.complete(p, ITEM_LIST);
|
m.complete(p, ITEM_LIST);
|
||||||
}
|
}
|
||||||
|
|
||||||
// test impl_item
|
// test impl_block
|
||||||
// impl Foo {}
|
// impl Foo {}
|
||||||
pub(super) fn impl_item(p: &mut Parser) {
|
pub(super) fn impl_block(p: &mut Parser) {
|
||||||
assert!(p.at(IMPL_KW));
|
assert!(p.at(IMPL_KW));
|
||||||
p.bump();
|
p.bump();
|
||||||
if choose_type_params_over_qpath(p) {
|
if choose_type_params_over_qpath(p) {
|
||||||
@ -52,7 +52,7 @@ pub(super) fn impl_item(p: &mut Parser) {
|
|||||||
// TODO: never type
|
// TODO: never type
|
||||||
// impl ! {}
|
// impl ! {}
|
||||||
|
|
||||||
// test impl_item_neg
|
// test impl_block_neg
|
||||||
// impl !Send for X {}
|
// impl !Send for X {}
|
||||||
p.eat(EXCL);
|
p.eat(EXCL);
|
||||||
impl_type(p);
|
impl_type(p);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user