mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-14 06:03:58 +00:00
Auto merge of #18362 - duncpro:goto-def-ranges, r=Veykril
feat: goto definition on range operators Closes #18342
This commit is contained in:
commit
17055aaca9
4 changed files with 139 additions and 17 deletions
|
@ -17,7 +17,7 @@ use hir_def::{
|
||||||
path::ModPath,
|
path::ModPath,
|
||||||
resolver::{self, HasResolver, Resolver, TypeNs},
|
resolver::{self, HasResolver, Resolver, TypeNs},
|
||||||
type_ref::Mutability,
|
type_ref::Mutability,
|
||||||
AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId,
|
AsMacroCall, DefWithBodyId, FunctionId, MacroId, StructId, TraitId, VariantId,
|
||||||
};
|
};
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
attrs::collect_attrs,
|
attrs::collect_attrs,
|
||||||
|
@ -203,6 +203,10 @@ impl<'db, DB: HirDatabase> Semantics<'db, DB> {
|
||||||
self.imp.descend_node_at_offset(node, offset).filter_map(|mut it| it.find_map(N::cast))
|
self.imp.descend_node_at_offset(node, offset).filter_map(|mut it| it.find_map(N::cast))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn resolve_range_expr(&self, range_expr: &ast::RangeExpr) -> Option<Struct> {
|
||||||
|
self.imp.resolve_range_expr(range_expr).map(Struct::from)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
|
pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<Function> {
|
||||||
self.imp.resolve_await_to_poll(await_expr).map(Function::from)
|
self.imp.resolve_await_to_poll(await_expr).map(Function::from)
|
||||||
}
|
}
|
||||||
|
@ -1363,6 +1367,10 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
|
self.analyze(call.syntax())?.resolve_method_call_fallback(self.db, call)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_range_expr(&self, range_expr: &ast::RangeExpr) -> Option<StructId> {
|
||||||
|
self.analyze(range_expr.syntax())?.resolve_range_expr(self.db, range_expr)
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<FunctionId> {
|
fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option<FunctionId> {
|
||||||
self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr)
|
self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr)
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,11 @@
|
||||||
//! purely for "IDE needs".
|
//! purely for "IDE needs".
|
||||||
use std::iter::{self, once};
|
use std::iter::{self, once};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
|
||||||
|
BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static,
|
||||||
|
Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, Variant,
|
||||||
|
};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
body::{
|
body::{
|
||||||
|
@ -21,7 +26,7 @@ use hir_def::{
|
||||||
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
|
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
|
||||||
type_ref::Mutability,
|
type_ref::Mutability,
|
||||||
AsMacroCall, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, ItemContainerId,
|
AsMacroCall, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, ItemContainerId,
|
||||||
LocalFieldId, Lookup, ModuleDefId, TraitId, VariantId,
|
LocalFieldId, Lookup, ModuleDefId, StructId, TraitId, VariantId,
|
||||||
};
|
};
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
mod_path::path,
|
mod_path::path,
|
||||||
|
@ -40,18 +45,13 @@ use hir_ty::{
|
||||||
use intern::sym;
|
use intern::sym;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
use syntax::ast::{RangeItem, RangeOp};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
SyntaxKind, SyntaxNode, TextRange, TextSize,
|
SyntaxKind, SyntaxNode, TextRange, TextSize,
|
||||||
};
|
};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
use crate::{
|
|
||||||
db::HirDatabase, semantics::PathResolution, Adt, AssocItem, BindingMode, BuiltinAttr,
|
|
||||||
BuiltinType, Callable, Const, DeriveHelper, Field, Function, Local, Macro, ModuleDef, Static,
|
|
||||||
Struct, ToolModule, Trait, TraitAlias, TupleField, Type, TypeAlias, Variant,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
|
/// `SourceAnalyzer` is a convenience wrapper which exposes HIR API in terms of
|
||||||
/// original source files. It should not be used inside the HIR itself.
|
/// original source files. It should not be used inside the HIR itself.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -348,6 +348,26 @@ impl SourceAnalyzer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn resolve_range_expr(
|
||||||
|
&self,
|
||||||
|
db: &dyn HirDatabase,
|
||||||
|
range_expr: &ast::RangeExpr,
|
||||||
|
) -> Option<StructId> {
|
||||||
|
let path: ModPath = match (range_expr.op_kind()?, range_expr.start(), range_expr.end()) {
|
||||||
|
(RangeOp::Exclusive, None, None) => path![core::ops::RangeFull],
|
||||||
|
(RangeOp::Exclusive, None, Some(_)) => path![core::ops::RangeTo],
|
||||||
|
(RangeOp::Exclusive, Some(_), None) => path![core::ops::RangeFrom],
|
||||||
|
(RangeOp::Exclusive, Some(_), Some(_)) => path![core::ops::Range],
|
||||||
|
(RangeOp::Inclusive, None, Some(_)) => path![core::ops::RangeToInclusive],
|
||||||
|
(RangeOp::Inclusive, Some(_), Some(_)) => path![core::ops::RangeInclusive],
|
||||||
|
|
||||||
|
// [E0586] inclusive ranges must be bounded at the end
|
||||||
|
(RangeOp::Inclusive, None, None) => return None,
|
||||||
|
(RangeOp::Inclusive, Some(_), None) => return None,
|
||||||
|
};
|
||||||
|
self.resolver.resolve_known_struct(db.upcast(), &path)
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn resolve_await_to_poll(
|
pub(crate) fn resolve_await_to_poll(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn HirDatabase,
|
db: &dyn HirDatabase,
|
||||||
|
|
|
@ -5,14 +5,17 @@
|
||||||
|
|
||||||
// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
|
// FIXME: this badly needs rename/rewrite (matklad, 2020-02-06).
|
||||||
|
|
||||||
|
use crate::documentation::{Documentation, HasDocs};
|
||||||
|
use crate::famous_defs::FamousDefs;
|
||||||
|
use crate::RootDatabase;
|
||||||
use arrayvec::ArrayVec;
|
use arrayvec::ArrayVec;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{
|
use hir::{
|
||||||
Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
|
Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
|
||||||
Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field,
|
Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field,
|
||||||
Function, GenericParam, HasVisibility, HirDisplay, Impl, InlineAsmOperand, Label, Local, Macro,
|
Function, GenericParam, HasVisibility, HirDisplay, Impl, InlineAsmOperand, Label, Local, Macro,
|
||||||
Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait,
|
Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule,
|
||||||
TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility,
|
Trait, TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility,
|
||||||
};
|
};
|
||||||
use span::Edition;
|
use span::Edition;
|
||||||
use stdx::{format_to, impl_from};
|
use stdx::{format_to, impl_from};
|
||||||
|
@ -21,10 +24,6 @@ use syntax::{
|
||||||
match_ast, SyntaxKind, SyntaxNode, SyntaxToken,
|
match_ast, SyntaxKind, SyntaxNode, SyntaxToken,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::documentation::{Documentation, HasDocs};
|
|
||||||
use crate::famous_defs::FamousDefs;
|
|
||||||
use crate::RootDatabase;
|
|
||||||
|
|
||||||
// FIXME: a more precise name would probably be `Symbol`?
|
// FIXME: a more precise name would probably be `Symbol`?
|
||||||
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
|
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
|
||||||
pub enum Definition {
|
pub enum Definition {
|
||||||
|
@ -319,6 +318,7 @@ impl IdentClass {
|
||||||
.map(IdentClass::NameClass)
|
.map(IdentClass::NameClass)
|
||||||
.or_else(|| NameRefClass::classify_lifetime(sema, &lifetime).map(IdentClass::NameRefClass))
|
.or_else(|| NameRefClass::classify_lifetime(sema, &lifetime).map(IdentClass::NameRefClass))
|
||||||
},
|
},
|
||||||
|
ast::RangeExpr(range_expr) => OperatorClass::classify_range(sema, &range_expr).map(IdentClass::Operator),
|
||||||
ast::AwaitExpr(await_expr) => OperatorClass::classify_await(sema, &await_expr).map(IdentClass::Operator),
|
ast::AwaitExpr(await_expr) => OperatorClass::classify_await(sema, &await_expr).map(IdentClass::Operator),
|
||||||
ast::BinExpr(bin_expr) => OperatorClass::classify_bin(sema, &bin_expr).map(IdentClass::Operator),
|
ast::BinExpr(bin_expr) => OperatorClass::classify_bin(sema, &bin_expr).map(IdentClass::Operator),
|
||||||
ast::IndexExpr(index_expr) => OperatorClass::classify_index(sema, &index_expr).map(IdentClass::Operator),
|
ast::IndexExpr(index_expr) => OperatorClass::classify_index(sema, &index_expr).map(IdentClass::Operator),
|
||||||
|
@ -372,6 +372,9 @@ impl IdentClass {
|
||||||
| OperatorClass::Index(func)
|
| OperatorClass::Index(func)
|
||||||
| OperatorClass::Try(func),
|
| OperatorClass::Try(func),
|
||||||
) => res.push(Definition::Function(func)),
|
) => res.push(Definition::Function(func)),
|
||||||
|
IdentClass::Operator(OperatorClass::Range(struct0)) => {
|
||||||
|
res.push(Definition::Adt(Adt::Struct(struct0)))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
@ -546,6 +549,7 @@ impl NameClass {
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum OperatorClass {
|
pub enum OperatorClass {
|
||||||
|
Range(Struct),
|
||||||
Await(Function),
|
Await(Function),
|
||||||
Prefix(Function),
|
Prefix(Function),
|
||||||
Index(Function),
|
Index(Function),
|
||||||
|
@ -554,6 +558,13 @@ pub enum OperatorClass {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl OperatorClass {
|
impl OperatorClass {
|
||||||
|
pub fn classify_range(
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
range_expr: &ast::RangeExpr,
|
||||||
|
) -> Option<OperatorClass> {
|
||||||
|
sema.resolve_range_expr(range_expr).map(OperatorClass::Range)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn classify_await(
|
pub fn classify_await(
|
||||||
sema: &Semantics<'_, RootDatabase>,
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
await_expr: &ast::AwaitExpr,
|
await_expr: &ast::AwaitExpr,
|
||||||
|
|
|
@ -13,7 +13,6 @@ use ide_db::{
|
||||||
RootDatabase, SymbolKind,
|
RootDatabase, SymbolKind,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
|
||||||
use span::{Edition, FileId};
|
use span::{Edition, FileId};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, HasLoopBody},
|
ast::{self, HasLoopBody},
|
||||||
|
@ -418,10 +417,10 @@ fn expr_to_nav(
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::fixture;
|
||||||
use ide_db::FileRange;
|
use ide_db::FileRange;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
use syntax::SmolStr;
|
||||||
use crate::fixture;
|
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn check(ra_fixture: &str) {
|
fn check(ra_fixture: &str) {
|
||||||
|
@ -450,6 +449,90 @@ mod tests {
|
||||||
assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}")
|
assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_name(expected_name: &str, ra_fixture: &str) {
|
||||||
|
let (analysis, position, _) = fixture::annotations(ra_fixture);
|
||||||
|
let navs = analysis.goto_definition(position).unwrap().expect("no definition found").info;
|
||||||
|
assert!(navs.len() < 2, "expected single navigation target but encountered {}", navs.len());
|
||||||
|
let Some(target) = navs.into_iter().next() else {
|
||||||
|
panic!("expected single navigation target but encountered none");
|
||||||
|
};
|
||||||
|
assert_eq!(target.name, SmolStr::new_inline(expected_name));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_range() {
|
||||||
|
check_name(
|
||||||
|
"Range",
|
||||||
|
r#"
|
||||||
|
//- minicore: range
|
||||||
|
let x = 0.$0.1;
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_range_from() {
|
||||||
|
check_name(
|
||||||
|
"RangeFrom",
|
||||||
|
r#"
|
||||||
|
//- minicore: range
|
||||||
|
fn f(arr: &[i32]) -> &[i32] {
|
||||||
|
&arr[0.$0.]
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_range_inclusive() {
|
||||||
|
check_name(
|
||||||
|
"RangeInclusive",
|
||||||
|
r#"
|
||||||
|
//- minicore: range
|
||||||
|
let x = 0.$0.=1;
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_range_full() {
|
||||||
|
check_name(
|
||||||
|
"RangeFull",
|
||||||
|
r#"
|
||||||
|
//- minicore: range
|
||||||
|
fn f(arr: &[i32]) -> &[i32] {
|
||||||
|
&arr[.$0.]
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_range_to() {
|
||||||
|
check_name(
|
||||||
|
"RangeTo",
|
||||||
|
r#"
|
||||||
|
//- minicore: range
|
||||||
|
fn f(arr: &[i32]) -> &[i32] {
|
||||||
|
&arr[.$0.10]
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn goto_def_range_to_inclusive() {
|
||||||
|
check_name(
|
||||||
|
"RangeToInclusive",
|
||||||
|
r#"
|
||||||
|
//- minicore: range
|
||||||
|
fn f(arr: &[i32]) -> &[i32] {
|
||||||
|
&arr[.$0.=10]
|
||||||
|
}
|
||||||
|
"#,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn goto_def_in_included_file() {
|
fn goto_def_in_included_file() {
|
||||||
check(
|
check(
|
||||||
|
|
Loading…
Reference in a new issue