Add basic HIR and types for structs/enums
This commit is contained in:
parent
5fb426cb9e
commit
b5b68f2094
@ -95,6 +95,8 @@ salsa::database_storage! {
|
||||
fn submodules() for hir::db::SubmodulesQuery;
|
||||
fn infer() for hir::db::InferQuery;
|
||||
fn type_for_def() for hir::db::TypeForDefQuery;
|
||||
fn struct_data() for db::StructDataQuery;
|
||||
fn enum_data() for db::EnumDataQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
114
crates/ra_hir/src/adt.rs
Normal file
114
crates/ra_hir/src/adt.rs
Normal file
@ -0,0 +1,114 @@
|
||||
use ra_syntax::{SmolStr, ast::{self, NameOwner}};
|
||||
|
||||
use crate::{
|
||||
DefId, Cancelable,
|
||||
db::{HirDatabase},
|
||||
ty::{Ty},
|
||||
};
|
||||
|
||||
pub struct Struct {
|
||||
def_id: DefId,
|
||||
}
|
||||
|
||||
impl Struct {
|
||||
pub(crate) fn new(def_id: DefId) -> Self {
|
||||
Struct { def_id }
|
||||
}
|
||||
|
||||
pub fn name(&self, db: &impl HirDatabase) -> Cancelable<SmolStr> {
|
||||
Ok(db.struct_data(self.def_id)?.name.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct StructData {
|
||||
name: SmolStr,
|
||||
variant_data: VariantData,
|
||||
}
|
||||
|
||||
impl StructData {
|
||||
pub(crate) fn new(struct_def: ast::StructDef) -> StructData {
|
||||
let name = struct_def
|
||||
.name()
|
||||
.map(|n| n.text())
|
||||
.unwrap_or(SmolStr::new("[error]"));
|
||||
let variant_data = VariantData::Unit; // TODO implement this
|
||||
StructData { name, variant_data }
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Enum {
|
||||
def_id: DefId,
|
||||
}
|
||||
|
||||
impl Enum {
|
||||
pub(crate) fn new(def_id: DefId) -> Self {
|
||||
Enum { def_id }
|
||||
}
|
||||
|
||||
pub fn name(&self, db: &impl HirDatabase) -> Cancelable<SmolStr> {
|
||||
Ok(db.enum_data(self.def_id)?.name.clone())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct EnumData {
|
||||
name: SmolStr,
|
||||
variants: Vec<(SmolStr, VariantData)>,
|
||||
}
|
||||
|
||||
impl EnumData {
|
||||
pub(crate) fn new(enum_def: ast::EnumDef) -> Self {
|
||||
let name = enum_def
|
||||
.name()
|
||||
.map(|n| n.text())
|
||||
.unwrap_or(SmolStr::new("[error]"));
|
||||
let variants = Vec::new(); // TODO implement this
|
||||
EnumData { name, variants }
|
||||
}
|
||||
}
|
||||
|
||||
/// A single field of an enum variant or struct
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub struct StructField {
|
||||
name: SmolStr,
|
||||
ty: Ty,
|
||||
}
|
||||
|
||||
/// Fields of an enum variant or struct
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
pub enum VariantData {
|
||||
Struct(Vec<StructField>),
|
||||
Tuple(Vec<StructField>),
|
||||
Unit,
|
||||
}
|
||||
|
||||
impl VariantData {
|
||||
pub fn fields(&self) -> &[StructField] {
|
||||
match *self {
|
||||
VariantData::Struct(ref fields) | VariantData::Tuple(ref fields) => fields,
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
pub fn is_struct(&self) -> bool {
|
||||
if let VariantData::Struct(..) = *self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
pub fn is_tuple(&self) -> bool {
|
||||
if let VariantData::Tuple(..) = *self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
pub fn is_unit(&self) -> bool {
|
||||
if let VariantData::Unit = *self {
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
@ -15,6 +15,7 @@ use crate::{
|
||||
module::{ModuleId, ModuleTree, ModuleSource,
|
||||
nameres::{ItemMap, InputModuleItems}},
|
||||
ty::{InferenceResult, Ty},
|
||||
adt::{StructData, EnumData},
|
||||
};
|
||||
|
||||
salsa::query_group! {
|
||||
@ -31,6 +32,16 @@ pub trait HirDatabase: SyntaxDatabase
|
||||
use fn query_definitions::fn_syntax;
|
||||
}
|
||||
|
||||
fn struct_data(def_id: DefId) -> Cancelable<Arc<StructData>> {
|
||||
type StructDataQuery;
|
||||
use fn query_definitions::struct_data;
|
||||
}
|
||||
|
||||
fn enum_data(def_id: DefId) -> Cancelable<Arc<EnumData>> {
|
||||
type EnumDataQuery;
|
||||
use fn query_definitions::enum_data;
|
||||
}
|
||||
|
||||
fn infer(fn_id: FnId) -> Cancelable<Arc<InferenceResult>> {
|
||||
type InferQuery;
|
||||
use fn query_definitions::infer;
|
||||
|
@ -25,6 +25,7 @@ pub mod source_binder;
|
||||
mod krate;
|
||||
mod module;
|
||||
mod function;
|
||||
mod adt;
|
||||
mod ty;
|
||||
|
||||
use std::ops::Index;
|
||||
@ -42,6 +43,7 @@ pub use self::{
|
||||
krate::Crate,
|
||||
module::{Module, ModuleId, Problem, nameres::ItemMap, ModuleScope, Resolution},
|
||||
function::{Function, FnScopes},
|
||||
adt::{Struct, Enum},
|
||||
};
|
||||
|
||||
pub use self::function::FnSignatureInfo;
|
||||
@ -56,6 +58,8 @@ ra_db::impl_numeric_id!(DefId);
|
||||
pub(crate) enum DefKind {
|
||||
Module,
|
||||
Function,
|
||||
Struct,
|
||||
Enum,
|
||||
Item,
|
||||
}
|
||||
|
||||
@ -73,8 +77,8 @@ impl DefKind {
|
||||
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::STRUCT_DEF => Some(DefKind::Struct),
|
||||
SyntaxKind::ENUM_DEF => Some(DefKind::Enum),
|
||||
SyntaxKind::TRAIT_DEF => Some(DefKind::Item),
|
||||
SyntaxKind::TYPE_DEF => Some(DefKind::Item),
|
||||
SyntaxKind::CONST_DEF => Some(DefKind::Item),
|
||||
@ -99,6 +103,8 @@ impl DefLoc {
|
||||
pub enum Def {
|
||||
Module(Module),
|
||||
Function(Function),
|
||||
Struct(Struct),
|
||||
Enum(Enum),
|
||||
Item,
|
||||
}
|
||||
|
||||
@ -114,6 +120,14 @@ impl DefId {
|
||||
let function = Function::new(self);
|
||||
Def::Function(function)
|
||||
}
|
||||
DefKind::Struct => {
|
||||
let struct_def = Struct::new(self);
|
||||
Def::Struct(struct_def)
|
||||
}
|
||||
DefKind::Enum => {
|
||||
let enum_def = Enum::new(self);
|
||||
Def::Enum(enum_def)
|
||||
}
|
||||
DefKind::Item => Def::Item,
|
||||
};
|
||||
Ok(res)
|
||||
|
@ -193,6 +193,8 @@ salsa::database_storage! {
|
||||
fn submodules() for db::SubmodulesQuery;
|
||||
fn infer() for db::InferQuery;
|
||||
fn type_for_def() for db::TypeForDefQuery;
|
||||
fn struct_data() for db::StructDataQuery;
|
||||
fn enum_data() for db::EnumDataQuery;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,8 @@ use crate::{
|
||||
imp::Submodule,
|
||||
nameres::{InputModuleItems, ItemMap, Resolver},
|
||||
},
|
||||
ty::{self, InferenceResult, Ty}
|
||||
ty::{self, InferenceResult, Ty},
|
||||
adt::{StructData, EnumData},
|
||||
};
|
||||
|
||||
/// Resolve `FnId` to the corresponding `SyntaxNode`
|
||||
@ -45,6 +46,24 @@ pub(super) fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<T
|
||||
ty::type_for_def(db, def_id)
|
||||
}
|
||||
|
||||
pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> {
|
||||
let def_loc = def_id.loc(db);
|
||||
assert!(def_loc.kind == DefKind::Struct);
|
||||
let syntax = db.file_item(def_loc.source_item_id);
|
||||
let struct_def =
|
||||
ast::StructDef::cast(syntax.borrowed()).expect("struct def should point to StructDef node");
|
||||
Ok(Arc::new(StructData::new(struct_def.borrowed())))
|
||||
}
|
||||
|
||||
pub(super) fn enum_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<EnumData>> {
|
||||
let def_loc = def_id.loc(db);
|
||||
assert!(def_loc.kind == DefKind::Enum);
|
||||
let syntax = db.file_item(def_loc.source_item_id);
|
||||
let enum_def =
|
||||
ast::EnumDef::cast(syntax.borrowed()).expect("enum def should point to EnumDef node");
|
||||
Ok(Arc::new(EnumData::new(enum_def.borrowed())))
|
||||
}
|
||||
|
||||
pub(super) fn file_items(db: &impl HirDatabase, file_id: FileId) -> Arc<SourceFileItems> {
|
||||
let mut res = SourceFileItems::new(file_id);
|
||||
let source_file = db.source_file(file_id);
|
||||
|
@ -17,7 +17,7 @@ use ra_syntax::{
|
||||
|
||||
use crate::{Def, DefId, FnScopes, Module, Function, Path, db::HirDatabase};
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub enum Ty {
|
||||
/// The primitive boolean type. Written as `bool`.
|
||||
Bool,
|
||||
@ -35,8 +35,15 @@ pub enum Ty {
|
||||
/// A primitive floating-point type. For example, `f64`.
|
||||
Float(primitive::FloatTy),
|
||||
|
||||
// Structures, enumerations and unions.
|
||||
// Adt(AdtDef, Substs),
|
||||
/// Structures, enumerations and unions.
|
||||
Adt {
|
||||
/// The DefId of the struct/enum.
|
||||
def_id: DefId,
|
||||
/// The name, for displaying.
|
||||
name: SmolStr,
|
||||
// later we'll need generic substitutions here
|
||||
},
|
||||
|
||||
/// The pointee of a string slice. Written as `str`.
|
||||
Str,
|
||||
|
||||
@ -107,45 +114,48 @@ pub enum Ty {
|
||||
|
||||
type TyRef = Arc<Ty>;
|
||||
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
|
||||
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
|
||||
pub struct FnSig {
|
||||
input: Vec<Ty>,
|
||||
output: Ty,
|
||||
}
|
||||
|
||||
impl Ty {
|
||||
pub fn new(_db: &impl HirDatabase, node: ast::TypeRef) -> Cancelable<Self> {
|
||||
pub(crate) fn new(
|
||||
db: &impl HirDatabase,
|
||||
module: &Module,
|
||||
node: ast::TypeRef,
|
||||
) -> Cancelable<Self> {
|
||||
use ra_syntax::ast::TypeRef::*;
|
||||
Ok(match node {
|
||||
ParenType(_inner) => Ty::Unknown, // TODO
|
||||
TupleType(_inner) => Ty::Unknown, // TODO
|
||||
NeverType(..) => Ty::Never,
|
||||
PathType(inner) => {
|
||||
let path = if let Some(p) = inner.path() {
|
||||
let path = if let Some(p) = inner.path().and_then(Path::from_ast) {
|
||||
p
|
||||
} else {
|
||||
return Ok(Ty::Unknown);
|
||||
};
|
||||
if path.qualifier().is_none() {
|
||||
let name = path
|
||||
.segment()
|
||||
.and_then(|s| s.name_ref())
|
||||
.map(|n| n.text())
|
||||
.unwrap_or(SmolStr::new(""));
|
||||
if path.is_ident() {
|
||||
let name = &path.segments[0];
|
||||
if let Some(int_ty) = primitive::IntTy::from_string(&name) {
|
||||
Ty::Int(int_ty)
|
||||
return Ok(Ty::Int(int_ty));
|
||||
} else if let Some(uint_ty) = primitive::UintTy::from_string(&name) {
|
||||
Ty::Uint(uint_ty)
|
||||
return Ok(Ty::Uint(uint_ty));
|
||||
} else if let Some(float_ty) = primitive::FloatTy::from_string(&name) {
|
||||
Ty::Float(float_ty)
|
||||
} else {
|
||||
// TODO
|
||||
Ty::Unknown
|
||||
return Ok(Ty::Float(float_ty));
|
||||
}
|
||||
} else {
|
||||
// TODO
|
||||
Ty::Unknown
|
||||
}
|
||||
|
||||
// Resolve in module (in type namespace)
|
||||
let resolved = if let Some(r) = module.resolve_path(db, path)? {
|
||||
r
|
||||
} else {
|
||||
return Ok(Ty::Unknown);
|
||||
};
|
||||
let ty = db.type_for_def(resolved)?;
|
||||
ty
|
||||
}
|
||||
PointerType(_inner) => Ty::Unknown, // TODO
|
||||
ArrayType(_inner) => Ty::Unknown, // TODO
|
||||
@ -189,6 +199,7 @@ impl fmt::Display for Ty {
|
||||
}
|
||||
write!(f, ") -> {}", sig.output)
|
||||
}
|
||||
Ty::Adt { name, .. } => write!(f, "{}", name),
|
||||
Ty::Unknown => write!(f, "[unknown]"),
|
||||
}
|
||||
}
|
||||
@ -196,6 +207,7 @@ impl fmt::Display for Ty {
|
||||
|
||||
pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
|
||||
let syntax = f.syntax(db);
|
||||
let module = f.module(db)?;
|
||||
let node = syntax.borrowed();
|
||||
// TODO we ignore type parameters for now
|
||||
let input = node
|
||||
@ -204,7 +216,7 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
|
||||
pl.params()
|
||||
.map(|p| {
|
||||
p.type_ref()
|
||||
.map(|t| Ty::new(db, t))
|
||||
.map(|t| Ty::new(db, &module, t))
|
||||
.unwrap_or(Ok(Ty::Unknown))
|
||||
})
|
||||
.collect()
|
||||
@ -213,7 +225,7 @@ pub fn type_for_fn(db: &impl HirDatabase, f: Function) -> Cancelable<Ty> {
|
||||
let output = node
|
||||
.ret_type()
|
||||
.and_then(|rt| rt.type_ref())
|
||||
.map(|t| Ty::new(db, t))
|
||||
.map(|t| Ty::new(db, &module, t))
|
||||
.unwrap_or(Ok(Ty::Unknown))?;
|
||||
let sig = FnSig { input, output };
|
||||
Ok(Ty::FnPtr(Arc::new(sig)))
|
||||
@ -232,6 +244,14 @@ pub fn type_for_def(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Ty> {
|
||||
Ok(Ty::Unknown)
|
||||
}
|
||||
Def::Function(f) => type_for_fn(db, f),
|
||||
Def::Struct(s) => Ok(Ty::Adt {
|
||||
def_id,
|
||||
name: s.name(db)?,
|
||||
}),
|
||||
Def::Enum(e) => Ok(Ty::Adt {
|
||||
def_id,
|
||||
name: e.name(db)?,
|
||||
}),
|
||||
Def::Item => {
|
||||
log::debug!("trying to get type for item of unknown type {:?}", def_id);
|
||||
Ok(Ty::Unknown)
|
||||
@ -492,7 +512,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
};
|
||||
let cast_ty = e
|
||||
.type_ref()
|
||||
.map(|t| Ty::new(self.db, t))
|
||||
.map(|t| Ty::new(self.db, &self.module, t))
|
||||
.unwrap_or(Ok(Ty::Unknown))?;
|
||||
// TODO do the coercion...
|
||||
cast_ty
|
||||
@ -526,7 +546,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||
match stmt {
|
||||
ast::Stmt::LetStmt(stmt) => {
|
||||
let decl_ty = if let Some(type_ref) = stmt.type_ref() {
|
||||
Ty::new(self.db, type_ref)?
|
||||
Ty::new(self.db, &self.module, type_ref)?
|
||||
} else {
|
||||
Ty::Unknown
|
||||
};
|
||||
@ -576,7 +596,7 @@ pub fn infer(db: &impl HirDatabase, function: Function) -> Cancelable<InferenceR
|
||||
continue;
|
||||
};
|
||||
if let Some(type_ref) = param.type_ref() {
|
||||
let ty = Ty::new(db, type_ref)?;
|
||||
let ty = Ty::new(db, &ctx.module, type_ref)?;
|
||||
ctx.type_of.insert(LocalSyntaxPtr::new(pat.syntax()), ty);
|
||||
} else {
|
||||
// TODO self param
|
||||
|
@ -68,6 +68,29 @@ fn test() {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn infer_struct() {
|
||||
check_inference(
|
||||
r#"
|
||||
struct A {
|
||||
b: B,
|
||||
c: C,
|
||||
}
|
||||
struct B;
|
||||
struct C(usize);
|
||||
|
||||
fn test() {
|
||||
let c = C(1);
|
||||
B;
|
||||
let a: A = A { b: B, c: C() };
|
||||
a.b;
|
||||
a.c;
|
||||
}
|
||||
"#,
|
||||
"0004_struct.txt",
|
||||
);
|
||||
}
|
||||
|
||||
fn infer(content: &str) -> String {
|
||||
let (db, _, file_id) = MockDatabase::with_single_file(content);
|
||||
let source_file = db.source_file(file_id);
|
||||
|
10
crates/ra_hir/src/ty/tests/data/0004_struct.txt
Normal file
10
crates/ra_hir/src/ty/tests/data/0004_struct.txt
Normal file
@ -0,0 +1,10 @@
|
||||
[86; 90) 'C(1)': [unknown]
|
||||
[72; 153) '{ ...a.c; }': ()
|
||||
[86; 87) 'C': C
|
||||
[107; 108) 'a': A
|
||||
[114; 132) 'A { b:... C() }': [unknown]
|
||||
[138; 141) 'a.b': [unknown]
|
||||
[147; 150) 'a.c': [unknown]
|
||||
[96; 97) 'B': B
|
||||
[88; 89) '1': [unknown]
|
||||
[82; 83) 'c': [unknown]
|
Loading…
x
Reference in New Issue
Block a user