Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir/src/semantics.rs10
-rw-r--r--crates/hir/src/source_analyzer.rs34
-rw-r--r--crates/ide-db/src/defs.rs23
-rw-r--r--crates/ide/src/goto_definition.rs89
4 files changed, 139 insertions, 17 deletions
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index 3eac33ce99..80908dac5d 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -17,7 +17,7 @@ use hir_def::{
path::ModPath,
resolver::{self, HasResolver, Resolver, TypeNs},
type_ref::Mutability,
- AsMacroCall, DefWithBodyId, FunctionId, MacroId, TraitId, VariantId,
+ AsMacroCall, DefWithBodyId, FunctionId, MacroId, StructId, TraitId, VariantId,
};
use hir_expand::{
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))
}
+ 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> {
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)
}
+ 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> {
self.analyze(await_expr.syntax())?.resolve_await_to_poll(self.db, await_expr)
}
diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs
index 3da67ae23f..be0e57b0b1 100644
--- a/crates/hir/src/source_analyzer.rs
+++ b/crates/hir/src/source_analyzer.rs
@@ -7,6 +7,11 @@
//! purely for "IDE needs".
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 hir_def::{
body::{
@@ -21,7 +26,7 @@ use hir_def::{
resolver::{resolver_for_scope, Resolver, TypeNs, ValueNs},
type_ref::Mutability,
AsMacroCall, AssocItemId, ConstId, DefWithBodyId, FieldId, FunctionId, ItemContainerId,
- LocalFieldId, Lookup, ModuleDefId, TraitId, VariantId,
+ LocalFieldId, Lookup, ModuleDefId, StructId, TraitId, VariantId,
};
use hir_expand::{
mod_path::path,
@@ -40,18 +45,13 @@ use hir_ty::{
use intern::sym;
use itertools::Itertools;
use smallvec::SmallVec;
+use syntax::ast::{RangeItem, RangeOp};
use syntax::{
ast::{self, AstNode},
SyntaxKind, SyntaxNode, TextRange, TextSize,
};
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
/// original source files. It should not be used inside the HIR itself.
#[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(
&self,
db: &dyn HirDatabase,
diff --git a/crates/ide-db/src/defs.rs b/crates/ide-db/src/defs.rs
index 099f26eba7..a253e086f8 100644
--- a/crates/ide-db/src/defs.rs
+++ b/crates/ide-db/src/defs.rs
@@ -5,14 +5,17 @@
// 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 either::Either;
use hir::{
Adt, AsAssocItem, AsExternAssocItem, AssocItem, AttributeTemplate, BuiltinAttr, BuiltinType,
Const, Crate, DefWithBody, DeriveHelper, DocLinkDef, ExternAssocItem, ExternCrateDecl, Field,
Function, GenericParam, HasVisibility, HirDisplay, Impl, InlineAsmOperand, Label, Local, Macro,
- Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, ToolModule, Trait,
- TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility,
+ Module, ModuleDef, Name, PathResolution, Semantics, Static, StaticLifetime, Struct, ToolModule,
+ Trait, TraitAlias, TupleField, TypeAlias, Variant, VariantDef, Visibility,
};
use span::Edition;
use stdx::{format_to, impl_from};
@@ -21,10 +24,6 @@ use syntax::{
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`?
#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
pub enum Definition {
@@ -319,6 +318,7 @@ impl IdentClass {
.map(IdentClass::NameClass)
.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::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),
@@ -372,6 +372,9 @@ impl IdentClass {
| OperatorClass::Index(func)
| OperatorClass::Try(func),
) => res.push(Definition::Function(func)),
+ IdentClass::Operator(OperatorClass::Range(struct0)) => {
+ res.push(Definition::Adt(Adt::Struct(struct0)))
+ }
}
res
}
@@ -546,6 +549,7 @@ impl NameClass {
#[derive(Debug)]
pub enum OperatorClass {
+ Range(Struct),
Await(Function),
Prefix(Function),
Index(Function),
@@ -554,6 +558,13 @@ pub enum 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(
sema: &Semantics<'_, RootDatabase>,
await_expr: &ast::AwaitExpr,
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 4cbcb6ed05..ae3ef3e443 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -13,7 +13,6 @@ use ide_db::{
RootDatabase, SymbolKind,
};
use itertools::Itertools;
-
use span::{Edition, FileId};
use syntax::{
ast::{self, HasLoopBody},
@@ -418,10 +417,10 @@ fn expr_to_nav(
#[cfg(test)]
mod tests {
+ use crate::fixture;
use ide_db::FileRange;
use itertools::Itertools;
-
- use crate::fixture;
+ use syntax::SmolStr;
#[track_caller]
fn check(ra_fixture: &str) {
@@ -450,6 +449,90 @@ mod tests {
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]
fn goto_def_in_included_file() {
check(