Allow hir::Param to refer to other entity params aside from functions

This commit is contained in:
Lukas Wirth 2024-05-18 12:35:55 +02:00
parent cd9e90cc71
commit 7c6f31a45b
9 changed files with 114 additions and 118 deletions

View file

@ -35,7 +35,7 @@ pub mod term_search;
mod display;
use std::{iter, mem::discriminant, ops::ControlFlow};
use std::{mem::discriminant, ops::ControlFlow};
use arrayvec::ArrayVec;
use base_db::{CrateDisplayName, CrateId, CrateOrigin, FileId};
@ -52,7 +52,6 @@ use hir_def::{
path::ImportAlias,
per_ns::PerNs,
resolver::{HasResolver, Resolver},
src::HasSource as _,
AssocItemId, AssocItemLoc, AttrDefId, ConstId, ConstParamId, CrateRootModuleId, DefWithBodyId,
EnumId, EnumVariantId, ExternCrateId, FunctionId, GenericDefId, GenericParamId, HasModule,
ImplId, InTypeConstId, ItemContainerId, LifetimeParamId, LocalFieldId, Lookup, MacroExpander,
@ -1965,7 +1964,7 @@ impl Function {
.enumerate()
.map(|(idx, ty)| {
let ty = Type { env: environment.clone(), ty: ty.clone() };
Param { func: self, ty, idx }
Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx }
})
.collect()
}
@ -1991,7 +1990,7 @@ impl Function {
.skip(skip)
.map(|(idx, ty)| {
let ty = Type { env: environment.clone(), ty: ty.clone() };
Param { func: self, ty, idx }
Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx }
})
.collect()
}
@ -2037,7 +2036,7 @@ impl Function {
.skip(skip)
.map(|(idx, ty)| {
let ty = Type { env: environment.clone(), ty: ty.clone() };
Param { func: self, ty, idx }
Param { func: Callee::Def(CallableDefId::FunctionId(self.id)), ty, idx }
})
.collect()
}
@ -2167,17 +2166,24 @@ impl From<hir_ty::Mutability> for Access {
#[derive(Clone, PartialEq, Eq, Hash, Debug)]
pub struct Param {
func: Function,
func: Callee,
/// The index in parameter list, including self parameter.
idx: usize,
ty: Type,
}
impl Param {
pub fn parent_fn(&self) -> Function {
self.func
pub fn parent_fn(&self) -> Option<Function> {
match self.func {
Callee::Def(CallableDefId::FunctionId(f)) => Some(f.into()),
_ => None,
}
}
// pub fn parent_closure(&self) -> Option<Closure> {
// self.func.as_ref().right().cloned()
// }
pub fn index(&self) -> usize {
self.idx
}
@ -2191,7 +2197,11 @@ impl Param {
}
pub fn as_local(&self, db: &dyn HirDatabase) -> Option<Local> {
let parent = DefWithBodyId::FunctionId(self.func.into());
let parent = match self.func {
Callee::Def(CallableDefId::FunctionId(it)) => DefWithBodyId::FunctionId(it),
Callee::Closure(closure) => db.lookup_intern_closure(closure.into()).0,
_ => return None,
};
let body = db.body(parent);
if let Some(self_param) = body.self_param.filter(|_| self.idx == 0) {
Some(Local { parent, binding_id: self_param })
@ -2205,18 +2215,45 @@ impl Param {
}
pub fn pattern_source(&self, db: &dyn HirDatabase) -> Option<ast::Pat> {
self.source(db).and_then(|p| p.value.pat())
self.source(db).and_then(|p| p.value.right()?.pat())
}
pub fn source(&self, db: &dyn HirDatabase) -> Option<InFile<ast::Param>> {
let InFile { file_id, value } = self.func.source(db)?;
let params = value.param_list()?;
if params.self_param().is_some() {
params.params().nth(self.idx.checked_sub(params.self_param().is_some() as usize)?)
} else {
params.params().nth(self.idx)
pub fn source(
&self,
db: &dyn HirDatabase,
) -> Option<InFile<Either<ast::SelfParam, ast::Param>>> {
match self.func {
Callee::Def(CallableDefId::FunctionId(func)) => {
let InFile { file_id, value } = Function { id: func }.source(db)?;
let params = value.param_list()?;
if let Some(self_param) = params.self_param() {
if let Some(idx) = self.idx.checked_sub(1 as usize) {
params.params().nth(idx).map(Either::Right)
} else {
Some(Either::Left(self_param))
}
} else {
params.params().nth(self.idx).map(Either::Right)
}
.map(|value| InFile { file_id, value })
}
Callee::Closure(closure) => {
let InternedClosure(owner, expr_id) = db.lookup_intern_closure(closure.into());
let (_, source_map) = db.body_with_source_map(owner);
let ast @ InFile { file_id, value } = source_map.expr_syntax(expr_id).ok()?;
let root = db.parse_or_expand(file_id);
match value.to_node(&root) {
ast::Expr::ClosureExpr(it) => it
.param_list()?
.params()
.nth(self.idx)
.map(Either::Right)
.map(|value| InFile { file_id: ast.file_id, value }),
_ => None,
}
}
_ => None,
}
.map(|value| InFile { file_id, value })
}
}
@ -4919,7 +4956,7 @@ pub struct Callable {
pub(crate) is_bound_method: bool,
}
#[derive(Debug)]
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
enum Callee {
Def(CallableDefId),
Closure(ClosureId),
@ -4960,43 +4997,15 @@ impl Callable {
pub fn n_params(&self) -> usize {
self.sig.params().len() - if self.is_bound_method { 1 } else { 0 }
}
pub fn params(
&self,
db: &dyn HirDatabase,
) -> Vec<(Option<Either<ast::SelfParam, ast::Pat>>, Type)> {
let types = self
.sig
pub fn params(&self) -> Vec<Param> {
self.sig
.params()
.iter()
.enumerate()
.skip(if self.is_bound_method { 1 } else { 0 })
.map(|ty| self.ty.derived(ty.clone()));
let map_param = |it: ast::Param| it.pat().map(Either::Right);
let patterns = match self.callee {
Callee::Def(CallableDefId::FunctionId(func)) => {
let src = func.lookup(db.upcast()).source(db.upcast());
src.value.param_list().map(|param_list| {
param_list
.self_param()
.map(|it| Some(Either::Left(it)))
.filter(|_| !self.is_bound_method)
.into_iter()
.chain(param_list.params().map(map_param))
})
}
Callee::Closure(closure_id) => match closure_source(db, closure_id) {
Some(src) => src.param_list().map(|param_list| {
param_list
.self_param()
.map(|it| Some(Either::Left(it)))
.filter(|_| !self.is_bound_method)
.into_iter()
.chain(param_list.params().map(map_param))
}),
None => None,
},
_ => None,
};
patterns.into_iter().flatten().chain(iter::repeat(None)).zip(types).collect()
.map(|(idx, ty)| (idx, self.ty.derived(ty.clone())))
.map(|(idx, ty)| Param { func: self.callee, idx, ty })
.collect()
}
pub fn return_type(&self) -> Type {
self.ty.derived(self.sig.ret().clone())
@ -5006,18 +5015,6 @@ impl Callable {
}
}
fn closure_source(db: &dyn HirDatabase, closure: ClosureId) -> Option<ast::ClosureExpr> {
let InternedClosure(owner, expr_id) = db.lookup_intern_closure(closure.into());
let (_, source_map) = db.body_with_source_map(owner);
let ast = source_map.expr_syntax(expr_id).ok()?;
let root = ast.file_syntax(db.upcast());
let expr = ast.value.to_node(&root);
match expr {
ast::Expr::ClosureExpr(it) => Some(it),
_ => None,
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct Layout(Arc<TyLayout>, Arc<TargetDataLayout>);