mirror of
https://github.com/rust-lang/rust-analyzer.git
synced 2025-09-27 20:42:04 +00:00
Make goto definition/hover resolve constructors
This commit is contained in:
parent
dc8bcc1e42
commit
49da9a3e81
3 changed files with 87 additions and 7 deletions
|
@ -54,6 +54,24 @@ pub fn infer(db: &impl HirDatabase, func: Function) -> Arc<InferenceResult> {
|
||||||
Arc::new(ctx.resolve_all())
|
Arc::new(ctx.resolve_all())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Copy, Clone)]
|
||||||
|
enum ExprOrPatId {
|
||||||
|
Expr(ExprId),
|
||||||
|
Pat(PatId),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<ExprId> for ExprOrPatId {
|
||||||
|
fn from(id: ExprId) -> Self {
|
||||||
|
ExprOrPatId::Expr(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PatId> for ExprOrPatId {
|
||||||
|
fn from(id: PatId) -> Self {
|
||||||
|
ExprOrPatId::Pat(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The result of type inference: A mapping from expressions and patterns to types.
|
/// The result of type inference: A mapping from expressions and patterns to types.
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
pub struct InferenceResult {
|
pub struct InferenceResult {
|
||||||
|
@ -61,6 +79,8 @@ pub struct InferenceResult {
|
||||||
method_resolutions: FxHashMap<ExprId, Function>,
|
method_resolutions: FxHashMap<ExprId, Function>,
|
||||||
/// For each field access expr, records the field it resolves to.
|
/// For each field access expr, records the field it resolves to.
|
||||||
field_resolutions: FxHashMap<ExprId, StructField>,
|
field_resolutions: FxHashMap<ExprId, StructField>,
|
||||||
|
/// For each associated function call expr, records the function it resolves to
|
||||||
|
assoc_fn_resolutions: FxHashMap<ExprId, Function>,
|
||||||
pub(super) type_of_expr: ArenaMap<ExprId, Ty>,
|
pub(super) type_of_expr: ArenaMap<ExprId, Ty>,
|
||||||
pub(super) type_of_pat: ArenaMap<PatId, Ty>,
|
pub(super) type_of_pat: ArenaMap<PatId, Ty>,
|
||||||
}
|
}
|
||||||
|
@ -72,6 +92,9 @@ impl InferenceResult {
|
||||||
pub fn field_resolution(&self, expr: ExprId) -> Option<StructField> {
|
pub fn field_resolution(&self, expr: ExprId) -> Option<StructField> {
|
||||||
self.field_resolutions.get(&expr).map(|it| *it)
|
self.field_resolutions.get(&expr).map(|it| *it)
|
||||||
}
|
}
|
||||||
|
pub fn assoc_fn_resolutions(&self, expr: ExprId) -> Option<Function> {
|
||||||
|
self.assoc_fn_resolutions.get(&expr).map(|it| *it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Index<ExprId> for InferenceResult {
|
impl Index<ExprId> for InferenceResult {
|
||||||
|
@ -99,6 +122,7 @@ struct InferenceContext<'a, D: HirDatabase> {
|
||||||
var_unification_table: InPlaceUnificationTable<TypeVarId>,
|
var_unification_table: InPlaceUnificationTable<TypeVarId>,
|
||||||
method_resolutions: FxHashMap<ExprId, Function>,
|
method_resolutions: FxHashMap<ExprId, Function>,
|
||||||
field_resolutions: FxHashMap<ExprId, StructField>,
|
field_resolutions: FxHashMap<ExprId, StructField>,
|
||||||
|
assoc_fn_resolutions: FxHashMap<ExprId, Function>,
|
||||||
type_of_expr: ArenaMap<ExprId, Ty>,
|
type_of_expr: ArenaMap<ExprId, Ty>,
|
||||||
type_of_pat: ArenaMap<PatId, Ty>,
|
type_of_pat: ArenaMap<PatId, Ty>,
|
||||||
/// The return type of the function being inferred.
|
/// The return type of the function being inferred.
|
||||||
|
@ -110,6 +134,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
InferenceContext {
|
InferenceContext {
|
||||||
method_resolutions: FxHashMap::default(),
|
method_resolutions: FxHashMap::default(),
|
||||||
field_resolutions: FxHashMap::default(),
|
field_resolutions: FxHashMap::default(),
|
||||||
|
assoc_fn_resolutions: FxHashMap::default(),
|
||||||
type_of_expr: ArenaMap::default(),
|
type_of_expr: ArenaMap::default(),
|
||||||
type_of_pat: ArenaMap::default(),
|
type_of_pat: ArenaMap::default(),
|
||||||
var_unification_table: InPlaceUnificationTable::new(),
|
var_unification_table: InPlaceUnificationTable::new(),
|
||||||
|
@ -135,6 +160,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
InferenceResult {
|
InferenceResult {
|
||||||
method_resolutions: self.method_resolutions,
|
method_resolutions: self.method_resolutions,
|
||||||
field_resolutions: self.field_resolutions,
|
field_resolutions: self.field_resolutions,
|
||||||
|
assoc_fn_resolutions: self.assoc_fn_resolutions,
|
||||||
type_of_expr: expr_types,
|
type_of_expr: expr_types,
|
||||||
type_of_pat: pat_types,
|
type_of_pat: pat_types,
|
||||||
}
|
}
|
||||||
|
@ -152,6 +178,10 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
self.field_resolutions.insert(expr, field);
|
self.field_resolutions.insert(expr, field);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn write_assoc_fn_resolution(&mut self, expr: ExprId, func: Function) {
|
||||||
|
self.assoc_fn_resolutions.insert(expr, func);
|
||||||
|
}
|
||||||
|
|
||||||
fn write_pat_ty(&mut self, pat: PatId, ty: Ty) {
|
fn write_pat_ty(&mut self, pat: PatId, ty: Ty) {
|
||||||
self.type_of_pat.insert(pat, ty);
|
self.type_of_pat.insert(pat, ty);
|
||||||
}
|
}
|
||||||
|
@ -341,7 +371,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path) -> Option<Ty> {
|
fn infer_path_expr(&mut self, resolver: &Resolver, path: &Path, id: ExprOrPatId) -> Option<Ty> {
|
||||||
let resolved = resolver.resolve_path_segments(self.db, &path);
|
let resolved = resolver.resolve_path_segments(self.db, &path);
|
||||||
|
|
||||||
let (def, remaining_index) = resolved.into_inner();
|
let (def, remaining_index) = resolved.into_inner();
|
||||||
|
@ -421,6 +451,13 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
let typable: Option<TypableDef> = def.into();
|
let typable: Option<TypableDef> = def.into();
|
||||||
let typable = typable?;
|
let typable = typable?;
|
||||||
|
|
||||||
|
if let ExprOrPatId::Expr(expr) = id {
|
||||||
|
match typable {
|
||||||
|
TypableDef::Function(func) => self.write_assoc_fn_resolution(expr, func),
|
||||||
|
_ => {}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable);
|
let substs = Ty::substs_from_path(self.db, &self.resolver, path, typable);
|
||||||
let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs);
|
let ty = self.db.type_for_def(typable, Namespace::Values).apply_substs(substs);
|
||||||
let ty = self.insert_type_vars(ty);
|
let ty = self.insert_type_vars(ty);
|
||||||
|
@ -572,7 +609,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
Pat::Path(path) => {
|
Pat::Path(path) => {
|
||||||
// TODO use correct resolver for the surrounding expression
|
// TODO use correct resolver for the surrounding expression
|
||||||
let resolver = self.resolver.clone();
|
let resolver = self.resolver.clone();
|
||||||
self.infer_path_expr(&resolver, &path).unwrap_or(Ty::Unknown)
|
self.infer_path_expr(&resolver, &path, pat.into()).unwrap_or(Ty::Unknown)
|
||||||
}
|
}
|
||||||
Pat::Bind { mode, name: _name, subpat } => {
|
Pat::Bind { mode, name: _name, subpat } => {
|
||||||
let inner_ty = if let Some(subpat) = subpat {
|
let inner_ty = if let Some(subpat) = subpat {
|
||||||
|
@ -782,7 +819,7 @@ impl<'a, D: HirDatabase> InferenceContext<'a, D> {
|
||||||
Expr::Path(p) => {
|
Expr::Path(p) => {
|
||||||
// TODO this could be more efficient...
|
// TODO this could be more efficient...
|
||||||
let resolver = expr::resolver_for_expr(self.body.clone(), self.db, tgt_expr);
|
let resolver = expr::resolver_for_expr(self.body.clone(), self.db, tgt_expr);
|
||||||
self.infer_path_expr(&resolver, p).unwrap_or(Ty::Unknown)
|
self.infer_path_expr(&resolver, p, tgt_expr.into()).unwrap_or(Ty::Unknown)
|
||||||
}
|
}
|
||||||
Expr::Continue => Ty::Never,
|
Expr::Continue => Ty::Never,
|
||||||
Expr::Break { expr } => {
|
Expr::Break { expr } => {
|
||||||
|
|
|
@ -47,9 +47,10 @@ pub(crate) fn reference_definition(
|
||||||
name_ref: &ast::NameRef,
|
name_ref: &ast::NameRef,
|
||||||
) -> ReferenceResult {
|
) -> ReferenceResult {
|
||||||
use self::ReferenceResult::*;
|
use self::ReferenceResult::*;
|
||||||
if let Some(function) =
|
|
||||||
hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax())
|
let function = hir::source_binder::function_from_child_node(db, file_id, name_ref.syntax());
|
||||||
{
|
|
||||||
|
if let Some(function) = function {
|
||||||
// Check if it is a method
|
// Check if it is a method
|
||||||
if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) {
|
if let Some(method_call) = name_ref.syntax().parent().and_then(ast::MethodCallExpr::cast) {
|
||||||
tested_by!(goto_definition_works_for_methods);
|
tested_by!(goto_definition_works_for_methods);
|
||||||
|
@ -122,9 +123,29 @@ pub(crate) fn reference_definition(
|
||||||
Some(Resolution::SelfType(_impl_block)) => {
|
Some(Resolution::SelfType(_impl_block)) => {
|
||||||
// TODO: go to the implemented type
|
// TODO: go to the implemented type
|
||||||
}
|
}
|
||||||
None => {}
|
None => {
|
||||||
|
// If we failed to resolve then check associated items
|
||||||
|
if let Some(function) = function {
|
||||||
|
// Should we do this above and then grab path from the PathExpr?
|
||||||
|
if let Some(path_expr) =
|
||||||
|
name_ref.syntax().ancestors().find_map(ast::PathExpr::cast)
|
||||||
|
{
|
||||||
|
let infer_result = function.infer(db);
|
||||||
|
let syntax_mapping = function.body_syntax_mapping(db);
|
||||||
|
let expr = ast::Expr::cast(path_expr.syntax()).unwrap();
|
||||||
|
|
||||||
|
if let Some(func) = syntax_mapping
|
||||||
|
.node_expr(expr)
|
||||||
|
.and_then(|it| infer_result.assoc_fn_resolutions(it))
|
||||||
|
{
|
||||||
|
return Exact(NavigationTarget::from_function(db, func));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If that fails try the index based approach.
|
// If that fails try the index based approach.
|
||||||
let navs = crate::symbol_index::index_resolve(db, name_ref)
|
let navs = crate::symbol_index::index_resolve(db, name_ref)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
|
|
@ -512,4 +512,26 @@ mod tests {
|
||||||
let hover = analysis.hover(position).unwrap().unwrap();
|
let hover = analysis.hover(position).unwrap().unwrap();
|
||||||
assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing"));
|
assert_eq!(trim_markup_opt(hover.info.first()), Some("Thing"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_hover_infer_associated_method_exact() {
|
||||||
|
let (analysis, position) = single_file_with_position(
|
||||||
|
"
|
||||||
|
struct Thing { x: u32 }
|
||||||
|
|
||||||
|
impl Thing {
|
||||||
|
fn new() -> Thing {
|
||||||
|
Thing { x: 0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let foo_test = Thing::new<|>();
|
||||||
|
}
|
||||||
|
",
|
||||||
|
);
|
||||||
|
let hover = analysis.hover(position).unwrap().unwrap();
|
||||||
|
assert_eq!(hover.info.first(), Some("```rust\nfn new() -> Thing\n```"));
|
||||||
|
assert_eq!(hover.info.is_exact(), true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue