Make FnScopes use hir::Expr

This was a bit complicated. I've added a wrapper type for now that does the
LocalSyntaxPtr <-> ExprId translation; we might want to get rid of that or give
it a nicer interface.
This commit is contained in:
Florian Diebold 2019-01-05 22:37:59 +01:00
parent 136aba1cf3
commit 8e3e5ab2c8
13 changed files with 533 additions and 313 deletions

View File

@ -15,7 +15,7 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) ->
None => return Ok(()),
};
if let Some(function) = &ctx.function {
let scopes = function.scopes(ctx.db);
let scopes = function.scopes(ctx.db)?;
complete_fn(acc, &scopes, ctx.offset);
}
@ -40,20 +40,17 @@ pub(super) fn complete_scope(acc: &mut Completions, ctx: &CompletionContext) ->
Ok(())
}
fn complete_fn(acc: &mut Completions, scopes: &hir::FnScopes, offset: TextUnit) {
fn complete_fn(acc: &mut Completions, scopes: &hir::ScopesWithSyntaxMapping, offset: TextUnit) {
let mut shadowed = FxHashSet::default();
scopes
.scope_chain_for_offset(offset)
.flat_map(|scope| scopes.entries(scope).iter())
.flat_map(|scope| scopes.scopes.entries(scope).iter())
.filter(|entry| shadowed.insert(entry.name()))
.for_each(|entry| {
CompletionItem::new(CompletionKind::Reference, entry.name().to_string())
.kind(CompletionItemKind::Binding)
.add_to(acc)
});
if scopes.self_param.is_some() {
CompletionItem::new(CompletionKind::Reference, "self").add_to(acc);
}
}
#[cfg(test)]

View File

@ -28,7 +28,7 @@ pub(crate) fn reference_defenition(
if let Some(fn_descr) =
hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())?
{
let scope = fn_descr.scopes(db);
let scope = fn_descr.scopes(db)?;
// First try to resolve the symbol locally
if let Some(entry) = scope.resolve_local_name(name_ref) {
let nav = NavigationTarget {

View File

@ -157,7 +157,7 @@ impl db::RootDatabase {
.collect::<Vec<_>>();
ret.extend(
descr
.scopes(self)
.scopes(self)?
.find_all_refs(binding)
.into_iter()
.map(|ref_desc| (position.file_id, ref_desc.range)),
@ -185,7 +185,7 @@ impl db::RootDatabase {
position.file_id,
name_ref.syntax(),
)?);
let scope = descr.scopes(db);
let scope = descr.scopes(db)?;
let resolved = ctry!(scope.resolve_local_name(name_ref));
let resolved = resolved.ptr().resolve(source_file);
let binding = ctry!(find_node_at_offset::<ast::BindPat>(

View File

@ -31,7 +31,7 @@ pub trait HirDatabase: SyntaxDatabase
use fn crate::macros::expand_macro_invocation;
}
fn fn_scopes(def_id: DefId) -> Arc<FnScopes> {
fn fn_scopes(def_id: DefId) -> Cancelable<Arc<FnScopes>> {
type FnScopesQuery;
use fn query_definitions::fn_scopes;
}

View File

@ -4,7 +4,7 @@ use rustc_hash::FxHashMap;
use ra_arena::{Arena, RawId, impl_arena_id};
use ra_db::{LocalSyntaxPtr, Cancelable};
use ra_syntax::ast::{self, AstNode, LoopBodyOwner, ArgListOwner};
use ra_syntax::ast::{self, AstNode, LoopBodyOwner, ArgListOwner, NameOwner};
use crate::{Path, type_ref::{Mutability, TypeRef}, Name, HirDatabase, DefId, Def, name::AsName};
@ -21,7 +21,7 @@ pub struct Body {
/// part of the function signature, the patterns are not (they don't change
/// the external type of the function).
///
/// If this `ExprTable` is for the body of a constant, this will just be
/// If this `Body` is for the body of a constant, this will just be
/// empty.
args: Vec<PatId>,
/// The `ExprId` of the actual body expression.
@ -43,6 +43,43 @@ pub struct BodySyntaxMapping {
pat_syntax_mapping_back: FxHashMap<PatId, LocalSyntaxPtr>,
}
impl Body {
pub fn expr(&self, expr: ExprId) -> &Expr {
&self.exprs[expr]
}
pub fn pat(&self, pat: PatId) -> &Pat {
&self.pats[pat]
}
pub fn args(&self) -> &[PatId] {
&self.args
}
pub fn body_expr(&self) -> ExprId {
self.body_expr
}
}
impl BodySyntaxMapping {
pub fn expr_syntax(&self, expr: ExprId) -> Option<LocalSyntaxPtr> {
self.expr_syntax_mapping_back.get(&expr).cloned()
}
pub fn syntax_expr(&self, ptr: LocalSyntaxPtr) -> Option<ExprId> {
self.expr_syntax_mapping.get(&ptr).cloned()
}
pub fn pat_syntax(&self, pat: PatId) -> Option<LocalSyntaxPtr> {
self.pat_syntax_mapping_back.get(&pat).cloned()
}
pub fn syntax_pat(&self, ptr: LocalSyntaxPtr) -> Option<PatId> {
self.pat_syntax_mapping.get(&ptr).cloned()
}
pub fn body(&self) -> &Arc<Body> {
&self.body
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum Expr {
/// This is produced if syntax tree does not have a required expression piece.
@ -113,21 +150,26 @@ pub enum Expr {
expr: ExprId,
op: Option<UnaryOp>,
},
Lambda {
args: Vec<PatId>,
arg_types: Vec<Option<TypeRef>>,
body: ExprId,
},
}
pub type UnaryOp = ast::PrefixOp;
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct MatchArm {
pats: Vec<PatId>,
pub pats: Vec<PatId>,
// guard: Option<ExprId>, // TODO
expr: ExprId,
pub expr: ExprId,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct StructLitField {
name: Name,
expr: ExprId,
pub name: Name,
pub expr: ExprId,
}
#[derive(Debug, Clone, Eq, PartialEq)]
@ -140,12 +182,118 @@ pub enum Statement {
Expr(ExprId),
}
impl Expr {
pub fn walk_child_exprs(&self, mut f: impl FnMut(ExprId)) {
match self {
Expr::Missing => {}
Expr::Path(_) => {}
Expr::If {
condition,
then_branch,
else_branch,
} => {
f(*condition);
f(*then_branch);
if let Some(else_branch) = else_branch {
f(*else_branch);
}
}
Expr::Block { statements, tail } => {
for stmt in statements {
match stmt {
Statement::Let { initializer, .. } => {
if let Some(expr) = initializer {
f(*expr);
}
}
Statement::Expr(e) => f(*e),
}
}
if let Some(expr) = tail {
f(*expr);
}
}
Expr::Loop { body } => f(*body),
Expr::While { condition, body } => {
f(*condition);
f(*body);
}
Expr::For { iterable, body, .. } => {
f(*iterable);
f(*body);
}
Expr::Call { callee, args } => {
f(*callee);
for arg in args {
f(*arg);
}
}
Expr::MethodCall { receiver, args, .. } => {
f(*receiver);
for arg in args {
f(*arg);
}
}
Expr::Match { expr, arms } => {
f(*expr);
for arm in arms {
f(arm.expr);
}
}
Expr::Continue => {}
Expr::Break { expr } | Expr::Return { expr } => {
if let Some(expr) = expr {
f(*expr);
}
}
Expr::StructLit { fields, spread, .. } => {
for field in fields {
f(field.expr);
}
if let Some(expr) = spread {
f(*expr);
}
}
Expr::Lambda { body, .. } => {
f(*body);
}
Expr::Field { expr, .. }
| Expr::Try { expr }
| Expr::Cast { expr, .. }
| Expr::Ref { expr, .. }
| Expr::UnaryOp { expr, .. } => {
f(*expr);
}
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct PatId(RawId);
impl_arena_id!(PatId);
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Pat;
pub enum Pat {
Missing,
Bind {
name: Name,
},
TupleStruct {
path: Option<Path>,
args: Vec<PatId>,
},
}
impl Pat {
pub fn walk_child_pats(&self, f: impl FnMut(PatId)) {
match self {
Pat::Missing | Pat::Bind { .. } => {}
Pat::TupleStruct { args, .. } => {
args.iter().map(|pat| *pat).for_each(f);
}
}
}
}
// Queries
@ -163,6 +311,17 @@ struct ExprCollector {
}
impl ExprCollector {
fn new() -> Self {
ExprCollector {
exprs: Arena::default(),
pats: Arena::default(),
expr_syntax_mapping: FxHashMap::default(),
expr_syntax_mapping_back: FxHashMap::default(),
pat_syntax_mapping: FxHashMap::default(),
pat_syntax_mapping_back: FxHashMap::default(),
}
}
fn alloc_expr(&mut self, expr: Expr, syntax_ptr: LocalSyntaxPtr) -> ExprId {
let id = self.exprs.alloc(expr);
self.expr_syntax_mapping.insert(syntax_ptr, id);
@ -177,30 +336,63 @@ impl ExprCollector {
id
}
fn empty_block(&mut self) -> ExprId {
let block = Expr::Block {
statements: Vec::new(),
tail: None,
};
self.exprs.alloc(block)
}
fn collect_expr(&mut self, expr: ast::Expr) -> ExprId {
let syntax_ptr = LocalSyntaxPtr::new(expr.syntax());
match expr {
ast::Expr::IfExpr(e) => {
let condition = if let Some(condition) = e.condition() {
if condition.pat().is_none() {
if let Some(pat) = e.condition().and_then(|c| c.pat()) {
// if let -- desugar to match
let pat = self.collect_pat(pat);
let match_expr =
self.collect_expr_opt(e.condition().expect("checked above").expr());
let then_branch = self.collect_block_opt(e.then_branch());
let else_branch = e
.else_branch()
.map(|e| self.collect_block(e))
.unwrap_or_else(|| self.empty_block());
let placeholder_pat = self.pats.alloc(Pat::Missing);
let arms = vec![
MatchArm {
pats: vec![pat],
expr: then_branch,
},
MatchArm {
pats: vec![placeholder_pat],
expr: else_branch,
},
];
self.alloc_expr(
Expr::Match {
expr: match_expr,
arms,
},
syntax_ptr,
)
} else {
let condition = if let Some(condition) = e.condition() {
self.collect_expr_opt(condition.expr())
} else {
// TODO handle if let
return self.alloc_expr(Expr::Missing, syntax_ptr);
}
} else {
self.exprs.alloc(Expr::Missing)
};
let then_branch = self.collect_block_opt(e.then_branch());
let else_branch = e.else_branch().map(|e| self.collect_block(e));
self.alloc_expr(
Expr::If {
condition,
then_branch,
else_branch,
},
syntax_ptr,
)
self.exprs.alloc(Expr::Missing)
};
let then_branch = self.collect_block_opt(e.then_branch());
let else_branch = e.else_branch().map(|e| self.collect_block(e));
self.alloc_expr(
Expr::If {
condition,
then_branch,
else_branch,
},
syntax_ptr,
)
}
}
ast::Expr::BlockExpr(e) => self.collect_block_opt(e.block()),
ast::Expr::LoopExpr(e) => {
@ -368,18 +560,30 @@ impl ExprCollector {
let op = e.op();
self.alloc_expr(Expr::UnaryOp { expr, op }, syntax_ptr)
}
// We should never get to these because they're handled in MatchExpr resp. StructLit:
ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => {
panic!("collect_expr called on {:?}", expr)
}
ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => {
panic!("collect_expr called on {:?}", expr)
ast::Expr::LambdaExpr(e) => {
let mut args = Vec::new();
let mut arg_types = Vec::new();
if let Some(pl) = e.param_list() {
for param in pl.params() {
let pat = self.collect_pat_opt(param.pat());
let type_ref = param.type_ref().map(TypeRef::from_ast);
args.push(pat);
arg_types.push(type_ref);
}
}
let body = self.collect_expr_opt(e.body());
self.alloc_expr(
Expr::Lambda {
args,
arg_types,
body,
},
syntax_ptr,
)
}
// TODO implement HIR for these:
ast::Expr::Label(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
ast::Expr::LambdaExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
ast::Expr::IndexExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
ast::Expr::TupleExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
ast::Expr::ArrayExpr(_e) => self.alloc_expr(Expr::Missing, syntax_ptr),
@ -431,16 +635,31 @@ impl ExprCollector {
fn collect_pat(&mut self, pat: ast::Pat) -> PatId {
let syntax_ptr = LocalSyntaxPtr::new(pat.syntax());
// TODO
self.alloc_pat(Pat, syntax_ptr)
match pat {
ast::Pat::BindPat(bp) => {
let name = bp
.name()
.map(|nr| nr.as_name())
.unwrap_or_else(Name::missing);
self.alloc_pat(Pat::Bind { name }, syntax_ptr)
}
ast::Pat::TupleStructPat(p) => {
let path = p.path().and_then(Path::from_ast);
let args = p.args().map(|p| self.collect_pat(p)).collect();
self.alloc_pat(Pat::TupleStruct { path, args }, syntax_ptr)
}
_ => {
// TODO
self.alloc_pat(Pat::Missing, syntax_ptr)
}
}
}
fn collect_pat_opt(&mut self, pat: Option<ast::Pat>) -> PatId {
if let Some(pat) = pat {
self.collect_pat(pat)
} else {
// TODO
self.pats.alloc(Pat)
self.pats.alloc(Pat::Missing)
}
}
@ -461,47 +680,61 @@ impl ExprCollector {
}
}
pub(crate) fn collect_fn_body_syntax(node: ast::FnDef) -> BodySyntaxMapping {
let mut collector = ExprCollector::new();
let args = if let Some(param_list) = node.param_list() {
let mut args = Vec::new();
if let Some(self_param) = param_list.self_param() {
let self_param = LocalSyntaxPtr::new(
self_param
.self_kw()
.expect("self param without self keyword")
.syntax(),
);
let arg = collector.alloc_pat(
Pat::Bind {
name: Name::self_param(),
},
self_param,
);
args.push(arg);
}
for param in param_list.params() {
let pat = if let Some(pat) = param.pat() {
pat
} else {
continue;
};
args.push(collector.collect_pat(pat));
}
args
} else {
Vec::new()
};
let body = collector.collect_block_opt(node.body());
collector.into_body_syntax_mapping(args, body)
}
pub(crate) fn body_syntax_mapping(
db: &impl HirDatabase,
def_id: DefId,
) -> Cancelable<Arc<BodySyntaxMapping>> {
let def = def_id.resolve(db)?;
let mut collector = ExprCollector {
exprs: Arena::default(),
pats: Arena::default(),
expr_syntax_mapping: FxHashMap::default(),
expr_syntax_mapping_back: FxHashMap::default(),
pat_syntax_mapping: FxHashMap::default(),
pat_syntax_mapping_back: FxHashMap::default(),
};
let (body, args) = match def {
let body_syntax_mapping = match def {
Def::Function(f) => {
let node = f.syntax(db);
let node = node.borrowed();
let args = if let Some(param_list) = node.param_list() {
let mut args = Vec::new();
// TODO self param
for param in param_list.params() {
let pat = if let Some(pat) = param.pat() {
pat
} else {
continue;
};
args.push(collector.collect_pat(pat));
}
args
} else {
Vec::new()
};
let body = collector.collect_block_opt(node.body());
(body, args)
collect_fn_body_syntax(node)
}
// TODO: consts, etc.
_ => panic!("Trying to get body for item type without body"),
};
Ok(Arc::new(collector.into_body_syntax_mapping(args, body)))
Ok(Arc::new(body_syntax_mapping))
}

View File

@ -11,9 +11,9 @@ use ra_syntax::{
ast::{self, AstNode, DocCommentsOwner, NameOwner},
};
use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock};
use crate::{DefId, DefKind, HirDatabase, ty::InferenceResult, Module, Crate, impl_block::ImplBlock, expr::{Body, BodySyntaxMapping}};
pub use self::scope::FnScopes;
pub use self::scope::{FnScopes, ScopesWithSyntaxMapping};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Function {
@ -36,8 +36,21 @@ impl Function {
ast::FnDef::cast(syntax.borrowed()).unwrap().owned()
}
pub fn scopes(&self, db: &impl HirDatabase) -> Arc<FnScopes> {
db.fn_scopes(self.def_id)
pub fn body(&self, db: &impl HirDatabase) -> Cancelable<Arc<Body>> {
db.body_hir(self.def_id)
}
pub fn body_syntax_mapping(&self, db: &impl HirDatabase) -> Cancelable<Arc<BodySyntaxMapping>> {
db.body_syntax_mapping(self.def_id)
}
pub fn scopes(&self, db: &impl HirDatabase) -> Cancelable<ScopesWithSyntaxMapping> {
let scopes = db.fn_scopes(self.def_id)?;
let syntax_mapping = db.body_syntax_mapping(self.def_id)?;
Ok(ScopesWithSyntaxMapping {
scopes,
syntax_mapping,
})
}
pub fn signature_info(&self, db: &impl HirDatabase) -> Option<FnSignatureInfo> {

View File

@ -1,14 +1,16 @@
use std::sync::Arc;
use rustc_hash::{FxHashMap, FxHashSet};
use ra_syntax::{
AstNode, SyntaxNodeRef, TextUnit, TextRange,
algo::generate,
ast::{self, ArgListOwner, LoopBodyOwner, NameOwner},
ast,
};
use ra_arena::{Arena, RawId, impl_arena_id};
use ra_db::LocalSyntaxPtr;
use crate::{Name, AsName};
use crate::{Name, AsName, expr::{PatId, ExprId, Pat, Expr, Body, Statement, BodySyntaxMapping}};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct ScopeId(RawId);
@ -16,15 +18,15 @@ impl_arena_id!(ScopeId);
#[derive(Debug, PartialEq, Eq)]
pub struct FnScopes {
pub self_param: Option<LocalSyntaxPtr>,
body: Arc<Body>,
scopes: Arena<ScopeId, ScopeData>,
scope_for: FxHashMap<LocalSyntaxPtr, ScopeId>,
scope_for: FxHashMap<ExprId, ScopeId>,
}
#[derive(Debug, PartialEq, Eq)]
pub struct ScopeEntry {
name: Name,
ptr: LocalSyntaxPtr,
pat: PatId,
}
#[derive(Debug, PartialEq, Eq)]
@ -34,28 +36,101 @@ pub struct ScopeData {
}
impl FnScopes {
pub(crate) fn new(fn_def: ast::FnDef) -> FnScopes {
pub(crate) fn new(body: Arc<Body>) -> FnScopes {
let mut scopes = FnScopes {
self_param: fn_def
.param_list()
.and_then(|it| it.self_param())
.map(|it| LocalSyntaxPtr::new(it.syntax())),
body: body.clone(),
scopes: Arena::default(),
scope_for: FxHashMap::default(),
};
let root = scopes.root_scope();
scopes.add_params_bindings(root, fn_def.param_list());
if let Some(body) = fn_def.body() {
compute_block_scopes(body, &mut scopes, root)
}
scopes.add_params_bindings(root, body.args());
compute_expr_scopes(body.body_expr(), &body, &mut scopes, root);
scopes
}
pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
&self.scopes[scope].entries
}
pub fn scope_chain_for<'a>(&'a self, expr: ExprId) -> impl Iterator<Item = ScopeId> + 'a {
generate(self.scope_for(expr), move |&scope| {
self.scopes[scope].parent
})
}
pub fn resolve_local_name<'a>(
&'a self,
context_expr: ExprId,
name: Name,
) -> Option<&'a ScopeEntry> {
let mut shadowed = FxHashSet::default();
let ret = self
.scope_chain_for(context_expr)
.flat_map(|scope| self.entries(scope).iter())
.filter(|entry| shadowed.insert(entry.name()))
.filter(|entry| entry.name() == &name)
.nth(0);
ret
}
fn root_scope(&mut self) -> ScopeId {
self.scopes.alloc(ScopeData {
parent: None,
entries: vec![],
})
}
fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
self.scopes.alloc(ScopeData {
parent: Some(parent),
entries: vec![],
})
}
fn add_bindings(&mut self, body: &Body, scope: ScopeId, pat: PatId) {
match body.pat(pat) {
Pat::Bind { name } => self.scopes[scope].entries.push(ScopeEntry {
name: name.clone(),
pat,
}),
p => p.walk_child_pats(|pat| self.add_bindings(body, scope, pat)),
}
}
fn add_params_bindings(&mut self, scope: ScopeId, params: &[PatId]) {
let body = Arc::clone(&self.body);
params
.into_iter()
.for_each(|it| self.add_bindings(&body, scope, *it));
}
fn set_scope(&mut self, node: ExprId, scope: ScopeId) {
self.scope_for.insert(node, scope);
}
fn scope_for(&self, expr: ExprId) -> Option<ScopeId> {
self.scope_for.get(&expr).map(|&scope| scope)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScopesWithSyntaxMapping {
pub syntax_mapping: Arc<BodySyntaxMapping>,
pub scopes: Arc<FnScopes>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ScopeEntryWithSyntax {
name: Name,
ptr: LocalSyntaxPtr,
}
impl ScopeEntryWithSyntax {
pub fn name(&self) -> &Name {
&self.name
}
pub fn ptr(&self) -> LocalSyntaxPtr {
self.ptr
}
}
impl ScopesWithSyntaxMapping {
pub fn scope_chain<'a>(&'a self, node: SyntaxNodeRef) -> impl Iterator<Item = ScopeId> + 'a {
generate(self.scope_for(node), move |&scope| {
self.scopes[scope].parent
self.scopes.scopes[scope].parent
})
}
pub fn scope_chain_for_offset<'a>(
@ -63,26 +138,30 @@ impl FnScopes {
offset: TextUnit,
) -> impl Iterator<Item = ScopeId> + 'a {
let scope = self
.scopes
.scope_for
.iter()
// find containin scope
.filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope)))
// find containing scope
.min_by_key(|(ptr, _scope)| {
(
!(ptr.range().start() <= offset && offset <= ptr.range().end()),
ptr.range().len(),
)
})
.map(|(ptr, scope)| self.adjust(*ptr, *scope, offset));
.map(|(ptr, scope)| self.adjust(ptr, *scope, offset));
generate(scope, move |&scope| self.scopes[scope].parent)
generate(scope, move |&scope| self.scopes.scopes[scope].parent)
}
// XXX: during completion, cursor might be outside of any particular
// expression. Try to figure out the correct scope...
fn adjust(&self, ptr: LocalSyntaxPtr, original_scope: ScopeId, offset: TextUnit) -> ScopeId {
let r = ptr.range();
let child_scopes = self
.scopes
.scope_for
.iter()
.filter_map(|(id, scope)| Some((self.syntax_mapping.expr_syntax(*id)?, scope)))
.map(|(ptr, scope)| (ptr.range(), scope))
.filter(|(range, _)| range.start() <= offset && range.is_subrange(&r) && *range != r);
@ -100,22 +179,27 @@ impl FnScopes {
.unwrap_or(original_scope)
}
pub fn resolve_local_name<'a>(&'a self, name_ref: ast::NameRef) -> Option<&'a ScopeEntry> {
pub fn resolve_local_name(&self, name_ref: ast::NameRef) -> Option<ScopeEntryWithSyntax> {
let mut shadowed = FxHashSet::default();
let name = name_ref.as_name();
let ret = self
.scope_chain(name_ref.syntax())
.flat_map(|scope| self.entries(scope).iter())
.flat_map(|scope| self.scopes.entries(scope).iter())
.filter(|entry| shadowed.insert(entry.name()))
.filter(|entry| entry.name() == &name)
.nth(0);
ret
ret.and_then(|entry| {
Some(ScopeEntryWithSyntax {
name: entry.name().clone(),
ptr: self.syntax_mapping.pat_syntax(entry.pat())?,
})
})
}
pub fn find_all_refs(&self, pat: ast::BindPat) -> Vec<ReferenceDescriptor> {
let fn_def = pat.syntax().ancestors().find_map(ast::FnDef::cast).unwrap();
let name_ptr = LocalSyntaxPtr::new(pat.syntax());
let refs: Vec<_> = fn_def
fn_def
.syntax()
.descendants()
.filter_map(ast::NameRef::cast)
@ -127,203 +211,95 @@ impl FnScopes {
name: name_ref.syntax().text().to_string(),
range: name_ref.syntax().range(),
})
.collect();
refs
.collect()
}
fn root_scope(&mut self) -> ScopeId {
self.scopes.alloc(ScopeData {
parent: None,
entries: vec![],
})
}
fn new_scope(&mut self, parent: ScopeId) -> ScopeId {
self.scopes.alloc(ScopeData {
parent: Some(parent),
entries: vec![],
})
}
fn add_bindings(&mut self, scope: ScopeId, pat: ast::Pat) {
let entries = pat
.syntax()
.descendants()
.filter_map(ast::BindPat::cast)
.filter_map(ScopeEntry::new);
self.scopes[scope].entries.extend(entries);
}
fn add_params_bindings(&mut self, scope: ScopeId, params: Option<ast::ParamList>) {
params
.into_iter()
.flat_map(|it| it.params())
.filter_map(|it| it.pat())
.for_each(|it| self.add_bindings(scope, it));
}
fn set_scope(&mut self, node: SyntaxNodeRef, scope: ScopeId) {
self.scope_for.insert(LocalSyntaxPtr::new(node), scope);
}
fn scope_for(&self, node: SyntaxNodeRef) -> Option<ScopeId> {
node.ancestors()
.map(LocalSyntaxPtr::new)
.filter_map(|it| self.scope_for.get(&it).map(|&scope| scope))
.filter_map(|ptr| self.syntax_mapping.syntax_expr(ptr))
.filter_map(|it| self.scopes.scope_for(it))
.next()
}
}
impl ScopeEntry {
fn new(pat: ast::BindPat) -> Option<ScopeEntry> {
let name = pat.name()?.as_name();
let res = ScopeEntry {
name,
ptr: LocalSyntaxPtr::new(pat.syntax()),
};
Some(res)
}
pub fn name(&self) -> &Name {
&self.name
}
pub fn ptr(&self) -> LocalSyntaxPtr {
self.ptr
pub fn pat(&self) -> PatId {
self.pat
}
}
fn compute_block_scopes(block: ast::Block, scopes: &mut FnScopes, mut scope: ScopeId) {
// A hack for completion :(
scopes.set_scope(block.syntax(), scope);
for stmt in block.statements() {
fn compute_block_scopes(
statements: &[Statement],
tail: Option<ExprId>,
body: &Body,
scopes: &mut FnScopes,
mut scope: ScopeId,
) {
for stmt in statements {
match stmt {
ast::Stmt::LetStmt(stmt) => {
if let Some(expr) = stmt.initializer() {
scopes.set_scope(expr.syntax(), scope);
compute_expr_scopes(expr, scopes, scope);
Statement::Let {
pat, initializer, ..
} => {
if let Some(expr) = initializer {
scopes.set_scope(*expr, scope);
compute_expr_scopes(*expr, body, scopes, scope);
}
scope = scopes.new_scope(scope);
if let Some(pat) = stmt.pat() {
scopes.add_bindings(scope, pat);
}
scopes.add_bindings(body, scope, *pat);
}
ast::Stmt::ExprStmt(expr_stmt) => {
if let Some(expr) = expr_stmt.expr() {
scopes.set_scope(expr.syntax(), scope);
compute_expr_scopes(expr, scopes, scope);
}
Statement::Expr(expr) => {
scopes.set_scope(*expr, scope);
compute_expr_scopes(*expr, body, scopes, scope);
}
}
}
if let Some(expr) = block.expr() {
scopes.set_scope(expr.syntax(), scope);
compute_expr_scopes(expr, scopes, scope);
if let Some(expr) = tail {
compute_expr_scopes(expr, body, scopes, scope);
}
}
fn compute_expr_scopes(expr: ast::Expr, scopes: &mut FnScopes, scope: ScopeId) {
match expr {
ast::Expr::IfExpr(e) => {
let cond_scope = e
.condition()
.and_then(|cond| compute_cond_scopes(cond, scopes, scope));
if let Some(block) = e.then_branch() {
compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
}
if let Some(block) = e.else_branch() {
compute_block_scopes(block, scopes, scope);
}
fn compute_expr_scopes(expr: ExprId, body: &Body, scopes: &mut FnScopes, scope: ScopeId) {
scopes.set_scope(expr, scope);
match body.expr(expr) {
Expr::Block { statements, tail } => {
compute_block_scopes(&statements, *tail, body, scopes, scope);
}
ast::Expr::BlockExpr(e) => {
if let Some(block) = e.block() {
compute_block_scopes(block, scopes, scope);
}
}
ast::Expr::LoopExpr(e) => {
if let Some(block) = e.loop_body() {
compute_block_scopes(block, scopes, scope);
}
}
ast::Expr::WhileExpr(e) => {
let cond_scope = e
.condition()
.and_then(|cond| compute_cond_scopes(cond, scopes, scope));
if let Some(block) = e.loop_body() {
compute_block_scopes(block, scopes, cond_scope.unwrap_or(scope));
}
}
ast::Expr::ForExpr(e) => {
if let Some(expr) = e.iterable() {
compute_expr_scopes(expr, scopes, scope);
}
let mut scope = scope;
if let Some(pat) = e.pat() {
scope = scopes.new_scope(scope);
scopes.add_bindings(scope, pat);
}
if let Some(block) = e.loop_body() {
compute_block_scopes(block, scopes, scope);
}
}
ast::Expr::LambdaExpr(e) => {
Expr::For {
iterable,
pat,
body: body_expr,
} => {
compute_expr_scopes(*iterable, body, scopes, scope);
let scope = scopes.new_scope(scope);
scopes.add_params_bindings(scope, e.param_list());
if let Some(body) = e.body() {
scopes.set_scope(body.syntax(), scope);
compute_expr_scopes(body, scopes, scope);
}
scopes.add_bindings(body, scope, *pat);
compute_expr_scopes(*body_expr, body, scopes, scope);
}
ast::Expr::CallExpr(e) => {
compute_call_scopes(e.expr(), e.arg_list(), scopes, scope);
Expr::Lambda {
args,
body: body_expr,
..
} => {
let scope = scopes.new_scope(scope);
scopes.add_params_bindings(scope, &args);
compute_expr_scopes(*body_expr, body, scopes, scope);
}
ast::Expr::MethodCallExpr(e) => {
compute_call_scopes(e.expr(), e.arg_list(), scopes, scope);
}
ast::Expr::MatchExpr(e) => {
if let Some(expr) = e.expr() {
compute_expr_scopes(expr, scopes, scope);
}
for arm in e.match_arm_list().into_iter().flat_map(|it| it.arms()) {
Expr::Match { expr, arms } => {
compute_expr_scopes(*expr, body, scopes, scope);
for arm in arms {
let scope = scopes.new_scope(scope);
for pat in arm.pats() {
scopes.add_bindings(scope, pat);
}
if let Some(expr) = arm.expr() {
compute_expr_scopes(expr, scopes, scope);
for pat in &arm.pats {
scopes.add_bindings(body, scope, *pat);
}
scopes.set_scope(arm.expr, scope);
compute_expr_scopes(arm.expr, body, scopes, scope);
}
}
_ => expr
.syntax()
.children()
.filter_map(ast::Expr::cast)
.for_each(|expr| compute_expr_scopes(expr, scopes, scope)),
e => e.walk_child_exprs(|e| compute_expr_scopes(e, body, scopes, scope)),
};
fn compute_call_scopes(
receiver: Option<ast::Expr>,
arg_list: Option<ast::ArgList>,
scopes: &mut FnScopes,
scope: ScopeId,
) {
arg_list
.into_iter()
.flat_map(|it| it.args())
.chain(receiver)
.for_each(|expr| compute_expr_scopes(expr, scopes, scope));
}
fn compute_cond_scopes(
cond: ast::Condition,
scopes: &mut FnScopes,
scope: ScopeId,
) -> Option<ScopeId> {
if let Some(expr) = cond.expr() {
compute_expr_scopes(expr, scopes, scope);
}
if let Some(pat) = cond.pat() {
let s = scopes.new_scope(scope);
scopes.add_bindings(s, pat);
Some(s)
} else {
None
}
}
}
#[derive(Debug)]
@ -338,6 +314,8 @@ mod tests {
use ra_syntax::SourceFileNode;
use test_utils::{extract_offset, assert_eq_text};
use crate::expr;
use super::*;
fn do_check(code: &str, expected: &[&str]) {
@ -353,15 +331,20 @@ mod tests {
let file = SourceFileNode::parse(&code);
let marker: ast::PathExpr = find_node_at_offset(file.syntax(), off).unwrap();
let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
let scopes = FnScopes::new(fn_def);
let body_hir = expr::collect_fn_body_syntax(fn_def);
let scopes = FnScopes::new(Arc::clone(body_hir.body()));
let scopes = ScopesWithSyntaxMapping {
scopes: Arc::new(scopes),
syntax_mapping: Arc::new(body_hir),
};
let actual = scopes
.scope_chain(marker.syntax())
.flat_map(|scope| scopes.entries(scope))
.flat_map(|scope| scopes.scopes.entries(scope))
.map(|it| it.name().to_string())
.collect::<Vec<_>>()
.join("\n");
let expected = expected.join("\n");
assert_eq_text!(&actual, &expected);
assert_eq_text!(&expected, &actual);
}
#[test]
@ -389,7 +372,7 @@ mod tests {
}
#[test]
fn test_metod_call_scope() {
fn test_method_call_scope() {
do_check(
r"
fn quux() {
@ -445,10 +428,15 @@ mod tests {
let fn_def: ast::FnDef = find_node_at_offset(file.syntax(), off).unwrap();
let name_ref: ast::NameRef = find_node_at_offset(file.syntax(), off).unwrap();
let scopes = FnScopes::new(fn_def);
let body_hir = expr::collect_fn_body_syntax(fn_def);
let scopes = FnScopes::new(Arc::clone(body_hir.body()));
let scopes = ScopesWithSyntaxMapping {
scopes: Arc::new(scopes),
syntax_mapping: Arc::new(body_hir),
};
let local_name_entry = scopes.resolve_local_name(name_ref).unwrap();
let local_name = local_name_entry.ptr().resolve(&file);
let local_name = local_name_entry.ptr();
let expected_name =
find_node_at_offset::<ast::Name>(file.syntax(), expected_offset.into()).unwrap();
assert_eq!(local_name.range(), expected_name.syntax().range());

View File

@ -47,7 +47,7 @@ pub use self::{
ids::{HirFileId, DefId, DefLoc, MacroCallId, MacroCallLoc},
macros::{MacroDef, MacroInput, MacroExpansion},
module::{Module, ModuleId, Problem, nameres::{ItemMap, PerNs, Namespace}, ModuleScope, Resolution},
function::{Function, FnScopes},
function::{Function, FnScopes, ScopesWithSyntaxMapping},
adt::{Struct, Enum},
ty::Ty,
impl_block::{ImplBlock, ImplItem},

View File

@ -31,6 +31,10 @@ impl Name {
Name::new("[missing name]".into())
}
pub(crate) fn self_param() -> Name {
Name::new("self".into())
}
pub(crate) fn tuple_field_name(idx: usize) -> Name {
Name::new(idx.to_string().into())
}
@ -51,7 +55,8 @@ impl Name {
"u128" => KnownName::U128,
"f32" => KnownName::F32,
"f64" => KnownName::F64,
"Self" => KnownName::Self_,
"Self" => KnownName::SelfType,
"self" => KnownName::SelfParam,
_ => return None,
};
Some(name)
@ -104,5 +109,6 @@ pub(crate) enum KnownName {
F32,
F64,
Self_,
SelfType,
SelfParam,
}

View File

@ -11,7 +11,7 @@ use ra_syntax::{
use ra_db::{SourceRootId, Cancelable,};
use crate::{
SourceFileItems, SourceItemId, DefKind, Function, DefId, Name, AsName, HirFileId,
SourceFileItems, SourceItemId, DefKind, DefId, Name, AsName, HirFileId,
MacroCallLoc,
db::HirDatabase,
function::FnScopes,
@ -23,11 +23,10 @@ use crate::{
adt::{StructData, EnumData},
};
pub(super) fn fn_scopes(db: &impl HirDatabase, def_id: DefId) -> Arc<FnScopes> {
let function = Function::new(def_id);
let syntax = function.syntax(db);
let res = FnScopes::new(syntax.borrowed());
Arc::new(res)
pub(super) fn fn_scopes(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<FnScopes>> {
let body = db.body_hir(def_id)?;
let res = FnScopes::new(body);
Ok(Arc::new(res))
}
pub(super) fn struct_data(db: &impl HirDatabase, def_id: DefId) -> Cancelable<Arc<StructData>> {

View File

@ -31,10 +31,11 @@ use ra_syntax::{
};
use crate::{
Def, DefId, FnScopes, Module, Function, Struct, Enum, Path, Name, AsName, ImplBlock,
Def, DefId, Module, Function, Struct, Enum, Path, Name, AsName, ImplBlock,
db::HirDatabase,
type_ref::{TypeRef, Mutability},
name::KnownName,
ScopesWithSyntaxMapping,
};
/// The ID of a type variable.
@ -305,7 +306,7 @@ impl Ty {
return Ok(Ty::Uint(uint_ty));
} else if let Some(float_ty) = primitive::FloatTy::from_name(name) {
return Ok(Ty::Float(float_ty));
} else if name.as_known_name() == Some(KnownName::Self_) {
} else if name.as_known_name() == Some(KnownName::SelfType) {
return Ty::from_hir_opt(db, module, None, impl_block.map(|i| i.target_type()));
}
}
@ -515,7 +516,7 @@ impl InferenceResult {
#[derive(Clone, Debug)]
struct InferenceContext<'a, D: HirDatabase> {
db: &'a D,
scopes: Arc<FnScopes>,
scopes: ScopesWithSyntaxMapping,
/// The self param for the current method, if it exists.
self_param: Option<LocalSyntaxPtr>,
module: Module,
@ -529,7 +530,7 @@ struct InferenceContext<'a, D: HirDatabase> {
impl<'a, D: HirDatabase> InferenceContext<'a, D> {
fn new(
db: &'a D,
scopes: Arc<FnScopes>,
scopes: ScopesWithSyntaxMapping,
module: Module,
impl_block: Option<ImplBlock>,
) -> Self {
@ -826,10 +827,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
self.infer_expr_opt(e.expr(), &Expectation::none())?;
Ty::Never
}
ast::Expr::MatchArmList(_) | ast::Expr::MatchArm(_) | ast::Expr::MatchGuard(_) => {
// Can this even occur outside of a match expression?
Ty::Unknown
}
ast::Expr::StructLit(e) => {
let (ty, def_id) = self.resolve_variant(e.path())?;
if let Some(nfl) = e.named_field_list() {
@ -845,10 +842,6 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
}
ty
}
ast::Expr::NamedFieldList(_) | ast::Expr::NamedField(_) => {
// Can this even occur outside of a struct literal?
Ty::Unknown
}
ast::Expr::IndexExpr(_e) => Ty::Unknown,
ast::Expr::FieldExpr(e) => {
let receiver_ty = self.infer_expr_opt(e.expr(), &Expectation::none())?;
@ -1016,7 +1009,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
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 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);

View File

@ -927,12 +927,7 @@ pub enum Expr<'a> {
BlockExpr(BlockExpr<'a>),
ReturnExpr(ReturnExpr<'a>),
MatchExpr(MatchExpr<'a>),
MatchArmList(MatchArmList<'a>),
MatchArm(MatchArm<'a>),
MatchGuard(MatchGuard<'a>),
StructLit(StructLit<'a>),
NamedFieldList(NamedFieldList<'a>),
NamedField(NamedField<'a>),
CallExpr(CallExpr<'a>),
IndexExpr(IndexExpr<'a>),
MethodCallExpr(MethodCallExpr<'a>),
@ -964,12 +959,7 @@ impl<'a> AstNode<'a> for Expr<'a> {
BLOCK_EXPR => Some(Expr::BlockExpr(BlockExpr { syntax })),
RETURN_EXPR => Some(Expr::ReturnExpr(ReturnExpr { syntax })),
MATCH_EXPR => Some(Expr::MatchExpr(MatchExpr { syntax })),
MATCH_ARM_LIST => Some(Expr::MatchArmList(MatchArmList { syntax })),
MATCH_ARM => Some(Expr::MatchArm(MatchArm { syntax })),
MATCH_GUARD => Some(Expr::MatchGuard(MatchGuard { syntax })),
STRUCT_LIT => Some(Expr::StructLit(StructLit { syntax })),
NAMED_FIELD_LIST => Some(Expr::NamedFieldList(NamedFieldList { syntax })),
NAMED_FIELD => Some(Expr::NamedField(NamedField { syntax })),
CALL_EXPR => Some(Expr::CallExpr(CallExpr { syntax })),
INDEX_EXPR => Some(Expr::IndexExpr(IndexExpr { syntax })),
METHOD_CALL_EXPR => Some(Expr::MethodCallExpr(MethodCallExpr { syntax })),
@ -1001,12 +991,7 @@ impl<'a> AstNode<'a> for Expr<'a> {
Expr::BlockExpr(inner) => inner.syntax(),
Expr::ReturnExpr(inner) => inner.syntax(),
Expr::MatchExpr(inner) => inner.syntax(),
Expr::MatchArmList(inner) => inner.syntax(),
Expr::MatchArm(inner) => inner.syntax(),
Expr::MatchGuard(inner) => inner.syntax(),
Expr::StructLit(inner) => inner.syntax(),
Expr::NamedFieldList(inner) => inner.syntax(),
Expr::NamedField(inner) => inner.syntax(),
Expr::CallExpr(inner) => inner.syntax(),
Expr::IndexExpr(inner) => inner.syntax(),
Expr::MethodCallExpr(inner) => inner.syntax(),
@ -4155,7 +4140,15 @@ impl<R: TreeRoot<RaTypes>> TupleStructPatNode<R> {
}
impl<'a> TupleStructPat<'a> {}
impl<'a> TupleStructPat<'a> {
pub fn args(self) -> impl Iterator<Item = Pat<'a>> + 'a {
super::children(self)
}
pub fn path(self) -> Option<Path<'a>> {
super::child_opt(self)
}
}
// TupleType
#[derive(Debug, Clone, Copy,)]

View File

@ -446,12 +446,7 @@ Grammar(
"BlockExpr",
"ReturnExpr",
"MatchExpr",
"MatchArmList",
"MatchArm",
"MatchGuard",
"StructLit",
"NamedFieldList",
"NamedField",
"CallExpr",
"IndexExpr",
"MethodCallExpr",
@ -472,7 +467,10 @@ Grammar(
"PathPat": (),
"StructPat": (),
"FieldPatList": (),
"TupleStructPat": (),
"TupleStructPat": (
options: ["Path"],
collections: [["args", "Pat"]],
),
"TuplePat": (),
"SlicePat": (),
"RangePat": (),