mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-25 12:33:33 +00:00
Resolve paths to defs (functions currently) during type inference
This commit is contained in:
parent
93ffbf80c6
commit
ef67581104
17 changed files with 215 additions and 87 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -695,6 +695,7 @@ name = "ra_hir"
|
|||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"arrayvec 0.4.9 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"flexi_logger 0.10.3 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"id-arena 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
"parking_lot 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||
|
|
|
@ -94,6 +94,7 @@ salsa::database_storage! {
|
|||
fn fn_syntax() for hir::db::FnSyntaxQuery;
|
||||
fn submodules() for hir::db::SubmodulesQuery;
|
||||
fn infer() for hir::db::InferQuery;
|
||||
fn type_for_def() for hir::db::TypeForDefQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -524,7 +524,7 @@ impl AnalysisImpl {
|
|||
let function = ctry!(source_binder::function_from_source(
|
||||
&*self.db, file_id, parent_fn
|
||||
)?);
|
||||
let infer = function.infer(&*self.db);
|
||||
let infer = function.infer(&*self.db)?;
|
||||
Ok(infer.type_of_node(node).map(|t| t.to_string()))
|
||||
}
|
||||
|
||||
|
|
|
@ -16,3 +16,6 @@ ra_syntax = { path = "../ra_syntax" }
|
|||
ra_editor = { path = "../ra_editor" }
|
||||
ra_db = { path = "../ra_db" }
|
||||
test_utils = { path = "../test_utils" }
|
||||
|
||||
[dev-dependencies]
|
||||
flexi_logger = "0.10.0"
|
||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
|||
function::FnId,
|
||||
module::{ModuleId, ModuleTree, ModuleSource,
|
||||
nameres::{ItemMap, InputModuleItems}},
|
||||
ty::InferenceResult,
|
||||
ty::{InferenceResult, Ty},
|
||||
};
|
||||
|
||||
salsa::query_group! {
|
||||
|
@ -31,11 +31,16 @@ pub trait HirDatabase: SyntaxDatabase
|
|||
use fn query_definitions::fn_syntax;
|
||||
}
|
||||
|
||||
fn infer(fn_id: FnId) -> Arc<InferenceResult> {
|
||||
fn infer(fn_id: FnId) -> Cancelable<Arc<InferenceResult>> {
|
||||
type InferQuery;
|
||||
use fn query_definitions::infer;
|
||||
}
|
||||
|
||||
fn type_for_def(def_id: DefId) -> Cancelable<Ty> {
|
||||
type TypeForDefQuery;
|
||||
use fn query_definitions::type_for_def;
|
||||
}
|
||||
|
||||
fn file_items(file_id: FileId) -> Arc<SourceFileItems> {
|
||||
type SourceFileItemsQuery;
|
||||
use fn query_definitions::file_items;
|
||||
|
|
|
@ -5,12 +5,13 @@ use std::{
|
|||
sync::Arc,
|
||||
};
|
||||
|
||||
use ra_db::Cancelable;
|
||||
use ra_syntax::{
|
||||
TextRange, TextUnit,
|
||||
ast::{self, AstNode, DocCommentsOwner, NameOwner},
|
||||
};
|
||||
|
||||
use crate::{ DefId, HirDatabase, ty::InferenceResult };
|
||||
use crate::{ DefId, HirDatabase, ty::InferenceResult, Module };
|
||||
|
||||
pub use self::scope::FnScopes;
|
||||
|
||||
|
@ -18,7 +19,7 @@ pub use self::scope::FnScopes;
|
|||
pub struct FnId(pub(crate) DefId);
|
||||
|
||||
pub struct Function {
|
||||
fn_id: FnId,
|
||||
pub(crate) fn_id: FnId,
|
||||
}
|
||||
|
||||
impl Function {
|
||||
|
@ -27,6 +28,10 @@ impl Function {
|
|||
Function { fn_id }
|
||||
}
|
||||
|
||||
pub fn syntax(&self, db: &impl HirDatabase) -> ast::FnDefNode {
|
||||
db.fn_syntax(self.fn_id)
|
||||
}
|
||||
|
||||
pub fn scopes(&self, db: &impl HirDatabase) -> Arc<FnScopes> {
|
||||
db.fn_scopes(self.fn_id)
|
||||
}
|
||||
|
@ -36,9 +41,14 @@ impl Function {
|
|||
FnSignatureInfo::new(syntax.borrowed())
|
||||
}
|
||||
|
||||
pub fn infer(&self, db: &impl HirDatabase) -> Arc<InferenceResult> {
|
||||
pub fn infer(&self, db: &impl HirDatabase) -> Cancelable<Arc<InferenceResult>> {
|
||||
db.infer(self.fn_id)
|
||||
}
|
||||
|
||||
pub fn module(&self, db: &impl HirDatabase) -> Cancelable<Module> {
|
||||
let loc = self.fn_id.0.loc(db);
|
||||
Module::new(db, loc.source_root_id, loc.module_id)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
|
|
|
@ -29,7 +29,7 @@ mod ty;
|
|||
|
||||
use std::ops::Index;
|
||||
|
||||
use ra_syntax::{SyntaxNodeRef, SyntaxNode};
|
||||
use ra_syntax::{SyntaxNodeRef, SyntaxNode, SyntaxKind};
|
||||
use ra_db::{LocationIntener, SourceRootId, FileId, Cancelable};
|
||||
|
||||
use crate::{
|
||||
|
@ -67,6 +67,23 @@ pub struct DefLoc {
|
|||
source_item_id: SourceItemId,
|
||||
}
|
||||
|
||||
impl DefKind {
|
||||
pub(crate) fn for_syntax_kind(kind: SyntaxKind) -> Option<DefKind> {
|
||||
match kind {
|
||||
SyntaxKind::FN_DEF => Some(DefKind::Function),
|
||||
SyntaxKind::MODULE => Some(DefKind::Module),
|
||||
// These define items, but don't have their own DefKinds yet:
|
||||
SyntaxKind::STRUCT_DEF => Some(DefKind::Item),
|
||||
SyntaxKind::ENUM_DEF => Some(DefKind::Item),
|
||||
SyntaxKind::TRAIT_DEF => Some(DefKind::Item),
|
||||
SyntaxKind::TYPE_DEF => Some(DefKind::Item),
|
||||
SyntaxKind::CONST_DEF => Some(DefKind::Item),
|
||||
SyntaxKind::STATIC_DEF => Some(DefKind::Item),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DefId {
|
||||
pub(crate) fn loc(self, db: &impl AsRef<LocationIntener<DefLoc, DefId>>) -> DefLoc {
|
||||
db.as_ref().id2loc(self)
|
||||
|
|
|
@ -192,6 +192,7 @@ salsa::database_storage! {
|
|||
fn fn_syntax() for db::FnSyntaxQuery;
|
||||
fn submodules() for db::SubmodulesQuery;
|
||||
fn infer() for db::InferQuery;
|
||||
fn type_for_def() for db::TypeForDefQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ pub(super) mod imp;
|
|||
pub(super) mod nameres;
|
||||
|
||||
use std::sync::Arc;
|
||||
use log;
|
||||
|
||||
use ra_syntax::{
|
||||
algo::generate,
|
||||
|
|
|
@ -272,13 +272,13 @@ where
|
|||
}
|
||||
}
|
||||
}
|
||||
// Populate explicitelly declared items, except modules
|
||||
// Populate explicitly declared items, except modules
|
||||
for item in input.items.iter() {
|
||||
if item.kind == MODULE {
|
||||
continue;
|
||||
}
|
||||
let def_loc = DefLoc {
|
||||
kind: DefKind::Item,
|
||||
kind: DefKind::for_syntax_kind(item.kind).unwrap_or(DefKind::Item),
|
||||
source_root_id: self.source_root,
|
||||
module_id,
|
||||
source_item_id: SourceItemId {
|
||||
|
|
|
@ -11,7 +11,7 @@ use ra_syntax::{
|
|||
use ra_db::{SourceRootId, FileId, Cancelable,};
|
||||
|
||||
use crate::{
|
||||
SourceFileItems, SourceItemId, DefKind,
|
||||
SourceFileItems, SourceItemId, DefKind, Function, DefId,
|
||||
db::HirDatabase,
|
||||
function::{FnScopes, FnId},
|
||||
module::{
|
||||
|
@ -19,7 +19,7 @@ use crate::{
|
|||
imp::Submodule,
|
||||
nameres::{InputModuleItems, ItemMap, Resolver},
|
||||
},
|
||||
ty::{self, InferenceResult}
|
||||
ty::{self, InferenceResult, Ty}
|
||||
};
|
||||
|
||||
/// Resolve `FnId` to the corresponding `SyntaxNode`
|
||||
|
@ -36,11 +36,13 @@ pub(super) fn fn_scopes(db: &impl HirDatabase, fn_id: FnId) -> Arc<FnScopes> {
|
|||
Arc::new(res)
|
||||
}
|
||||
|
||||
pub(super) fn infer(db: &impl HirDatabase, fn_id: FnId) -> Arc<InferenceResult> {
|
||||
let syntax = db.fn_syntax(fn_id);
|
||||
let scopes = db.fn_scopes(fn_id);
|
||||
let res = ty::infer(db, syntax.borrowed(), scopes);
|
||||
Arc::new(res)
|
||||
pub(super) fn infer(db: &impl HirDatabase, fn_id: FnId) -> Cancelable<Arc<InferenceResult>> {
|
||||
let function = Function { fn_id };
|
||||
ty::infer(db, function).map(Arc::new)
|
||||
}
|
||||
|
||||
pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
|
||||
ty::type_for_def(db, def_id)
|
||||
}
|
||||
|
||||
pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> {
|
||||
|
|
|
@ -5,21 +5,17 @@ mod tests;
|
|||
use std::sync::Arc;
|
||||
use std::fmt;
|
||||
|
||||
use log;
|
||||
use rustc_hash::{FxHashMap};
|
||||
|
||||
use ra_db::LocalSyntaxPtr;
|
||||
use ra_db::{LocalSyntaxPtr, Cancelable};
|
||||
use ra_syntax::{
|
||||
SmolStr,
|
||||
ast::{self, AstNode, LoopBodyOwner, ArgListOwner},
|
||||
SyntaxNodeRef
|
||||
};
|
||||
|
||||
use crate::{
|
||||
FnScopes,
|
||||
db::HirDatabase,
|
||||
};
|
||||
|
||||
// pub(crate) type TypeId = Id<Ty>;
|
||||
use crate::{Def, DefId, FnScopes, Module, Function, Path, db::HirDatabase};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub enum Ty {
|
||||
|
@ -65,18 +61,6 @@ pub enum Ty {
|
|||
/// `&'a mut T` or `&'a T`.
|
||||
// Ref(Region<'tcx>, Ty<'tcx>, hir::Mutability),
|
||||
|
||||
/// The anonymous type of a function declaration/definition. Each
|
||||
/// function has a unique type, which is output (for a function
|
||||
/// named `foo` returning an `i32`) as `fn() -> i32 {foo}`.
|
||||
///
|
||||
/// For example the type of `bar` here:
|
||||
///
|
||||
/// ```rust
|
||||
/// fn foo() -> i32 { 1 }
|
||||
/// let bar = foo; // bar: fn() -> i32 {foo}
|
||||
/// ```
|
||||
// FnDef(DefId, &'tcx Substs<'tcx>),
|
||||
|
||||
/// A pointer to a function. Written as `fn() -> i32`.
|
||||
///
|
||||
/// For example the type of `bar` here:
|
||||
|
@ -85,7 +69,7 @@ pub enum Ty {
|
|||
/// fn foo() -> i32 { 1 }
|
||||
/// let bar: fn() -> i32 = foo;
|
||||
/// ```
|
||||
// FnPtr(PolyFnSig<'tcx>),
|
||||
FnPtr(Arc<FnSig>),
|
||||
|
||||
/// A trait, defined with `trait`.
|
||||
// Dynamic(Binder<&'tcx List<ExistentialPredicate<'tcx>>>, ty::Region<'tcx>),
|
||||
|
@ -139,6 +123,12 @@ pub enum Ty {
|
|||
|
||||
type TyRef = Arc<Ty>;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
pub struct FnSig {
|
||||
input: Vec<Ty>,
|
||||
output: Ty,
|
||||
}
|
||||
|
||||
impl Ty {
|
||||
pub fn new(node: ast::TypeRef) -> Self {
|
||||
use ra_syntax::ast::TypeRef::*;
|
||||
|
@ -208,11 +198,55 @@ impl fmt::Display for Ty {
|
|||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
Ty::FnPtr(sig) => {
|
||||
write!(f, "fn(")?;
|
||||
for t in &sig.input {
|
||||
write!(f, "{},", t)?;
|
||||
}
|
||||
write!(f, ") -> {}", sig.output)
|
||||
}
|
||||
Ty::Unknown => write!(f, "[unknown]"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
|
||||
eprintln!("type_for_fn {:?}", f.fn_id);
|
||||
let syntax = f.syntax(db);
|
||||
let node = syntax.borrowed();
|
||||
// TODO we ignore type parameters for now
|
||||
let input = node
|
||||
.param_list()
|
||||
.map(|pl| {
|
||||
pl.params()
|
||||
.map(|p| p.type_ref().map(|t| Ty::new(t)).unwrap_or(Ty::Unknown))
|
||||
.collect()
|
||||
})
|
||||
.unwrap_or_else(Vec::new);
|
||||
let output = node
|
||||
.ret_type()
|
||||
.and_then(|rt| rt.type_ref())
|
||||
.map(|t| Ty::new(t))
|
||||
.unwrap_or(Ty::Unknown);
|
||||
let sig = FnSig { input, output };
|
||||
Ok(Ty::FnPtr(Arc::new(sig)))
|
||||
}
|
||||
|
||||
pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
|
||||
let def = def_id.resolve(db)?;
|
||||
match def {
|
||||
Def::Module(..) => {
|
||||
log::debug!("trying to get type for module {:?}", def_id);
|
||||
Ok(Ty::Unknown)
|
||||
}
|
||||
Def::Function(f) => type_for_fn(db, f),
|
||||
Def::Item => {
|
||||
log::debug!("trying to get type for item of unknown type {:?}", def_id);
|
||||
Ok(Ty::Unknown)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct InferenceResult {
|
||||
type_for: FxHashMap<LocalSyntaxPtr, Ty>,
|
||||
|
@ -224,18 +258,22 @@ impl InferenceResult {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||
pub struct InferenceContext {
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct InferenceContext<'a, D: HirDatabase> {
|
||||
db: &'a D,
|
||||
scopes: Arc<FnScopes>,
|
||||
module: Module,
|
||||
// TODO unification tables...
|
||||
type_for: FxHashMap<LocalSyntaxPtr, Ty>,
|
||||
}
|
||||
|
||||
impl InferenceContext {
|
||||
fn new(scopes: Arc<FnScopes>) -> Self {
|
||||
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
fn new(db: &'a D, scopes: Arc<FnScopes>, module: Module) -> Self {
|
||||
InferenceContext {
|
||||
type_for: FxHashMap::default(),
|
||||
db,
|
||||
scopes,
|
||||
module,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -262,36 +300,42 @@ impl InferenceContext {
|
|||
self.unify(ty1, ty2)
|
||||
}
|
||||
|
||||
fn infer_path_expr(&mut self, expr: ast::PathExpr) -> Option<Ty> {
|
||||
let p = expr.path()?;
|
||||
if p.qualifier().is_none() {
|
||||
let name = p.segment().and_then(|s| s.name_ref())?;
|
||||
let scope_entry = self.scopes.resolve_local_name(name)?;
|
||||
let ty = self.type_for.get(&scope_entry.ptr())?;
|
||||
Some(ty.clone())
|
||||
} else {
|
||||
// TODO resolve path
|
||||
Some(Ty::Unknown)
|
||||
}
|
||||
fn infer_path_expr(&mut self, expr: ast::PathExpr) -> Cancelable<Option<Ty>> {
|
||||
let ast_path = ctry!(expr.path());
|
||||
let path = ctry!(Path::from_ast(ast_path));
|
||||
if path.is_ident() {
|
||||
// resolve locally
|
||||
let name = ctry!(ast_path.segment().and_then(|s| s.name_ref()));
|
||||
if let Some(scope_entry) = self.scopes.resolve_local_name(name) {
|
||||
let ty = ctry!(self.type_for.get(&scope_entry.ptr()));
|
||||
return Ok(Some(ty.clone()));
|
||||
};
|
||||
};
|
||||
|
||||
// resolve in module
|
||||
let resolved = ctry!(self.module.resolve_path(self.db, path)?);
|
||||
let ty = self.db.type_for_def(resolved)?;
|
||||
// TODO we will need to add type variables for type parameters etc. here
|
||||
Ok(Some(ty))
|
||||
}
|
||||
|
||||
fn infer_expr(&mut self, expr: ast::Expr) -> Ty {
|
||||
fn infer_expr(&mut self, expr: ast::Expr) -> Cancelable<Ty> {
|
||||
let ty = match expr {
|
||||
ast::Expr::IfExpr(e) => {
|
||||
if let Some(condition) = e.condition() {
|
||||
if let Some(e) = condition.expr() {
|
||||
// TODO if no pat, this should be bool
|
||||
self.infer_expr(e);
|
||||
self.infer_expr(e)?;
|
||||
}
|
||||
// TODO write type for pat
|
||||
};
|
||||
let if_ty = if let Some(block) = e.then_branch() {
|
||||
self.infer_block(block)
|
||||
self.infer_block(block)?
|
||||
} else {
|
||||
Ty::Unknown
|
||||
};
|
||||
let else_ty = if let Some(block) = e.else_branch() {
|
||||
self.infer_block(block)
|
||||
self.infer_block(block)?
|
||||
} else {
|
||||
Ty::Unknown
|
||||
};
|
||||
|
@ -304,14 +348,14 @@ impl InferenceContext {
|
|||
}
|
||||
ast::Expr::BlockExpr(e) => {
|
||||
if let Some(block) = e.block() {
|
||||
self.infer_block(block)
|
||||
self.infer_block(block)?
|
||||
} else {
|
||||
Ty::Unknown
|
||||
}
|
||||
}
|
||||
ast::Expr::LoopExpr(e) => {
|
||||
if let Some(block) = e.loop_body() {
|
||||
self.infer_block(block);
|
||||
self.infer_block(block)?;
|
||||
};
|
||||
// TODO never, or the type of the break param
|
||||
Ty::Unknown
|
||||
|
@ -320,59 +364,69 @@ impl InferenceContext {
|
|||
if let Some(condition) = e.condition() {
|
||||
if let Some(e) = condition.expr() {
|
||||
// TODO if no pat, this should be bool
|
||||
self.infer_expr(e);
|
||||
self.infer_expr(e)?;
|
||||
}
|
||||
// TODO write type for pat
|
||||
};
|
||||
if let Some(block) = e.loop_body() {
|
||||
// TODO
|
||||
self.infer_block(block);
|
||||
self.infer_block(block)?;
|
||||
};
|
||||
// TODO always unit?
|
||||
Ty::Unknown
|
||||
}
|
||||
ast::Expr::ForExpr(e) => {
|
||||
if let Some(expr) = e.iterable() {
|
||||
self.infer_expr(expr);
|
||||
self.infer_expr(expr)?;
|
||||
}
|
||||
if let Some(_pat) = e.pat() {
|
||||
// TODO write type for pat
|
||||
}
|
||||
if let Some(block) = e.loop_body() {
|
||||
self.infer_block(block);
|
||||
self.infer_block(block)?;
|
||||
}
|
||||
// TODO always unit?
|
||||
Ty::Unknown
|
||||
}
|
||||
ast::Expr::LambdaExpr(e) => {
|
||||
let _body_ty = if let Some(body) = e.body() {
|
||||
self.infer_expr(body)
|
||||
self.infer_expr(body)?
|
||||
} else {
|
||||
Ty::Unknown
|
||||
};
|
||||
Ty::Unknown
|
||||
}
|
||||
ast::Expr::CallExpr(e) => {
|
||||
let _callee_ty = if let Some(e) = e.expr() {
|
||||
self.infer_expr(e)?
|
||||
} else {
|
||||
Ty::Unknown
|
||||
};
|
||||
if let Some(arg_list) = e.arg_list() {
|
||||
for arg in arg_list.args() {
|
||||
// TODO unify / expect argument type
|
||||
self.infer_expr(arg);
|
||||
self.infer_expr(arg)?;
|
||||
}
|
||||
}
|
||||
Ty::Unknown
|
||||
}
|
||||
ast::Expr::MethodCallExpr(e) => {
|
||||
let _receiver_ty = if let Some(e) = e.expr() {
|
||||
self.infer_expr(e)?
|
||||
} else {
|
||||
Ty::Unknown
|
||||
};
|
||||
if let Some(arg_list) = e.arg_list() {
|
||||
for arg in arg_list.args() {
|
||||
// TODO unify / expect argument type
|
||||
self.infer_expr(arg);
|
||||
self.infer_expr(arg)?;
|
||||
}
|
||||
}
|
||||
Ty::Unknown
|
||||
}
|
||||
ast::Expr::MatchExpr(e) => {
|
||||
let _ty = if let Some(match_expr) = e.expr() {
|
||||
self.infer_expr(match_expr)
|
||||
self.infer_expr(match_expr)?
|
||||
} else {
|
||||
Ty::Unknown
|
||||
};
|
||||
|
@ -381,7 +435,7 @@ impl InferenceContext {
|
|||
// TODO type the bindings in pat
|
||||
// TODO type the guard
|
||||
let _ty = if let Some(e) = arm.expr() {
|
||||
self.infer_expr(e)
|
||||
self.infer_expr(e)?
|
||||
} else {
|
||||
Ty::Unknown
|
||||
};
|
||||
|
@ -394,12 +448,12 @@ impl InferenceContext {
|
|||
}
|
||||
ast::Expr::TupleExpr(_e) => Ty::Unknown,
|
||||
ast::Expr::ArrayExpr(_e) => Ty::Unknown,
|
||||
ast::Expr::PathExpr(e) => self.infer_path_expr(e).unwrap_or(Ty::Unknown),
|
||||
ast::Expr::PathExpr(e) => self.infer_path_expr(e)?.unwrap_or(Ty::Unknown),
|
||||
ast::Expr::ContinueExpr(_e) => Ty::Never,
|
||||
ast::Expr::BreakExpr(_e) => Ty::Never,
|
||||
ast::Expr::ParenExpr(e) => {
|
||||
if let Some(e) = e.expr() {
|
||||
self.infer_expr(e)
|
||||
self.infer_expr(e)?
|
||||
} else {
|
||||
Ty::Unknown
|
||||
}
|
||||
|
@ -408,7 +462,7 @@ impl InferenceContext {
|
|||
ast::Expr::ReturnExpr(e) => {
|
||||
if let Some(e) = e.expr() {
|
||||
// TODO unify with return type
|
||||
self.infer_expr(e);
|
||||
self.infer_expr(e)?;
|
||||
};
|
||||
Ty::Never
|
||||
}
|
||||
|
@ -425,7 +479,7 @@ impl InferenceContext {
|
|||
ast::Expr::FieldExpr(_e) => Ty::Unknown,
|
||||
ast::Expr::TryExpr(e) => {
|
||||
let _inner_ty = if let Some(e) = e.expr() {
|
||||
self.infer_expr(e)
|
||||
self.infer_expr(e)?
|
||||
} else {
|
||||
Ty::Unknown
|
||||
};
|
||||
|
@ -433,7 +487,7 @@ impl InferenceContext {
|
|||
}
|
||||
ast::Expr::CastExpr(e) => {
|
||||
let _inner_ty = if let Some(e) = e.expr() {
|
||||
self.infer_expr(e)
|
||||
self.infer_expr(e)?
|
||||
} else {
|
||||
Ty::Unknown
|
||||
};
|
||||
|
@ -443,7 +497,7 @@ impl InferenceContext {
|
|||
}
|
||||
ast::Expr::RefExpr(e) => {
|
||||
let _inner_ty = if let Some(e) = e.expr() {
|
||||
self.infer_expr(e)
|
||||
self.infer_expr(e)?
|
||||
} else {
|
||||
Ty::Unknown
|
||||
};
|
||||
|
@ -451,7 +505,7 @@ impl InferenceContext {
|
|||
}
|
||||
ast::Expr::PrefixExpr(e) => {
|
||||
let _inner_ty = if let Some(e) = e.expr() {
|
||||
self.infer_expr(e)
|
||||
self.infer_expr(e)?
|
||||
} else {
|
||||
Ty::Unknown
|
||||
};
|
||||
|
@ -462,10 +516,10 @@ impl InferenceContext {
|
|||
ast::Expr::Literal(_e) => Ty::Unknown,
|
||||
};
|
||||
self.write_ty(expr.syntax(), ty.clone());
|
||||
ty
|
||||
Ok(ty)
|
||||
}
|
||||
|
||||
fn infer_block(&mut self, node: ast::Block) -> Ty {
|
||||
fn infer_block(&mut self, node: ast::Block) -> Cancelable<Ty> {
|
||||
for stmt in node.statements() {
|
||||
match stmt {
|
||||
ast::Stmt::LetStmt(stmt) => {
|
||||
|
@ -476,7 +530,7 @@ impl InferenceContext {
|
|||
};
|
||||
let ty = if let Some(expr) = stmt.initializer() {
|
||||
// TODO pass expectation
|
||||
let expr_ty = self.infer_expr(expr);
|
||||
let expr_ty = self.infer_expr(expr)?;
|
||||
self.unify_with_coercion(&expr_ty, &decl_ty)
|
||||
.unwrap_or(decl_ty)
|
||||
} else {
|
||||
|
@ -489,23 +543,28 @@ impl InferenceContext {
|
|||
}
|
||||
ast::Stmt::ExprStmt(expr_stmt) => {
|
||||
if let Some(expr) = expr_stmt.expr() {
|
||||
self.infer_expr(expr);
|
||||
self.infer_expr(expr)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let ty = if let Some(expr) = node.expr() {
|
||||
self.infer_expr(expr)
|
||||
self.infer_expr(expr)?
|
||||
} else {
|
||||
Ty::unit()
|
||||
};
|
||||
self.write_ty(node.syntax(), ty.clone());
|
||||
ty
|
||||
Ok(ty)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn infer(_db: &impl HirDatabase, node: ast::FnDef, scopes: Arc<FnScopes>) -> InferenceResult {
|
||||
let mut ctx = InferenceContext::new(scopes);
|
||||
pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceResult> {
|
||||
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();
|
||||
|
||||
if let Some(param_list) = node.param_list() {
|
||||
for param in param_list.params() {
|
||||
|
@ -529,12 +588,12 @@ pub fn infer(_db: &impl HirDatabase, node: ast::FnDef, scopes: Arc<FnScopes>) ->
|
|||
// (see Expectation in rustc_typeck)
|
||||
|
||||
if let Some(block) = node.body() {
|
||||
ctx.infer_block(block);
|
||||
ctx.infer_block(block)?;
|
||||
}
|
||||
|
||||
// TODO 'resolve' the types: replace inference variables by their inferred results
|
||||
|
||||
InferenceResult {
|
||||
Ok(InferenceResult {
|
||||
type_for: ctx.type_for,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
use std::fmt::Write;
|
||||
use std::path::{PathBuf};
|
||||
use std::sync::Once;
|
||||
|
||||
use flexi_logger::Logger;
|
||||
|
||||
use ra_db::{SyntaxDatabase};
|
||||
use ra_syntax::ast::{self, AstNode};
|
||||
|
@ -22,7 +25,7 @@ fn infer_file(content: &str) -> String {
|
|||
let func = source_binder::function_from_source(&db, file_id, fn_def)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
let inference_result = func.infer(&db);
|
||||
let inference_result = func.infer(&db).unwrap();
|
||||
for (syntax_ptr, ty) in &inference_result.type_for {
|
||||
let node = syntax_ptr.resolve(&source_file);
|
||||
write!(
|
||||
|
@ -58,6 +61,8 @@ fn ellipsize(mut text: String, max_len: usize) -> String {
|
|||
|
||||
#[test]
|
||||
pub fn infer_tests() {
|
||||
static INIT: Once = Once::new();
|
||||
INIT.call_once(|| Logger::with_env().start().unwrap());
|
||||
dir_tests(&test_data_dir(), &["."], |text, _path| infer_file(text));
|
||||
}
|
||||
|
||||
|
|
10
crates/ra_hir/src/ty/tests/data/0003_paths.rs
Normal file
10
crates/ra_hir/src/ty/tests/data/0003_paths.rs
Normal file
|
@ -0,0 +1,10 @@
|
|||
fn a() -> u32 { 1 }
|
||||
|
||||
mod b {
|
||||
fn c() -> u32 { 1 }
|
||||
}
|
||||
|
||||
fn test() {
|
||||
a();
|
||||
b::c();
|
||||
}
|
9
crates/ra_hir/src/ty/tests/data/0003_paths.txt
Normal file
9
crates/ra_hir/src/ty/tests/data/0003_paths.txt
Normal file
|
@ -0,0 +1,9 @@
|
|||
[16; 17) '1': [unknown]
|
||||
[14; 19) '{ 1 }': [unknown]
|
||||
[47; 52) '{ 1 }': [unknown]
|
||||
[49; 50) '1': [unknown]
|
||||
[81; 87) 'b::c()': [unknown]
|
||||
[66; 90) '{ ...c(); }': ()
|
||||
[72; 73) 'a': fn() -> u32
|
||||
[72; 75) 'a()': [unknown]
|
||||
[81; 85) 'b::c': fn() -> u32
|
|
@ -3083,7 +3083,11 @@ impl<R: TreeRoot<RaTypes>> RetTypeNode<R> {
|
|||
}
|
||||
|
||||
|
||||
impl<'a> RetType<'a> {}
|
||||
impl<'a> RetType<'a> {
|
||||
pub fn type_ref(self) -> Option<TypeRef<'a>> {
|
||||
super::child_opt(self)
|
||||
}
|
||||
}
|
||||
|
||||
// ReturnExpr
|
||||
#[derive(Debug, Clone, Copy,)]
|
||||
|
|
|
@ -254,7 +254,7 @@ Grammar(
|
|||
],
|
||||
options: [ "ParamList", ["body", "Block"], "RetType" ],
|
||||
),
|
||||
"RetType": (),
|
||||
"RetType": (options: ["TypeRef"]),
|
||||
"StructDef": (
|
||||
traits: [
|
||||
"NameOwner",
|
||||
|
|
Loading…
Reference in a new issue