pub fn owner(&self) -> Function {
self.owner
}
+
+ pub fn syntax_mapping(&self, db: &impl HirDatabase) -> Arc<BodySyntaxMapping> {
+ db.body_syntax_mapping(self.owner)
+ }
}
// needs arbitrary_self_types to be a method... or maybe move to the def?
scopes
}
+ pub fn body(&self) -> Arc<Body> {
+ self.body.clone()
+ }
+
pub fn entries(&self, scope: ScopeId) -> &[ScopeEntry] {
&self.scopes[scope].entries
}
.collect()
}
- fn scope_for(&self, node: &SyntaxNode) -> Option<ScopeId> {
+ pub fn scope_for(&self, node: &SyntaxNode) -> Option<ScopeId> {
node.ancestors()
.map(SyntaxNodePtr::new)
.filter_map(|ptr| self.syntax_mapping.syntax_expr(ptr))
name::{Name, KnownName},
nameres::{PerNs, ItemMap},
generics::GenericParams,
- expr::{scope::{ExprScopes, ScopeId}, PatId},
+ expr::{scope::{ExprScopes, ScopeId}, PatId, Body},
impl_block::ImplBlock,
path::Path,
};
}
fn module(&self) -> Option<(&ItemMap, Module)> {
- for scope in self.scopes.iter().rev() {
- match scope {
- Scope::ModuleScope(m) => {
- return Some((&m.item_map, m.module.clone()));
- }
- _ => {}
- }
- }
- None
+ self.scopes.iter().rev().find_map(|scope| match scope {
+ Scope::ModuleScope(m) => Some((&*m.item_map, m.module.clone())),
+
+ Scope::ModuleScopeRef(m) => Some((m.item_map, m.module.clone())),
+
+ _ => None,
+ })
+ }
+
+ /// The body from which any `LocalBinding` resolutions in this resolver come.
+ pub fn body(&self) -> Option<Arc<Body>> {
+ self.scopes.iter().rev().find_map(|scope| match scope {
+ Scope::ExprScope(expr_scope) => Some(expr_scope.expr_scopes.body()),
+ _ => None,
+ })
}
}
})
.unwrap_or_default()
}
+
+pub fn resolver_for_node(
+ db: &impl HirDatabase,
+ file_id: FileId,
+ node: &SyntaxNode,
+) -> Resolver<'static> {
+ node.ancestors()
+ .find_map(|node| {
+ if ast::Expr::cast(node).is_some() || ast::Block::cast(node).is_some() {
+ if let Some(func) = function_from_child_node(db, file_id, node) {
+ let scopes = func.scopes(db);
+ let scope = scopes.scope_for(&node);
+ Some(expr::resolver_for_scope(func.body(db), db, scope))
+ } else {
+ // TODO const/static/array length
+ None
+ }
+ } else if let Some(module) = ast::Module::cast(node) {
+ Some(module_from_declaration(db, file_id, module)?.resolver(db))
+ } else if let Some(_) = ast::SourceFile::cast(node) {
+ Some(module_from_source(db, file_id.into(), None)?.resolver(db))
+ } else {
+ // TODO add missing cases
+ None
+ }
+ })
+ .unwrap_or_default()
+}
if let Some(function) =
hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())
{
- let scope = function.scopes(db);
- // First try to resolve the symbol locally
- if let Some(entry) = scope.resolve_local_name(name_ref) {
- let nav = NavigationTarget::from_scope_entry(file_id, &entry);
- return Exact(nav);
- };
-
- // Next check if it is a method
+ // Check if it is a method
if let Some(method_call) = name_ref
.syntax()
.parent()
};
}
}
- // Then try module name resolution
- if let Some(module) = hir::source_binder::module_from_child_node(db, file_id, name_ref.syntax())
+ // Try name resolution
+ let resolver = hir::source_binder::resolver_for_node(db, file_id, name_ref.syntax());
+ if let Some(path) = name_ref
+ .syntax()
+ .ancestors()
+ .find_map(ast::Path::cast)
+ .and_then(hir::Path::from_ast)
{
- if let Some(path) = name_ref
- .syntax()
- .ancestors()
- .find_map(ast::Path::cast)
- .and_then(hir::Path::from_ast)
- {
- let resolved = module.resolve_path(db, &path);
- if let Some(def_id) = resolved.take_types().or(resolved.take_values()) {
- return Exact(NavigationTarget::from_def(db, def_id));
+ let resolved = resolver.resolve_path(db, &path);
+ match resolved.clone().take_types().or(resolved.take_values()) {
+ Some(Resolution::Def { def }) => return Exact(NavigationTarget::from_def(db, def)),
+ Some(Resolution::LocalBinding { pat }) => {
+ let body = resolver.body().expect("no body for local binding");
+ let syntax_mapping = body.syntax_mapping(db);
+ let ptr = syntax_mapping
+ .pat_syntax(pat)
+ .expect("pattern not found in syntax mapping");
+ let name = path
+ .as_ident()
+ .cloned()
+ .expect("local binding from a multi-segment path");
+ let nav = NavigationTarget::from_scope_entry(file_id, name, ptr);
+ return Exact(nav);
+ }
+ Some(Resolution::GenericParam { .. }) => {
+ // TODO go to the generic param def
+ }
+ Some(Resolution::SelfType(_impl_block)) => {
+ // TODO go to the implemented type
}
+ None => {}
}
}
// If that fails try the index based approach.
use ra_db::FileId;
use ra_syntax::{
- SyntaxNode, AstNode, SmolStr, TextRange, ast,
+ SyntaxNode, SyntaxNodePtr, AstNode, SmolStr, TextRange, ast,
SyntaxKind::{self, NAME},
};
-use hir::{ModuleSource, FieldSource};
+use hir::{ModuleSource, FieldSource, Name};
use crate::{FileSymbol, db::RootDatabase};
pub(crate) fn from_scope_entry(
file_id: FileId,
- entry: &hir::ScopeEntryWithSyntax,
+ name: Name,
+ ptr: SyntaxNodePtr,
) -> NavigationTarget {
NavigationTarget {
file_id,
- name: entry.name().to_string().into(),
- full_range: entry.ptr().range(),
+ name: name.to_string().into(),
+ full_range: ptr.range(),
focus_range: None,
kind: NAME,
}