From 3bc6e27993d8ed56d0b3c7cb409c95d0a95cfc43 Mon Sep 17 00:00:00 2001 From: duncanproctor Date: Mon, 21 Oct 2024 11:29:05 -0400 Subject: [PATCH 1/4] GotoDefinition on a Range or InclusiveRange operator will link to the struct definition --- crates/ide-db/src/famous_defs.rs | 17 +++++++++- crates/ide/src/goto_definition.rs | 55 +++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/crates/ide-db/src/famous_defs.rs b/crates/ide-db/src/famous_defs.rs index ba6e50abf6..9b4273ab10 100644 --- a/crates/ide-db/src/famous_defs.rs +++ b/crates/ide-db/src/famous_defs.rs @@ -1,7 +1,7 @@ //! See [`FamousDefs`]. use base_db::{CrateOrigin, LangCrateOrigin, SourceDatabase}; -use hir::{Crate, Enum, Function, Macro, Module, ScopeDef, Semantics, Trait}; +use hir::{Crate, Enum, Function, Macro, Module, ScopeDef, Semantics, Struct, Trait}; use crate::RootDatabase; @@ -102,6 +102,14 @@ impl FamousDefs<'_, '_> { self.find_trait("core:ops:Drop") } + pub fn core_ops_Range(&self) -> Option { + self.find_struct("core:ops:Range") + } + + pub fn core_ops_RangeInclusive(&self) -> Option { + self.find_struct("core:ops:RangeInclusive") + } + pub fn core_marker_Copy(&self) -> Option { self.find_trait("core:marker:Copy") } @@ -137,6 +145,13 @@ impl FamousDefs<'_, '_> { .flatten() } + fn find_struct(&self, path: &str) -> Option { + match self.find_def(path)? { + hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(it))) => Some(it), + _ => None, + } + } + fn find_trait(&self, path: &str) -> Option { match self.find_def(path)? { hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index c61b2ba84f..3ac7cc823e 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -5,7 +5,7 @@ use crate::{ navigation_target::{self, ToNav}, FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult, }; -use hir::{AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics}; +use hir::{Adt, AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics}; use ide_db::{ base_db::{AnchoredPath, FileLoader, SourceDatabase}, defs::{Definition, IdentClass}, @@ -13,7 +13,7 @@ use ide_db::{ RootDatabase, SymbolKind, }; use itertools::Itertools; - +use ide_db::famous_defs::FamousDefs; use span::{Edition, FileId}; use syntax::{ ast::{self, HasLoopBody}, @@ -41,6 +41,22 @@ pub(crate) fn goto_definition( ) -> Option>> { let sema = &Semantics::new(db); let file = sema.parse_guess_edition(file_id).syntax().clone(); + + if let syntax::TokenAtOffset::Single(tok) = file.token_at_offset(offset) { + if let Some(module) = sema.file_to_module_def(file_id) { + let famous_defs = FamousDefs(sema, module.krate()); + let maybe_famous_struct = match tok.kind() { + T![..] => famous_defs.core_ops_Range(), + T![..=] => famous_defs.core_ops_RangeInclusive(), + _ => None + }; + if let Some(fstruct) = maybe_famous_struct { + let target = def_to_nav(db, Definition::Adt(Adt::Struct(fstruct))); + return Some(RangeInfo::new(tok.text_range(), target)); + } + } + } + let edition = sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT); let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind { @@ -420,7 +436,7 @@ fn expr_to_nav( mod tests { use ide_db::FileRange; use itertools::Itertools; - + use syntax::SmolStr; use crate::fixture; #[track_caller] @@ -450,6 +466,39 @@ mod tests { assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}") } + + #[test] + fn goto_def_range_inclusive() { + let ra_fixture = r#" +//- minicore: range +fn f(a: usize, b: usize) { + for _ in a..$0=b { + + } +} +"#; + let (analysis, position, _) = fixture::annotations(ra_fixture); + let mut navs = analysis.goto_definition(position).unwrap().expect("no definition found").info; + let Some(target) = navs.pop() else { panic!("no target found") }; + assert_eq!(target.name, SmolStr::new_inline("RangeInclusive")); + } + + #[test] + fn goto_def_range_half_open() { + let ra_fixture = r#" +//- minicore: range +fn f(a: usize, b: usize) { + for _ in a.$0.b { + + } +} +"#; + let (analysis, position, _) = fixture::annotations(ra_fixture); + let mut navs = analysis.goto_definition(position).unwrap().expect("no definition found").info; + let Some(target) = navs.pop() else { panic!("no target found") }; + assert_eq!(target.name, SmolStr::new_inline("Range")); + } + #[test] fn goto_def_in_included_file() { check( From c7a8be110da587fc54e17a1f31c5c973d2064538 Mon Sep 17 00:00:00 2001 From: duncanproctor Date: Mon, 21 Oct 2024 20:07:07 -0400 Subject: [PATCH 2/4] Move explicit range handling out of goto_definition, use OperatorClass instead --- crates/hir/src/semantics.rs | 10 ++++++- crates/hir/src/source_analyzer.rs | 26 ++++++++++++----- crates/ide-db/src/defs.rs | 23 +++++++++++---- crates/ide-db/src/famous_defs.rs | 17 +---------- crates/ide/src/goto_definition.rs | 47 ++++++++++++++++--------------- 5 files changed, 70 insertions(+), 53 deletions(-) diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs index b27f1fbb5d..fd3172a61a 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 { + self.imp.resolve_range_expr(range_expr).map(Struct::from) + } + pub fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option { self.imp.resolve_await_to_poll(await_expr).map(Function::from) } @@ -1357,6 +1361,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 { + self.analyze(range_expr.syntax())?.resolve_range_expr(self.db, range_expr) + } + fn resolve_await_to_poll(&self, await_expr: &ast::AwaitExpr) -> Option { 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..5e05fad8c3 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,18 @@ impl SourceAnalyzer { } } + pub(crate) fn resolve_range_expr( + &self, + db: &dyn HirDatabase, + range_expr: &ast::RangeExpr, + ) -> Option { + let path = match range_expr.op_kind()? { + RangeOp::Exclusive => path![core::ops::Range], + RangeOp::Inclusive => path![core::ops::RangeInclusive], + }; + 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 { + 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-db/src/famous_defs.rs b/crates/ide-db/src/famous_defs.rs index 9b4273ab10..ba6e50abf6 100644 --- a/crates/ide-db/src/famous_defs.rs +++ b/crates/ide-db/src/famous_defs.rs @@ -1,7 +1,7 @@ //! See [`FamousDefs`]. use base_db::{CrateOrigin, LangCrateOrigin, SourceDatabase}; -use hir::{Crate, Enum, Function, Macro, Module, ScopeDef, Semantics, Struct, Trait}; +use hir::{Crate, Enum, Function, Macro, Module, ScopeDef, Semantics, Trait}; use crate::RootDatabase; @@ -102,14 +102,6 @@ impl FamousDefs<'_, '_> { self.find_trait("core:ops:Drop") } - pub fn core_ops_Range(&self) -> Option { - self.find_struct("core:ops:Range") - } - - pub fn core_ops_RangeInclusive(&self) -> Option { - self.find_struct("core:ops:RangeInclusive") - } - pub fn core_marker_Copy(&self) -> Option { self.find_trait("core:marker:Copy") } @@ -145,13 +137,6 @@ impl FamousDefs<'_, '_> { .flatten() } - fn find_struct(&self, path: &str) -> Option { - match self.find_def(path)? { - hir::ScopeDef::ModuleDef(hir::ModuleDef::Adt(hir::Adt::Struct(it))) => Some(it), - _ => None, - } - } - fn find_trait(&self, path: &str) -> Option { match self.find_def(path)? { hir::ScopeDef::ModuleDef(hir::ModuleDef::Trait(it)) => Some(it), diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 3ac7cc823e..4c965fd2d8 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -5,7 +5,7 @@ use crate::{ navigation_target::{self, ToNav}, FilePosition, NavigationTarget, RangeInfo, TryToNav, UpmappingResult, }; -use hir::{Adt, AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics}; +use hir::{AsAssocItem, AssocItem, FileRange, InFile, MacroFileIdExt, ModuleDef, Semantics}; use ide_db::{ base_db::{AnchoredPath, FileLoader, SourceDatabase}, defs::{Definition, IdentClass}, @@ -13,7 +13,6 @@ use ide_db::{ RootDatabase, SymbolKind, }; use itertools::Itertools; -use ide_db::famous_defs::FamousDefs; use span::{Edition, FileId}; use syntax::{ ast::{self, HasLoopBody}, @@ -41,22 +40,6 @@ pub(crate) fn goto_definition( ) -> Option>> { let sema = &Semantics::new(db); let file = sema.parse_guess_edition(file_id).syntax().clone(); - - if let syntax::TokenAtOffset::Single(tok) = file.token_at_offset(offset) { - if let Some(module) = sema.file_to_module_def(file_id) { - let famous_defs = FamousDefs(sema, module.krate()); - let maybe_famous_struct = match tok.kind() { - T![..] => famous_defs.core_ops_Range(), - T![..=] => famous_defs.core_ops_RangeInclusive(), - _ => None - }; - if let Some(fstruct) = maybe_famous_struct { - let target = def_to_nav(db, Definition::Adt(Adt::Struct(fstruct))); - return Some(RangeInfo::new(tok.text_range(), target)); - } - } - } - let edition = sema.attach_first_edition(file_id).map(|it| it.edition()).unwrap_or(Edition::CURRENT); let original_token = pick_best_token(file.token_at_offset(offset), |kind| match kind { @@ -434,10 +417,10 @@ fn expr_to_nav( #[cfg(test)] mod tests { + use crate::fixture; use ide_db::FileRange; use itertools::Itertools; use syntax::SmolStr; - use crate::fixture; #[track_caller] fn check(ra_fixture: &str) { @@ -466,9 +449,25 @@ mod tests { assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}") } + #[test] + fn goto_def_range_inclusive_0() { + let ra_fixture = r#" +//- minicore: range +fn f(a: usize, b: usize) { + for _ in a.$0.=b { + + } +} +"#; + let (analysis, position, _) = fixture::annotations(ra_fixture); + let mut navs = + analysis.goto_definition(position).unwrap().expect("no definition found").info; + let Some(target) = navs.pop() else { panic!("no target found") }; + assert_eq!(target.name, SmolStr::new_inline("RangeInclusive")); + } #[test] - fn goto_def_range_inclusive() { + fn goto_def_range_inclusive_1() { let ra_fixture = r#" //- minicore: range fn f(a: usize, b: usize) { @@ -478,13 +477,14 @@ fn f(a: usize, b: usize) { } "#; let (analysis, position, _) = fixture::annotations(ra_fixture); - let mut navs = analysis.goto_definition(position).unwrap().expect("no definition found").info; + let mut navs = + analysis.goto_definition(position).unwrap().expect("no definition found").info; let Some(target) = navs.pop() else { panic!("no target found") }; assert_eq!(target.name, SmolStr::new_inline("RangeInclusive")); } #[test] - fn goto_def_range_half_open() { + fn goto_def_range() { let ra_fixture = r#" //- minicore: range fn f(a: usize, b: usize) { @@ -494,7 +494,8 @@ fn f(a: usize, b: usize) { } "#; let (analysis, position, _) = fixture::annotations(ra_fixture); - let mut navs = analysis.goto_definition(position).unwrap().expect("no definition found").info; + let mut navs = + analysis.goto_definition(position).unwrap().expect("no definition found").info; let Some(target) = navs.pop() else { panic!("no target found") }; assert_eq!(target.name, SmolStr::new_inline("Range")); } From f54a863965dbf26accbafd2e6bcd3ea40209437d Mon Sep 17 00:00:00 2001 From: Duncan Proctor Date: Tue, 22 Oct 2024 03:11:23 -0400 Subject: [PATCH 3/4] goto definition on RangeFrom, RangeFull, RangeTo, and RangeToInclusive links to respective struct --- crates/hir/src/source_analyzer.rs | 14 +++- crates/ide/src/goto_definition.rs | 103 ++++++++++++++++++------------ 2 files changed, 73 insertions(+), 44 deletions(-) diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index 5e05fad8c3..e95041b923 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -353,9 +353,17 @@ impl SourceAnalyzer { db: &dyn HirDatabase, range_expr: &ast::RangeExpr, ) -> Option { - let path = match range_expr.op_kind()? { - RangeOp::Exclusive => path![core::ops::Range], - RangeOp::Inclusive => path![core::ops::RangeInclusive], + 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) } diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index 4c965fd2d8..c532e25935 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -449,55 +449,76 @@ mod tests { assert!(navs.is_empty(), "didn't expect this to resolve anywhere: {navs:?}") } - #[test] - fn goto_def_range_inclusive_0() { - let ra_fixture = r#" -//- minicore: range -fn f(a: usize, b: usize) { - for _ in a.$0.=b { - - } -} -"#; + fn check_name(expected_name: &str, ra_fixture: &str) { let (analysis, position, _) = fixture::annotations(ra_fixture); - let mut navs = - analysis.goto_definition(position).unwrap().expect("no definition found").info; - let Some(target) = navs.pop() else { panic!("no target found") }; - assert_eq!(target.name, SmolStr::new_inline("RangeInclusive")); - } - - #[test] - fn goto_def_range_inclusive_1() { - let ra_fixture = r#" -//- minicore: range -fn f(a: usize, b: usize) { - for _ in a..$0=b { - - } -} -"#; - let (analysis, position, _) = fixture::annotations(ra_fixture); - let mut navs = - analysis.goto_definition(position).unwrap().expect("no definition found").info; - let Some(target) = navs.pop() else { panic!("no target found") }; - assert_eq!(target.name, SmolStr::new_inline("RangeInclusive")); + 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() { - let ra_fixture = r#" + check_name("Range", r#" //- minicore: range -fn f(a: usize, b: usize) { - for _ in a.$0.b { - +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.] } -"#; - let (analysis, position, _) = fixture::annotations(ra_fixture); - let mut navs = - analysis.goto_definition(position).unwrap().expect("no definition found").info; - let Some(target) = navs.pop() else { panic!("no target found") }; - assert_eq!(target.name, SmolStr::new_inline("Range")); +"# + ); + } + + #[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] From 2f6923b8441c5e30934e8e7be0bed24c70427274 Mon Sep 17 00:00:00 2001 From: Duncan Proctor Date: Tue, 22 Oct 2024 03:19:47 -0400 Subject: [PATCH 4/4] tidy --- crates/hir/src/source_analyzer.rs | 2 +- crates/ide/src/goto_definition.rs | 40 ++++++++++++++++++++----------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/crates/hir/src/source_analyzer.rs b/crates/hir/src/source_analyzer.rs index e95041b923..be0e57b0b1 100644 --- a/crates/hir/src/source_analyzer.rs +++ b/crates/hir/src/source_analyzer.rs @@ -363,7 +363,7 @@ impl SourceAnalyzer { // [E0586] inclusive ranges must be bounded at the end (RangeOp::Inclusive, None, None) => return None, - (RangeOp::Inclusive, Some(_), None) => return None + (RangeOp::Inclusive, Some(_), None) => return None, }; self.resolver.resolve_known_struct(db.upcast(), &path) } diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index c532e25935..5c9ca0bc89 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs @@ -453,71 +453,83 @@ mod tests { 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"); + 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#" + check_name( + "Range", + r#" //- minicore: range let x = 0.$0.1; -"# +"#, ); } #[test] fn goto_def_range_from() { - check_name("RangeFrom", r#" + check_name( + "RangeFrom", + r#" //- minicore: range fn f(arr: &[i32]) -> &[i32] { &arr[0.$0.] } -"# +"#, ); } #[test] fn goto_def_range_inclusive() { - check_name("RangeInclusive", r#" + check_name( + "RangeInclusive", + r#" //- minicore: range let x = 0.$0.=1; -"# +"#, ); } #[test] fn goto_def_range_full() { - check_name("RangeFull", r#" + check_name( + "RangeFull", + r#" //- minicore: range fn f(arr: &[i32]) -> &[i32] { &arr[.$0.] } -"# +"#, ); } #[test] fn goto_def_range_to() { - check_name("RangeTo", r#" + 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#" + check_name( + "RangeToInclusive", + r#" //- minicore: range fn f(arr: &[i32]) -> &[i32] { &arr[.$0.=10] } -"# +"#, ); }