Add LifetimeParam resolving to Semantics

This commit is contained in:
Lukas Wirth 2020-12-13 22:13:16 +01:00
parent dbd0cfba53
commit c6172f3f6d
8 changed files with 131 additions and 39 deletions

View file

@ -1250,6 +1250,14 @@ impl LifetimeParam {
let params = db.generic_params(self.id.parent); let params = db.generic_params(self.id.parent);
params.lifetimes[self.id.local_id].name.clone() params.lifetimes[self.id.local_id].name.clone()
} }
pub fn module(self, db: &dyn HirDatabase) -> Module {
self.id.parent.module(db.upcast()).into()
}
pub fn parent(self, _db: &dyn HirDatabase) -> GenericDef {
self.id.parent.into()
}
} }
// FIXME: rename from `ImplDef` to `Impl` // FIXME: rename from `ImplDef` to `Impl`

View file

@ -41,6 +41,7 @@ from_id![
(hir_def::FunctionId, crate::Function), (hir_def::FunctionId, crate::Function),
(hir_def::ImplId, crate::ImplDef), (hir_def::ImplId, crate::ImplDef),
(hir_def::TypeParamId, crate::TypeParam), (hir_def::TypeParamId, crate::TypeParam),
(hir_def::LifetimeParamId, crate::LifetimeParam),
(hir_expand::MacroDefId, crate::MacroDef) (hir_expand::MacroDefId, crate::MacroDef)
]; ];
@ -154,6 +155,22 @@ impl From<GenericDef> for GenericDefId {
} }
} }
impl From<GenericDefId> for GenericDef {
fn from(def: GenericDefId) -> Self {
match def {
GenericDefId::FunctionId(it) => GenericDef::Function(it.into()),
GenericDefId::AdtId(it) => GenericDef::Adt(it.into()),
GenericDefId::TraitId(it) => GenericDef::Trait(it.into()),
GenericDefId::TypeAliasId(it) => GenericDef::TypeAlias(it.into()),
GenericDefId::ImplId(it) => GenericDef::ImplDef(it.into()),
GenericDefId::EnumVariantId(it) => {
GenericDef::EnumVariant(EnumVariant { parent: it.parent.into(), id: it.local_id })
}
GenericDefId::ConstId(it) => GenericDef::Const(it.into()),
}
}
}
impl From<Adt> for GenericDefId { impl From<Adt> for GenericDefId {
fn from(id: Adt) -> Self { fn from(id: Adt) -> Self {
match id { match id {

View file

@ -10,8 +10,8 @@ use hir_expand::InFile;
use syntax::ast; use syntax::ast;
use crate::{ use crate::{
db::HirDatabase, Const, Enum, EnumVariant, Field, FieldSource, Function, ImplDef, MacroDef, db::HirDatabase, Const, Enum, EnumVariant, Field, FieldSource, Function, ImplDef,
Module, Static, Struct, Trait, TypeAlias, TypeParam, Union, LifetimeParam, MacroDef, Module, Static, Struct, Trait, TypeAlias, TypeParam, Union,
}; };
pub trait HasSource { pub trait HasSource {
@ -129,6 +129,14 @@ impl HasSource for TypeParam {
type Ast = Either<ast::Trait, ast::TypeParam>; type Ast = Either<ast::Trait, ast::TypeParam>;
fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> { fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> {
let child_source = self.id.parent.child_source(db.upcast()); let child_source = self.id.parent.child_source(db.upcast());
child_source.map(|it| it[self.id.local_id].clone()) child_source.map(|it| it.type_params[self.id.local_id].clone())
}
}
impl HasSource for LifetimeParam {
type Ast = ast::LifetimeParam;
fn source(self, db: &dyn HirDatabase) -> InFile<Self::Ast> {
let child_source = self.id.parent.child_source(db.upcast());
child_source.map(|it| it.lifetime_params[self.id.local_id].clone())
} }
} }

View file

@ -13,7 +13,11 @@ use hir_expand::{hygiene::Hygiene, name::AsName, ExpansionInfo};
use hir_ty::associated_type_shorthand_candidates; use hir_ty::associated_type_shorthand_candidates;
use itertools::Itertools; use itertools::Itertools;
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
use syntax::{algo::find_node_at_offset, ast, AstNode, SyntaxNode, SyntaxToken, TextSize}; use syntax::{
algo::find_node_at_offset,
ast::{self, GenericParamsOwner},
match_ast, AstNode, SyntaxNode, SyntaxToken, TextSize,
};
use crate::{ use crate::{
code_model::Access, code_model::Access,
@ -21,8 +25,9 @@ use crate::{
diagnostics::Diagnostic, diagnostics::Diagnostic,
semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx}, semantics::source_to_def::{ChildContainer, SourceToDefCache, SourceToDefCtx},
source_analyzer::{resolve_hir_path, SourceAnalyzer}, source_analyzer::{resolve_hir_path, SourceAnalyzer},
AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, Local, MacroDef, AssocItem, Callable, Crate, Field, Function, HirFileId, ImplDef, InFile, LifetimeParam, Local,
Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam, VariantDef, MacroDef, Module, ModuleDef, Name, Path, ScopeDef, Trait, Type, TypeAlias, TypeParam,
VariantDef,
}; };
#[derive(Debug, Clone, PartialEq, Eq)] #[derive(Debug, Clone, PartialEq, Eq)]
@ -173,6 +178,11 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
self.imp.descend_node_at_offset(node, offset).find_map(N::cast) self.imp.descend_node_at_offset(node, offset).find_map(N::cast)
} }
// FIXME: Replace the SyntaxToken with a typed ast Node/Token
pub fn resolve_lifetime_param(&self, lifetime_token: &SyntaxToken) -> Option<LifetimeParam> {
self.imp.resolve_lifetime_param(lifetime_token)
}
pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { pub fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
self.imp.type_of_expr(expr) self.imp.type_of_expr(expr)
} }
@ -392,16 +402,44 @@ impl<'db> SemanticsImpl<'db> {
.kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len()) .kmerge_by(|node1, node2| node1.text_range().len() < node2.text_range().len())
} }
// FIXME: Replace the SyntaxToken with a typed ast Node/Token
fn resolve_lifetime_param(&self, lifetime_token: &SyntaxToken) -> Option<LifetimeParam> {
if lifetime_token.kind() != syntax::SyntaxKind::LIFETIME {
return None;
}
let lifetime_text = lifetime_token.text();
let lifetime_param = lifetime_token.parent().ancestors().find_map(|syn| {
let gpl = match_ast! {
match syn {
ast::Fn(it) => it.generic_param_list()?,
ast::TypeAlias(it) => it.generic_param_list()?,
ast::Struct(it) => it.generic_param_list()?,
ast::Enum(it) => it.generic_param_list()?,
ast::Union(it) => it.generic_param_list()?,
ast::Trait(it) => it.generic_param_list()?,
ast::Impl(it) => it.generic_param_list()?,
ast::WherePred(it) => it.generic_param_list()?,
ast::ForType(it) => it.generic_param_list()?,
_ => return None,
}
};
gpl.lifetime_params()
.find(|tp| tp.lifetime_token().as_ref().map(|lt| lt.text()) == Some(lifetime_text))
})?;
let src = self.find_file(lifetime_param.syntax().clone()).with_value(lifetime_param);
ToDef::to_def(self, src)
}
fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> { fn type_of_expr(&self, expr: &ast::Expr) -> Option<Type> {
self.analyze(expr.syntax()).type_of_expr(self.db, &expr) self.analyze(expr.syntax()).type_of_expr(self.db, expr)
} }
fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> { fn type_of_pat(&self, pat: &ast::Pat) -> Option<Type> {
self.analyze(pat.syntax()).type_of_pat(self.db, &pat) self.analyze(pat.syntax()).type_of_pat(self.db, pat)
} }
fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> { fn type_of_self(&self, param: &ast::SelfParam) -> Option<Type> {
self.analyze(param.syntax()).type_of_self(self.db, &param) self.analyze(param.syntax()).type_of_self(self.db, param)
} }
fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> { fn resolve_method_call(&self, call: &ast::MethodCallExpr) -> Option<FunctionId> {
@ -684,6 +722,7 @@ to_def_impls![
(crate::Field, ast::TupleField, tuple_field_to_def), (crate::Field, ast::TupleField, tuple_field_to_def),
(crate::EnumVariant, ast::Variant, enum_variant_to_def), (crate::EnumVariant, ast::Variant, enum_variant_to_def),
(crate::TypeParam, ast::TypeParam, type_param_to_def), (crate::TypeParam, ast::TypeParam, type_param_to_def),
(crate::LifetimeParam, ast::LifetimeParam, lifetime_param_to_def),
(crate::MacroDef, ast::MacroCall, macro_call_to_def), // this one is dubious, not all calls are macros (crate::MacroDef, ast::MacroCall, macro_call_to_def), // this one is dubious, not all calls are macros
(crate::Local, ast::IdentPat, bind_pat_to_def), (crate::Local, ast::IdentPat, bind_pat_to_def),
]; ];

View file

@ -7,7 +7,8 @@ use hir_def::{
expr::PatId, expr::PatId,
keys::{self, Key}, keys::{self, Key},
ConstId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId, ImplId, ConstId, DefWithBodyId, EnumId, EnumVariantId, FieldId, FunctionId, GenericDefId, ImplId,
ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId, VariantId, LifetimeParamId, ModuleId, StaticId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId,
VariantId,
}; };
use hir_expand::{name::AsName, AstId, MacroDefKind}; use hir_expand::{name::AsName, AstId, MacroDefKind};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
@ -128,13 +129,25 @@ impl SourceToDefCtx<'_, '_> {
pub(super) fn type_param_to_def(&mut self, src: InFile<ast::TypeParam>) -> Option<TypeParamId> { pub(super) fn type_param_to_def(&mut self, src: InFile<ast::TypeParam>) -> Option<TypeParamId> {
let container: ChildContainer = let container: ChildContainer =
self.find_type_param_container(src.as_ref().map(|it| it.syntax()))?.into(); self.find_generic_param_container(src.as_ref().map(|it| it.syntax()))?.into();
let db = self.db; let db = self.db;
let dyn_map = let dyn_map =
&*self.cache.entry(container).or_insert_with(|| container.child_by_source(db)); &*self.cache.entry(container).or_insert_with(|| container.child_by_source(db));
dyn_map[keys::TYPE_PARAM].get(&src).copied() dyn_map[keys::TYPE_PARAM].get(&src).copied()
} }
pub(super) fn lifetime_param_to_def(
&mut self,
src: InFile<ast::LifetimeParam>,
) -> Option<LifetimeParamId> {
let container: ChildContainer =
self.find_generic_param_container(src.as_ref().map(|it| it.syntax()))?.into();
let db = self.db;
let dyn_map =
&*self.cache.entry(container).or_insert_with(|| container.child_by_source(db));
dyn_map[keys::LIFETIME_PARAM].get(&src).copied()
}
// FIXME: use DynMap as well? // FIXME: use DynMap as well?
pub(super) fn macro_call_to_def(&mut self, src: InFile<ast::MacroCall>) -> Option<MacroDefId> { pub(super) fn macro_call_to_def(&mut self, src: InFile<ast::MacroCall>) -> Option<MacroDefId> {
let kind = MacroDefKind::Declarative; let kind = MacroDefKind::Declarative;
@ -203,7 +216,7 @@ impl SourceToDefCtx<'_, '_> {
Some(def.into()) Some(def.into())
} }
fn find_type_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> { fn find_generic_param_container(&mut self, src: InFile<&SyntaxNode>) -> Option<GenericDefId> {
for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) { for container in src.cloned().ancestors_with_macros(self.db.upcast()).skip(1) {
let res: GenericDefId = match_ast! { let res: GenericDefId = match_ast! {
match (container.value) { match (container.value) {
@ -247,7 +260,7 @@ pub(crate) enum ChildContainer {
VariantId(VariantId), VariantId(VariantId),
TypeAliasId(TypeAliasId), TypeAliasId(TypeAliasId),
/// XXX: this might be the same def as, for example an `EnumId`. However, /// XXX: this might be the same def as, for example an `EnumId`. However,
/// here the children generic parameters, and not, eg enum variants. /// here the children are generic parameters, and not, eg enum variants.
GenericDefId(GenericDefId), GenericDefId(GenericDefId),
} }
impl_from! { impl_from! {

View file

@ -19,10 +19,10 @@ use crate::{
db::DefDatabase, db::DefDatabase,
dyn_map::DynMap, dyn_map::DynMap,
keys, keys,
src::HasChildSource,
src::HasSource, src::HasSource,
type_ref::{LifetimeRef, TypeBound, TypeRef}, type_ref::{LifetimeRef, TypeBound, TypeRef},
AdtId, GenericDefId, LocalTypeParamId, Lookup, TypeParamId, AdtId, GenericDefId, LifetimeParamId, LocalLifetimeParamId, LocalTypeParamId, Lookup,
TypeParamId,
}; };
/// Data about a generic parameter (to a function, struct, impl, ...). /// Data about a generic parameter (to a function, struct, impl, ...).
@ -72,7 +72,11 @@ pub enum WherePredicateTypeTarget {
// FIXME: ForLifetime(Vec<LifetimeParamId>, TypeRef) // FIXME: ForLifetime(Vec<LifetimeParamId>, TypeRef)
} }
type SourceMap = ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>; #[derive(Default)]
pub struct SourceMaps {
pub type_params: ArenaMap<LocalTypeParamId, Either<ast::Trait, ast::TypeParam>>,
pub lifetime_params: ArenaMap<LocalLifetimeParamId, ast::LifetimeParam>,
}
impl GenericParams { impl GenericParams {
pub(crate) fn generic_params_query( pub(crate) fn generic_params_query(
@ -129,9 +133,9 @@ impl GenericParams {
Arc::new(generics) Arc::new(generics)
} }
fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMap>) { fn new(db: &dyn DefDatabase, def: GenericDefId) -> (GenericParams, InFile<SourceMaps>) {
let mut generics = GenericParams::default(); let mut generics = GenericParams::default();
let mut sm = ArenaMap::default(); let mut sm = SourceMaps::default();
// FIXME: add `: Sized` bound for everything except for `Self` in traits // FIXME: add `: Sized` bound for everything except for `Self` in traits
let file_id = match def { let file_id = match def {
@ -174,7 +178,7 @@ impl GenericParams {
default: None, default: None,
provenance: TypeParamProvenance::TraitSelf, provenance: TypeParamProvenance::TraitSelf,
}); });
sm.insert(self_param_id, Either::Left(src.value.clone())); sm.type_params.insert(self_param_id, Either::Left(src.value.clone()));
// add super traits as bounds on Self // add super traits as bounds on Self
// i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
let self_param = TypeRef::Path(name![Self].into()); let self_param = TypeRef::Path(name![Self].into());
@ -210,7 +214,7 @@ impl GenericParams {
pub(crate) fn fill( pub(crate) fn fill(
&mut self, &mut self,
lower_ctx: &LowerCtx, lower_ctx: &LowerCtx,
sm: &mut SourceMap, sm: &mut SourceMaps,
node: &dyn GenericParamsOwner, node: &dyn GenericParamsOwner,
) { ) {
if let Some(params) = node.generic_param_list() { if let Some(params) = node.generic_param_list() {
@ -237,7 +241,7 @@ impl GenericParams {
fn fill_params( fn fill_params(
&mut self, &mut self,
lower_ctx: &LowerCtx, lower_ctx: &LowerCtx,
sm: &mut SourceMap, sm: &mut SourceMaps,
params: ast::GenericParamList, params: ast::GenericParamList,
) { ) {
for type_param in params.type_params() { for type_param in params.type_params() {
@ -250,7 +254,7 @@ impl GenericParams {
provenance: TypeParamProvenance::TypeParamList, provenance: TypeParamProvenance::TypeParamList,
}; };
let param_id = self.types.alloc(param); let param_id = self.types.alloc(param);
sm.insert(param_id, Either::Right(type_param.clone())); sm.type_params.insert(param_id, Either::Right(type_param.clone()));
let type_ref = TypeRef::Path(name.into()); let type_ref = TypeRef::Path(name.into());
self.fill_bounds(&lower_ctx, &type_param, Either::Left(type_ref)); self.fill_bounds(&lower_ctx, &type_param, Either::Left(type_ref));
@ -260,7 +264,8 @@ impl GenericParams {
.lifetime_token() .lifetime_token()
.map_or_else(Name::missing, |tok| Name::new_lifetime(&tok)); .map_or_else(Name::missing, |tok| Name::new_lifetime(&tok));
let param = LifetimeParamData { name: name.clone() }; let param = LifetimeParamData { name: name.clone() };
let _param_id = self.lifetimes.alloc(param); let param_id = self.lifetimes.alloc(param);
sm.lifetime_params.insert(param_id, lifetime_param.clone());
let lifetime_ref = LifetimeRef::new_name(name); let lifetime_ref = LifetimeRef::new_name(name);
self.fill_bounds(&lower_ctx, &lifetime_param, Either::Right(lifetime_ref)); self.fill_bounds(&lower_ctx, &lifetime_param, Either::Right(lifetime_ref));
} }
@ -340,27 +345,29 @@ impl GenericParams {
}) })
} }
} }
impl GenericDefId {
impl HasChildSource for GenericDefId { // FIXME: Change HasChildSource's ChildId AssocItem to be a generic parameter instead
type ChildId = LocalTypeParamId; pub fn child_source(&self, db: &dyn DefDatabase) -> InFile<SourceMaps> {
type Value = Either<ast::Trait, ast::TypeParam>; GenericParams::new(db, *self).1
fn child_source(&self, db: &dyn DefDatabase) -> InFile<SourceMap> {
let (_, sm) = GenericParams::new(db, *self);
sm
} }
} }
impl ChildBySource for GenericDefId { impl ChildBySource for GenericDefId {
fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap { fn child_by_source(&self, db: &dyn DefDatabase) -> DynMap {
let mut res = DynMap::default(); let mut res = DynMap::default();
let arena_map = self.child_source(db); let (_, sm) = GenericParams::new(db, *self);
let arena_map = arena_map.as_ref();
for (local_id, src) in arena_map.value.iter() { let sm = sm.as_ref();
for (local_id, src) in sm.value.type_params.iter() {
let id = TypeParamId { parent: *self, local_id }; let id = TypeParamId { parent: *self, local_id };
if let Either::Right(type_param) = src { if let Either::Right(type_param) = src {
res[keys::TYPE_PARAM].insert(arena_map.with_value(type_param.clone()), id) res[keys::TYPE_PARAM].insert(sm.with_value(type_param.clone()), id)
} }
} }
for (local_id, src) in sm.value.lifetime_params.iter() {
let id = LifetimeParamId { parent: *self, local_id };
res[keys::LIFETIME_PARAM].insert(sm.with_value(src.clone()), id);
}
res res
} }
} }

View file

@ -2,7 +2,6 @@
use std::{collections::hash_map::Entry, mem, sync::Arc}; use std::{collections::hash_map::Entry, mem, sync::Arc};
use arena::map::ArenaMap;
use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, name::known, HirFileId}; use hir_expand::{ast_id_map::AstIdMap, hygiene::Hygiene, name::known, HirFileId};
use smallvec::SmallVec; use smallvec::SmallVec;
use syntax::{ use syntax::{
@ -607,7 +606,7 @@ impl Ctx {
owner: GenericsOwner<'_>, owner: GenericsOwner<'_>,
node: &impl ast::GenericParamsOwner, node: &impl ast::GenericParamsOwner,
) -> GenericParamsId { ) -> GenericParamsId {
let mut sm = &mut ArenaMap::default(); let mut sm = &mut Default::default();
let mut generics = GenericParams::default(); let mut generics = GenericParams::default();
match owner { match owner {
GenericsOwner::Function(func) => { GenericsOwner::Function(func) => {
@ -630,7 +629,7 @@ impl Ctx {
default: None, default: None,
provenance: TypeParamProvenance::TraitSelf, provenance: TypeParamProvenance::TraitSelf,
}); });
sm.insert(self_param_id, Either::Left(trait_def.clone())); sm.type_params.insert(self_param_id, Either::Left(trait_def.clone()));
// add super traits as bounds on Self // add super traits as bounds on Self
// i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar // i.e., trait Foo: Bar is equivalent to trait Foo where Self: Bar
let self_param = TypeRef::Path(name![Self].into()); let self_param = TypeRef::Path(name![Self].into());

View file

@ -8,8 +8,8 @@ use syntax::{ast, AstNode, AstPtr};
use crate::{ use crate::{
dyn_map::{DynMap, Policy}, dyn_map::{DynMap, Policy},
ConstId, EnumId, EnumVariantId, FieldId, FunctionId, ImplId, StaticId, StructId, TraitId, ConstId, EnumId, EnumVariantId, FieldId, FunctionId, ImplId, LifetimeParamId, StaticId,
TypeAliasId, TypeParamId, UnionId, StructId, TraitId, TypeAliasId, TypeParamId, UnionId,
}; };
pub type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>; pub type Key<K, V> = crate::dyn_map::Key<InFile<K>, V, AstPtrPolicy<K, V>>;
@ -28,6 +28,7 @@ pub const VARIANT: Key<ast::Variant, EnumVariantId> = Key::new();
pub const TUPLE_FIELD: Key<ast::TupleField, FieldId> = Key::new(); pub const TUPLE_FIELD: Key<ast::TupleField, FieldId> = Key::new();
pub const RECORD_FIELD: Key<ast::RecordField, FieldId> = Key::new(); pub const RECORD_FIELD: Key<ast::RecordField, FieldId> = Key::new();
pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new(); pub const TYPE_PARAM: Key<ast::TypeParam, TypeParamId> = Key::new();
pub const LIFETIME_PARAM: Key<ast::LifetimeParam, LifetimeParamId> = Key::new();
pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new(); pub const MACRO: Key<ast::MacroCall, MacroDefId> = Key::new();