aboutsummaryrefslogtreecommitdiff
path: root/crates/ide/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide/src')
-rw-r--r--crates/ide/src/call_hierarchy.rs11
-rw-r--r--crates/ide/src/display.rs1
-rw-r--r--crates/ide/src/display/navigation_target.rs14
-rw-r--r--crates/ide/src/doc_links.rs2
-rw-r--r--crates/ide/src/extend_selection.rs13
-rw-r--r--crates/ide/src/goto_definition.rs4
-rw-r--r--crates/ide/src/goto_type_definition.rs2
-rw-r--r--crates/ide/src/hover.rs186
-rw-r--r--crates/ide/src/join_lines.rs44
-rw-r--r--crates/ide/src/matching_brace.rs2
-rw-r--r--crates/ide/src/parent_module.rs79
-rw-r--r--crates/ide/src/references.rs7
-rw-r--r--crates/ide/src/runnables.rs3
-rw-r--r--crates/ide/src/syntax_highlighting.rs19
-rw-r--r--crates/ide/src/syntax_highlighting/format.rs2
-rw-r--r--crates/ide/src/syntax_tree.rs2
-rw-r--r--crates/ide/src/typing.rs2
17 files changed, 243 insertions, 150 deletions
diff --git a/crates/ide/src/call_hierarchy.rs b/crates/ide/src/call_hierarchy.rs
index b848945d7..96021f677 100644
--- a/crates/ide/src/call_hierarchy.rs
+++ b/crates/ide/src/call_hierarchy.rs
@@ -53,10 +53,8 @@ pub(crate) fn incoming_calls(db: &RootDatabase, position: FilePosition) -> Optio
53 for (r_range, _) in references { 53 for (r_range, _) in references {
54 let token = file.token_at_offset(r_range.start()).next()?; 54 let token = file.token_at_offset(r_range.start()).next()?;
55 let token = sema.descend_into_macros(token); 55 let token = sema.descend_into_macros(token);
56 let syntax = token.parent();
57
58 // This target is the containing function 56 // This target is the containing function
59 if let Some(nav) = syntax.ancestors().find_map(|node| { 57 if let Some(nav) = token.ancestors().find_map(|node| {
60 let fn_ = ast::Fn::cast(node)?; 58 let fn_ = ast::Fn::cast(node)?;
61 let def = sema.to_def(&fn_)?; 59 let def = sema.to_def(&fn_)?;
62 def.try_to_nav(sema.db) 60 def.try_to_nav(sema.db)
@@ -77,12 +75,13 @@ pub(crate) fn outgoing_calls(db: &RootDatabase, position: FilePosition) -> Optio
77 let file = file.syntax(); 75 let file = file.syntax();
78 let token = file.token_at_offset(position.offset).next()?; 76 let token = file.token_at_offset(position.offset).next()?;
79 let token = sema.descend_into_macros(token); 77 let token = sema.descend_into_macros(token);
80 let syntax = token.parent();
81 78
82 let mut calls = CallLocations::default(); 79 let mut calls = CallLocations::default();
83 80
84 syntax 81 token
85 .descendants() 82 .parent()
83 .into_iter()
84 .flat_map(|it| it.descendants())
86 .filter_map(|node| FnCallNode::with_node_exact(&node)) 85 .filter_map(|node| FnCallNode::with_node_exact(&node))
87 .filter_map(|call_node| { 86 .filter_map(|call_node| {
88 let name_ref = call_node.name_ref()?; 87 let name_ref = call_node.name_ref()?;
diff --git a/crates/ide/src/display.rs b/crates/ide/src/display.rs
index bae9e40df..1f7b665c0 100644
--- a/crates/ide/src/display.rs
+++ b/crates/ide/src/display.rs
@@ -5,6 +5,5 @@ pub(crate) mod navigation_target;
5mod short_label; 5mod short_label;
6 6
7pub(crate) use navigation_target::{ToNav, TryToNav}; 7pub(crate) use navigation_target::{ToNav, TryToNav};
8pub(crate) use short_label::ShortLabel;
9 8
10pub(crate) use syntax::display::{function_declaration, macro_label}; 9pub(crate) use syntax::display::{function_declaration, macro_label};
diff --git a/crates/ide/src/display/navigation_target.rs b/crates/ide/src/display/navigation_target.rs
index 198243466..69c3751a1 100644
--- a/crates/ide/src/display/navigation_target.rs
+++ b/crates/ide/src/display/navigation_target.rs
@@ -3,7 +3,9 @@
3use std::fmt; 3use std::fmt;
4 4
5use either::Either; 5use either::Either;
6use hir::{AssocItem, Documentation, FieldSource, HasAttrs, HasSource, InFile, ModuleSource}; 6use hir::{
7 AssocItem, Documentation, FieldSource, HasAttrs, HasSource, HirDisplay, InFile, ModuleSource,
8};
7use ide_db::{ 9use ide_db::{
8 base_db::{FileId, FileRange, SourceDatabase}, 10 base_db::{FileId, FileRange, SourceDatabase},
9 symbol_index::FileSymbolKind, 11 symbol_index::FileSymbolKind,
@@ -98,7 +100,7 @@ impl NavigationTarget {
98 SymbolKind::Module, 100 SymbolKind::Module,
99 ); 101 );
100 res.docs = module.attrs(db).docs(); 102 res.docs = module.attrs(db).docs();
101 res.description = src.value.short_label(); 103 res.description = Some(module.display(db).to_string());
102 return res; 104 return res;
103 } 105 }
104 module.to_nav(db) 106 module.to_nav(db)
@@ -251,8 +253,8 @@ impl ToNavFromAst for hir::Trait {
251 253
252impl<D> TryToNav for D 254impl<D> TryToNav for D
253where 255where
254 D: HasSource + ToNavFromAst + Copy + HasAttrs, 256 D: HasSource + ToNavFromAst + Copy + HasAttrs + HirDisplay,
255 D::Ast: ast::NameOwner + ShortLabel, 257 D::Ast: ast::NameOwner,
256{ 258{
257 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> { 259 fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
258 let src = self.source(db)?; 260 let src = self.source(db)?;
@@ -262,7 +264,7 @@ where
262 D::KIND, 264 D::KIND,
263 ); 265 );
264 res.docs = self.docs(db); 266 res.docs = self.docs(db);
265 res.description = src.value.short_label(); 267 res.description = Some(self.display(db).to_string());
266 Some(res) 268 Some(res)
267 } 269 }
268} 270}
@@ -317,7 +319,7 @@ impl TryToNav for hir::Field {
317 let mut res = 319 let mut res =
318 NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field); 320 NavigationTarget::from_named(db, src.with_value(it), SymbolKind::Field);
319 res.docs = self.docs(db); 321 res.docs = self.docs(db);
320 res.description = it.short_label(); 322 res.description = Some(self.display(db).to_string());
321 res 323 res
322 } 324 }
323 FieldSource::Pos(it) => { 325 FieldSource::Pos(it) => {
diff --git a/crates/ide/src/doc_links.rs b/crates/ide/src/doc_links.rs
index 7bdd3cca3..461e11060 100644
--- a/crates/ide/src/doc_links.rs
+++ b/crates/ide/src/doc_links.rs
@@ -279,7 +279,7 @@ pub(crate) fn external_docs(
279 let token = pick_best(file.token_at_offset(position.offset))?; 279 let token = pick_best(file.token_at_offset(position.offset))?;
280 let token = sema.descend_into_macros(token); 280 let token = sema.descend_into_macros(token);
281 281
282 let node = token.parent(); 282 let node = token.parent()?;
283 let definition = match_ast! { 283 let definition = match_ast! {
284 match node { 284 match node {
285 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)), 285 ast::NameRef(name_ref) => NameRefClass::classify(&sema, &name_ref).map(|d| d.referenced(sema.db)),
diff --git a/crates/ide/src/extend_selection.rs b/crates/ide/src/extend_selection.rs
index b540d04fe..e187243cb 100644
--- a/crates/ide/src/extend_selection.rs
+++ b/crates/ide/src/extend_selection.rs
@@ -88,7 +88,7 @@ fn try_extend_selection(
88 return Some(range); 88 return Some(range);
89 } 89 }
90 } 90 }
91 token.parent() 91 token.parent()?
92 } 92 }
93 NodeOrToken::Node(node) => node, 93 NodeOrToken::Node(node) => node,
94 }; 94 };
@@ -142,7 +142,8 @@ fn extend_tokens_from_range(
142 let extended = { 142 let extended = {
143 let fst_expanded = sema.descend_into_macros(first_token.clone()); 143 let fst_expanded = sema.descend_into_macros(first_token.clone());
144 let lst_expanded = sema.descend_into_macros(last_token.clone()); 144 let lst_expanded = sema.descend_into_macros(last_token.clone());
145 let mut lca = algo::least_common_ancestor(&fst_expanded.parent(), &lst_expanded.parent())?; 145 let mut lca =
146 algo::least_common_ancestor(&fst_expanded.parent()?, &lst_expanded.parent()?)?;
146 lca = shallowest_node(&lca); 147 lca = shallowest_node(&lca);
147 if lca.first_token() == Some(fst_expanded) && lca.last_token() == Some(lst_expanded) { 148 if lca.first_token() == Some(fst_expanded) && lca.last_token() == Some(lst_expanded) {
148 lca = lca.parent()?; 149 lca = lca.parent()?;
@@ -151,9 +152,13 @@ fn extend_tokens_from_range(
151 }; 152 };
152 153
153 // Compute parent node range 154 // Compute parent node range
154 let validate = |token: &SyntaxToken| { 155 let validate = |token: &SyntaxToken| -> bool {
155 let expanded = sema.descend_into_macros(token.clone()); 156 let expanded = sema.descend_into_macros(token.clone());
156 algo::least_common_ancestor(&extended, &expanded.parent()).as_ref() == Some(&extended) 157 let parent = match expanded.parent() {
158 Some(it) => it,
159 None => return false,
160 };
161 algo::least_common_ancestor(&extended, &parent).as_ref() == Some(&extended)
157 }; 162 };
158 163
159 // Find the first and last text range under expanded parent 164 // Find the first and last text range under expanded parent
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index e8f31e4b1..6986477a5 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -30,7 +30,7 @@ pub(crate) fn goto_definition(
30 let file = sema.parse(position.file_id).syntax().clone(); 30 let file = sema.parse(position.file_id).syntax().clone();
31 let original_token = pick_best(file.token_at_offset(position.offset))?; 31 let original_token = pick_best(file.token_at_offset(position.offset))?;
32 let token = sema.descend_into_macros(original_token.clone()); 32 let token = sema.descend_into_macros(original_token.clone());
33 let parent = token.parent(); 33 let parent = token.parent()?;
34 if let Some(comment) = ast::Comment::cast(token) { 34 if let Some(comment) = ast::Comment::cast(token) {
35 let nav = def_for_doc_comment(&sema, position, &comment)?.try_to_nav(db)?; 35 let nav = def_for_doc_comment(&sema, position, &comment)?.try_to_nav(db)?;
36 return Some(RangeInfo::new(original_token.text_range(), vec![nav])); 36 return Some(RangeInfo::new(original_token.text_range(), vec![nav]));
@@ -63,7 +63,7 @@ fn def_for_doc_comment(
63 position: FilePosition, 63 position: FilePosition,
64 doc_comment: &ast::Comment, 64 doc_comment: &ast::Comment,
65) -> Option<hir::ModuleDef> { 65) -> Option<hir::ModuleDef> {
66 let parent = doc_comment.syntax().parent(); 66 let parent = doc_comment.syntax().parent()?;
67 let (link, ns) = extract_positioned_link_from_comment(position, doc_comment)?; 67 let (link, ns) = extract_positioned_link_from_comment(position, doc_comment)?;
68 68
69 let def = doc_owner_to_def(sema, parent)?; 69 let def = doc_owner_to_def(sema, parent)?;
diff --git a/crates/ide/src/goto_type_definition.rs b/crates/ide/src/goto_type_definition.rs
index 369a59820..2d38cb112 100644
--- a/crates/ide/src/goto_type_definition.rs
+++ b/crates/ide/src/goto_type_definition.rs
@@ -22,7 +22,7 @@ pub(crate) fn goto_type_definition(
22 let token: SyntaxToken = pick_best(file.syntax().token_at_offset(position.offset))?; 22 let token: SyntaxToken = pick_best(file.syntax().token_at_offset(position.offset))?;
23 let token: SyntaxToken = sema.descend_into_macros(token); 23 let token: SyntaxToken = sema.descend_into_macros(token);
24 24
25 let (ty, node) = sema.ancestors_with_macros(token.parent()).find_map(|node| { 25 let (ty, node) = sema.token_ancestors_with_macros(token).find_map(|node| {
26 let ty = match_ast! { 26 let ty = match_ast! {
27 match node { 27 match node {
28 ast::Expr(it) => sema.type_of_expr(&it)?, 28 ast::Expr(it) => sema.type_of_expr(&it)?,
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index ea45086ce..8d45b4875 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -1,7 +1,7 @@
1use either::Either; 1use either::Either;
2use hir::{ 2use hir::{
3 Adt, AsAssocItem, AssocItemContainer, FieldSource, GenericParam, HasAttrs, HasSource, 3 Adt, AsAssocItem, AssocItemContainer, GenericParam, HasAttrs, HasSource, HirDisplay, Module,
4 HirDisplay, Module, ModuleDef, ModuleSource, Semantics, 4 ModuleDef, Semantics,
5}; 5};
6use ide_db::{ 6use ide_db::{
7 base_db::SourceDatabase, 7 base_db::SourceDatabase,
@@ -14,7 +14,7 @@ use stdx::format_to;
14use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T}; 14use syntax::{ast, match_ast, AstNode, SyntaxKind::*, SyntaxToken, TokenAtOffset, T};
15 15
16use crate::{ 16use crate::{
17 display::{macro_label, ShortLabel, TryToNav}, 17 display::{macro_label, TryToNav},
18 doc_links::{remove_links, rewrite_links}, 18 doc_links::{remove_links, rewrite_links},
19 markdown_remove::remove_markdown, 19 markdown_remove::remove_markdown,
20 markup::Markup, 20 markup::Markup,
@@ -92,7 +92,7 @@ pub(crate) fn hover(
92 92
93 let mut res = HoverResult::default(); 93 let mut res = HoverResult::default();
94 94
95 let node = token.parent(); 95 let node = token.parent()?;
96 let definition = match_ast! { 96 let definition = match_ast! {
97 match node { 97 match node {
98 // we don't use NameClass::referenced_or_defined here as we do not want to resolve 98 // we don't use NameClass::referenced_or_defined here as we do not want to resolve
@@ -335,34 +335,18 @@ fn hover_for_definition(
335 let label = macro_label(&it.source(db)?.value); 335 let label = macro_label(&it.source(db)?.value);
336 from_def_source_labeled(db, it, Some(label), mod_path) 336 from_def_source_labeled(db, it, Some(label), mod_path)
337 } 337 }
338 Definition::Field(def) => { 338 Definition::Field(def) => from_hir_fmt(db, def, mod_path),
339 let src = def.source(db)?.value;
340 if let FieldSource::Named(it) = src {
341 from_def_source_labeled(db, def, it.short_label(), mod_path)
342 } else {
343 None
344 }
345 }
346 Definition::ModuleDef(it) => match it { 339 Definition::ModuleDef(it) => match it {
347 ModuleDef::Module(it) => from_def_source_labeled( 340 ModuleDef::Module(it) => from_hir_fmt(db, it, mod_path),
348 db, 341 ModuleDef::Function(it) => from_hir_fmt(db, it, mod_path),
349 it, 342 ModuleDef::Adt(Adt::Struct(it)) => from_hir_fmt(db, it, mod_path),
350 match it.definition_source(db).value { 343 ModuleDef::Adt(Adt::Union(it)) => from_hir_fmt(db, it, mod_path),
351 ModuleSource::Module(it) => it.short_label(), 344 ModuleDef::Adt(Adt::Enum(it)) => from_hir_fmt(db, it, mod_path),
352 ModuleSource::SourceFile(it) => it.short_label(), 345 ModuleDef::Variant(it) => from_hir_fmt(db, it, mod_path),
353 ModuleSource::BlockExpr(it) => it.short_label(), 346 ModuleDef::Const(it) => from_hir_fmt(db, it, mod_path),
354 }, 347 ModuleDef::Static(it) => from_hir_fmt(db, it, mod_path),
355 mod_path, 348 ModuleDef::Trait(it) => from_hir_fmt(db, it, mod_path),
356 ), 349 ModuleDef::TypeAlias(it) => from_hir_fmt(db, it, mod_path),
357 ModuleDef::Function(it) => from_def_source(db, it, mod_path),
358 ModuleDef::Adt(Adt::Struct(it)) => from_def_source(db, it, mod_path),
359 ModuleDef::Adt(Adt::Union(it)) => from_def_source(db, it, mod_path),
360 ModuleDef::Adt(Adt::Enum(it)) => from_def_source(db, it, mod_path),
361 ModuleDef::Variant(it) => from_def_source(db, it, mod_path),
362 ModuleDef::Const(it) => from_def_source(db, it, mod_path),
363 ModuleDef::Static(it) => from_def_source(db, it, mod_path),
364 ModuleDef::Trait(it) => from_def_source(db, it, mod_path),
365 ModuleDef::TypeAlias(it) => from_def_source(db, it, mod_path),
366 ModuleDef::BuiltinType(it) => famous_defs 350 ModuleDef::BuiltinType(it) => famous_defs
367 .and_then(|fd| hover_for_builtin(fd, it)) 351 .and_then(|fd| hover_for_builtin(fd, it))
368 .or_else(|| Some(Markup::fenced_block(&it.name()))), 352 .or_else(|| Some(Markup::fenced_block(&it.name()))),
@@ -370,26 +354,25 @@ fn hover_for_definition(
370 Definition::Local(it) => hover_for_local(it, db), 354 Definition::Local(it) => hover_for_local(it, db),
371 Definition::SelfType(impl_def) => { 355 Definition::SelfType(impl_def) => {
372 impl_def.target_ty(db).as_adt().and_then(|adt| match adt { 356 impl_def.target_ty(db).as_adt().and_then(|adt| match adt {
373 Adt::Struct(it) => from_def_source(db, it, mod_path), 357 Adt::Struct(it) => from_hir_fmt(db, it, mod_path),
374 Adt::Union(it) => from_def_source(db, it, mod_path), 358 Adt::Union(it) => from_hir_fmt(db, it, mod_path),
375 Adt::Enum(it) => from_def_source(db, it, mod_path), 359 Adt::Enum(it) => from_hir_fmt(db, it, mod_path),
376 }) 360 })
377 } 361 }
378 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))), 362 Definition::Label(it) => Some(Markup::fenced_block(&it.name(db))),
379 Definition::GenericParam(it) => match it { 363 Definition::GenericParam(it) => match it {
380 GenericParam::TypeParam(it) => Some(Markup::fenced_block(&it.display(db))), 364 GenericParam::TypeParam(it) => Some(Markup::fenced_block(&it.display(db))),
381 GenericParam::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))), 365 GenericParam::LifetimeParam(it) => Some(Markup::fenced_block(&it.name(db))),
382 GenericParam::ConstParam(it) => from_def_source(db, it, None), 366 GenericParam::ConstParam(it) => Some(Markup::fenced_block(&it.display(db))),
383 }, 367 },
384 }; 368 };
385 369
386 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup> 370 fn from_hir_fmt<D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<Markup>
387 where 371 where
388 D: HasSource<Ast = A> + HasAttrs + Copy, 372 D: HasAttrs + HirDisplay,
389 A: ShortLabel,
390 { 373 {
391 let short_label = def.source(db)?.value.short_label(); 374 let label = def.display(db).to_string();
392 from_def_source_labeled(db, def, short_label, mod_path) 375 from_def_source_labeled(db, def, Some(label), mod_path)
393 } 376 }
394 377
395 fn from_def_source_labeled<D>( 378 fn from_def_source_labeled<D>(
@@ -438,7 +421,7 @@ fn hover_for_keyword(
438 if !token.kind().is_keyword() { 421 if !token.kind().is_keyword() {
439 return None; 422 return None;
440 } 423 }
441 let famous_defs = FamousDefs(&sema, sema.scope(&token.parent()).krate()); 424 let famous_defs = FamousDefs(&sema, sema.scope(&token.parent()?).krate());
442 // std exposes {}_keyword modules with docstrings on the root to document keywords 425 // std exposes {}_keyword modules with docstrings on the root to document keywords
443 let keyword_mod = format!("{}_keyword", token.text()); 426 let keyword_mod = format!("{}_keyword", token.text());
444 let doc_owner = find_std_module(&famous_defs, &keyword_mod)?; 427 let doc_owner = find_std_module(&famous_defs, &keyword_mod)?;
@@ -670,7 +653,9 @@ fn main() { let foo_test = fo$0o(); }
670 ``` 653 ```
671 654
672 ```rust 655 ```rust
673 pub fn foo<'a, T: AsRef<str>>(b: &'a T) -> &'a str 656 pub fn foo<'a, T>(b: &'a T) -> &'a str
657 where
658 T: AsRef<str>,
674 ``` 659 ```
675 "#]], 660 "#]],
676 ); 661 );
@@ -878,7 +863,7 @@ fn main() { So$0me(12); }
878 ``` 863 ```
879 864
880 ```rust 865 ```rust
881 Some 866 Some(T)
882 ``` 867 ```
883 "#]], 868 "#]],
884 ); 869 );
@@ -944,7 +929,7 @@ fn main() {
944 ``` 929 ```
945 930
946 ```rust 931 ```rust
947 Some 932 Some(T)
948 ``` 933 ```
949 934
950 --- 935 ---
@@ -1441,13 +1426,14 @@ fn bar() { fo$0o(); }
1441 ``` 1426 ```
1442 "#]], 1427 "#]],
1443 ); 1428 );
1429 // Top level `pub(crate)` will be displayed as no visibility.
1444 check( 1430 check(
1445 r#"pub(crate) async unsafe extern "C" fn foo$0() {}"#, 1431 r#"mod m { pub(crate) async unsafe extern "C" fn foo$0() {} }"#,
1446 expect![[r#" 1432 expect![[r#"
1447 *foo* 1433 *foo*
1448 1434
1449 ```rust 1435 ```rust
1450 test 1436 test::m
1451 ``` 1437 ```
1452 1438
1453 ```rust 1439 ```rust
@@ -1489,11 +1475,18 @@ extern crate st$0d;
1489//! abc123 1475//! abc123
1490 "#, 1476 "#,
1491 expect![[r#" 1477 expect![[r#"
1492 *std* 1478 *std*
1493 Standard library for this test 1479
1480 ```rust
1481 extern crate std
1482 ```
1494 1483
1495 Printed? 1484 ---
1496 abc123 1485
1486 Standard library for this test
1487
1488 Printed?
1489 abc123
1497 "#]], 1490 "#]],
1498 ); 1491 );
1499 check( 1492 check(
@@ -1507,11 +1500,18 @@ extern crate std as ab$0c;
1507//! abc123 1500//! abc123
1508 "#, 1501 "#,
1509 expect![[r#" 1502 expect![[r#"
1510 *abc* 1503 *abc*
1511 Standard library for this test 1504
1505 ```rust
1506 extern crate std
1507 ```
1508
1509 ---
1512 1510
1513 Printed? 1511 Standard library for this test
1514 abc123 1512
1513 Printed?
1514 abc123
1515 "#]], 1515 "#]],
1516 ); 1516 );
1517 } 1517 }
@@ -2021,7 +2021,7 @@ enum E {
2021 ``` 2021 ```
2022 2022
2023 ```rust 2023 ```rust
2024 V 2024 V { field: i32 }
2025 ``` 2025 ```
2026 2026
2027 --- 2027 ---
@@ -2417,7 +2417,7 @@ fn main() { let s$0t = S{ f1:Arg(0) }; }
2417 focus_range: 24..25, 2417 focus_range: 24..25,
2418 name: "S", 2418 name: "S",
2419 kind: Struct, 2419 kind: Struct,
2420 description: "struct S", 2420 description: "struct S<T>",
2421 }, 2421 },
2422 }, 2422 },
2423 HoverGotoTypeData { 2423 HoverGotoTypeData {
@@ -2463,7 +2463,7 @@ fn main() { let s$0t = S{ f1: S{ f1: Arg(0) } }; }
2463 focus_range: 24..25, 2463 focus_range: 24..25,
2464 name: "S", 2464 name: "S",
2465 kind: Struct, 2465 kind: Struct,
2466 description: "struct S", 2466 description: "struct S<T>",
2467 }, 2467 },
2468 }, 2468 },
2469 HoverGotoTypeData { 2469 HoverGotoTypeData {
@@ -2605,7 +2605,7 @@ fn main() { let s$0t = foo(); }
2605 focus_range: 6..9, 2605 focus_range: 6..9,
2606 name: "Foo", 2606 name: "Foo",
2607 kind: Trait, 2607 kind: Trait,
2608 description: "trait Foo", 2608 description: "trait Foo<T>",
2609 }, 2609 },
2610 }, 2610 },
2611 HoverGotoTypeData { 2611 HoverGotoTypeData {
@@ -2702,7 +2702,7 @@ fn main() { let s$0t = foo(); }
2702 focus_range: 6..9, 2702 focus_range: 6..9,
2703 name: "Foo", 2703 name: "Foo",
2704 kind: Trait, 2704 kind: Trait,
2705 description: "trait Foo", 2705 description: "trait Foo<T>",
2706 }, 2706 },
2707 }, 2707 },
2708 HoverGotoTypeData { 2708 HoverGotoTypeData {
@@ -2715,7 +2715,7 @@ fn main() { let s$0t = foo(); }
2715 focus_range: 22..25, 2715 focus_range: 22..25,
2716 name: "Bar", 2716 name: "Bar",
2717 kind: Trait, 2717 kind: Trait,
2718 description: "trait Bar", 2718 description: "trait Bar<T>",
2719 }, 2719 },
2720 }, 2720 },
2721 HoverGotoTypeData { 2721 HoverGotoTypeData {
@@ -2819,7 +2819,7 @@ fn foo(ar$0g: &impl Foo + Bar<S>) {}
2819 focus_range: 19..22, 2819 focus_range: 19..22,
2820 name: "Bar", 2820 name: "Bar",
2821 kind: Trait, 2821 kind: Trait,
2822 description: "trait Bar", 2822 description: "trait Bar<T>",
2823 }, 2823 },
2824 }, 2824 },
2825 HoverGotoTypeData { 2825 HoverGotoTypeData {
@@ -2916,7 +2916,7 @@ fn foo(ar$0g: &impl Foo<S>) {}
2916 focus_range: 6..9, 2916 focus_range: 6..9,
2917 name: "Foo", 2917 name: "Foo",
2918 kind: Trait, 2918 kind: Trait,
2919 description: "trait Foo", 2919 description: "trait Foo<T>",
2920 }, 2920 },
2921 }, 2921 },
2922 HoverGotoTypeData { 2922 HoverGotoTypeData {
@@ -2966,7 +2966,7 @@ fn main() { let s$0t = foo(); }
2966 focus_range: 49..50, 2966 focus_range: 49..50,
2967 name: "B", 2967 name: "B",
2968 kind: Struct, 2968 kind: Struct,
2969 description: "struct B", 2969 description: "struct B<T>",
2970 }, 2970 },
2971 }, 2971 },
2972 HoverGotoTypeData { 2972 HoverGotoTypeData {
@@ -3042,7 +3042,7 @@ fn foo(ar$0g: &dyn Foo<S>) {}
3042 focus_range: 6..9, 3042 focus_range: 6..9,
3043 name: "Foo", 3043 name: "Foo",
3044 kind: Trait, 3044 kind: Trait,
3045 description: "trait Foo", 3045 description: "trait Foo<T>",
3046 }, 3046 },
3047 }, 3047 },
3048 HoverGotoTypeData { 3048 HoverGotoTypeData {
@@ -3090,7 +3090,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
3090 focus_range: 6..15, 3090 focus_range: 6..15,
3091 name: "ImplTrait", 3091 name: "ImplTrait",
3092 kind: Trait, 3092 kind: Trait,
3093 description: "trait ImplTrait", 3093 description: "trait ImplTrait<T>",
3094 }, 3094 },
3095 }, 3095 },
3096 HoverGotoTypeData { 3096 HoverGotoTypeData {
@@ -3103,7 +3103,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
3103 focus_range: 50..51, 3103 focus_range: 50..51,
3104 name: "B", 3104 name: "B",
3105 kind: Struct, 3105 kind: Struct,
3106 description: "struct B", 3106 description: "struct B<T>",
3107 }, 3107 },
3108 }, 3108 },
3109 HoverGotoTypeData { 3109 HoverGotoTypeData {
@@ -3116,7 +3116,7 @@ fn foo(a$0rg: &impl ImplTrait<B<dyn DynTrait<B<S>>>>) {}
3116 focus_range: 28..36, 3116 focus_range: 28..36,
3117 name: "DynTrait", 3117 name: "DynTrait",
3118 kind: Trait, 3118 kind: Trait,
3119 description: "trait DynTrait", 3119 description: "trait DynTrait<T>",
3120 }, 3120 },
3121 }, 3121 },
3122 HoverGotoTypeData { 3122 HoverGotoTypeData {
@@ -3582,6 +3582,17 @@ mod foo$0;
3582"#, 3582"#,
3583 expect![[r#" 3583 expect![[r#"
3584 *foo* 3584 *foo*
3585
3586 ```rust
3587 test
3588 ```
3589
3590 ```rust
3591 mod foo
3592 ```
3593
3594 ---
3595
3585 For the horde! 3596 For the horde!
3586 "#]], 3597 "#]],
3587 ); 3598 );
@@ -3606,7 +3617,7 @@ use foo::bar::{self$0};
3606 ``` 3617 ```
3607 3618
3608 ```rust 3619 ```rust
3609 pub mod bar 3620 mod bar
3610 ``` 3621 ```
3611 3622
3612 --- 3623 ---
@@ -3657,4 +3668,43 @@ cosnt _: &str$0 = ""; }"#;
3657 "#]], 3668 "#]],
3658 ); 3669 );
3659 } 3670 }
3671
3672 #[test]
3673 fn hover_macro_expanded_function() {
3674 check(
3675 r#"
3676struct S<'a, T>(&'a T);
3677trait Clone {}
3678macro_rules! foo {
3679 () => {
3680 fn bar<'t, T: Clone + 't>(s: &mut S<'t, T>, t: u32) -> *mut u32 where
3681 't: 't + 't,
3682 for<'a> T: Clone + 'a
3683 { 0 as _ }
3684 };
3685}
3686
3687foo!();
3688
3689fn main() {
3690 bar$0;
3691}
3692"#,
3693 expect![[r#"
3694 *bar*
3695
3696 ```rust
3697 test
3698 ```
3699
3700 ```rust
3701 fn bar<'t, T>(s: &mut S<'t, T>, t: u32) -> *mut u32
3702 where
3703 T: Clone + 't,
3704 't: 't + 't,
3705 for<'a> T: Clone + 'a,
3706 ```
3707 "#]],
3708 )
3709 }
3660} 3710}
diff --git a/crates/ide/src/join_lines.rs b/crates/ide/src/join_lines.rs
index d571ed559..4b25135cd 100644
--- a/crates/ide/src/join_lines.rs
+++ b/crates/ide/src/join_lines.rs
@@ -32,29 +32,35 @@ pub(crate) fn join_lines(file: &SourceFile, range: TextRange) -> TextEdit {
32 range 32 range
33 }; 33 };
34 34
35 let node = match file.syntax().covering_element(range) {
36 NodeOrToken::Node(node) => node,
37 NodeOrToken::Token(token) => token.parent(),
38 };
39 let mut edit = TextEdit::builder(); 35 let mut edit = TextEdit::builder();
40 for token in node.descendants_with_tokens().filter_map(|it| it.into_token()) { 36 match file.syntax().covering_element(range) {
41 let range = match range.intersect(token.text_range()) { 37 NodeOrToken::Node(node) => {
42 Some(range) => range, 38 for token in node.descendants_with_tokens().filter_map(|it| it.into_token()) {
43 None => continue, 39 remove_newlines(&mut edit, &token, range)
44 } - token.text_range().start();
45 let text = token.text();
46 for (pos, _) in text[range].bytes().enumerate().filter(|&(_, b)| b == b'\n') {
47 let pos: TextSize = (pos as u32).into();
48 let offset = token.text_range().start() + range.start() + pos;
49 if !edit.invalidates_offset(offset) {
50 remove_newline(&mut edit, &token, offset);
51 } 40 }
52 } 41 }
53 } 42 NodeOrToken::Token(token) => remove_newlines(&mut edit, &token, range),
54 43 };
55 edit.finish() 44 edit.finish()
56} 45}
57 46
47fn remove_newlines(edit: &mut TextEditBuilder, token: &SyntaxToken, range: TextRange) {
48 let intersection = match range.intersect(token.text_range()) {
49 Some(range) => range,
50 None => return,
51 };
52
53 let range = intersection - token.text_range().start();
54 let text = token.text();
55 for (pos, _) in text[range].bytes().enumerate().filter(|&(_, b)| b == b'\n') {
56 let pos: TextSize = (pos as u32).into();
57 let offset = token.text_range().start() + range.start() + pos;
58 if !edit.invalidates_offset(offset) {
59 remove_newline(edit, &token, offset);
60 }
61 }
62}
63
58fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextSize) { 64fn remove_newline(edit: &mut TextEditBuilder, token: &SyntaxToken, offset: TextSize) {
59 if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 { 65 if token.kind() != WHITESPACE || token.text().bytes().filter(|&b| b == b'\n').count() != 1 {
60 let mut string_open_quote = false; 66 let mut string_open_quote = false;
@@ -148,7 +154,7 @@ fn has_comma_after(node: &SyntaxNode) -> bool {
148} 154}
149 155
150fn join_single_expr_block(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Option<()> { 156fn join_single_expr_block(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Option<()> {
151 let block_expr = ast::BlockExpr::cast(token.parent())?; 157 let block_expr = ast::BlockExpr::cast(token.parent()?)?;
152 if !block_expr.is_standalone() { 158 if !block_expr.is_standalone() {
153 return None; 159 return None;
154 } 160 }
@@ -170,7 +176,7 @@ fn join_single_expr_block(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Op
170} 176}
171 177
172fn join_single_use_tree(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Option<()> { 178fn join_single_use_tree(edit: &mut TextEditBuilder, token: &SyntaxToken) -> Option<()> {
173 let use_tree_list = ast::UseTreeList::cast(token.parent())?; 179 let use_tree_list = ast::UseTreeList::cast(token.parent()?)?;
174 let (tree,) = use_tree_list.use_trees().collect_tuple()?; 180 let (tree,) = use_tree_list.use_trees().collect_tuple()?;
175 edit.replace(use_tree_list.syntax().text_range(), tree.syntax().text().to_string()); 181 edit.replace(use_tree_list.syntax().text_range(), tree.syntax().text().to_string());
176 Some(()) 182 Some(())
diff --git a/crates/ide/src/matching_brace.rs b/crates/ide/src/matching_brace.rs
index 000c412d9..4241a6dac 100644
--- a/crates/ide/src/matching_brace.rs
+++ b/crates/ide/src/matching_brace.rs
@@ -25,7 +25,7 @@ pub(crate) fn matching_brace(file: &SourceFile, offset: TextSize) -> Option<Text
25 Some((node, idx)) 25 Some((node, idx))
26 }) 26 })
27 .next()?; 27 .next()?;
28 let parent = brace_token.parent(); 28 let parent = brace_token.parent()?;
29 if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) { 29 if brace_token.kind() == T![|] && !ast::ParamList::can_cast(parent.kind()) {
30 cov_mark::hit!(pipes_not_braces); 30 cov_mark::hit!(pipes_not_braces);
31 return None; 31 return None;
diff --git a/crates/ide/src/parent_module.rs b/crates/ide/src/parent_module.rs
index 03d71b380..22b0d6ecb 100644
--- a/crates/ide/src/parent_module.rs
+++ b/crates/ide/src/parent_module.rs
@@ -1,6 +1,7 @@
1use hir::Semantics; 1use hir::Semantics;
2use ide_db::base_db::{CrateId, FileId, FilePosition}; 2use ide_db::base_db::{CrateId, FileId, FilePosition};
3use ide_db::RootDatabase; 3use ide_db::RootDatabase;
4use itertools::Itertools;
4use syntax::{ 5use syntax::{
5 algo::find_node_at_offset, 6 algo::find_node_at_offset,
6 ast::{self, AstNode}, 7 ast::{self, AstNode},
@@ -18,8 +19,7 @@ use crate::NavigationTarget;
18// | VS Code | **Rust Analyzer: Locate parent module** 19// | VS Code | **Rust Analyzer: Locate parent module**
19// |=== 20// |===
20 21
21/// This returns `Vec` because a module may be included from several places. We 22/// This returns `Vec` because a module may be included from several places.
22/// don't handle this case yet though, so the Vec has length at most one.
23pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> { 23pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<NavigationTarget> {
24 let sema = Semantics::new(db); 24 let sema = Semantics::new(db);
25 let source_file = sema.parse(position.file_id); 25 let source_file = sema.parse(position.file_id);
@@ -37,27 +37,23 @@ pub(crate) fn parent_module(db: &RootDatabase, position: FilePosition) -> Vec<Na
37 } 37 }
38 } 38 }
39 39
40 let module = match module { 40 match module {
41 Some(module) => sema.to_def(&module), 41 Some(module) => sema
42 None => sema.to_module_def(position.file_id), 42 .to_def(&module)
43 }; 43 .into_iter()
44 let module = match module { 44 .map(|module| NavigationTarget::from_module_to_decl(db, module))
45 None => return Vec::new(), 45 .collect(),
46 Some(it) => it, 46 None => sema
47 }; 47 .to_module_defs(position.file_id)
48 let nav = NavigationTarget::from_module_to_decl(db, module); 48 .map(|module| NavigationTarget::from_module_to_decl(db, module))
49 vec![nav] 49 .collect(),
50 }
50} 51}
51 52
52/// Returns `Vec` for the same reason as `parent_module` 53/// Returns `Vec` for the same reason as `parent_module`
53pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> { 54pub(crate) fn crate_for(db: &RootDatabase, file_id: FileId) -> Vec<CrateId> {
54 let sema = Semantics::new(db); 55 let sema = Semantics::new(db);
55 let module = match sema.to_module_def(file_id) { 56 sema.to_module_defs(file_id).map(|module| module.krate().into()).unique().collect()
56 Some(it) => it,
57 None => return Vec::new(),
58 };
59 let krate = module.krate();
60 vec![krate.into()]
61} 57}
62 58
63#[cfg(test)] 59#[cfg(test)]
@@ -67,11 +63,13 @@ mod tests {
67 use crate::fixture; 63 use crate::fixture;
68 64
69 fn check(ra_fixture: &str) { 65 fn check(ra_fixture: &str) {
70 let (analysis, position, expected) = fixture::nav_target_annotation(ra_fixture); 66 let (analysis, position, expected) = fixture::annotations(ra_fixture);
71 let mut navs = analysis.parent_module(position).unwrap(); 67 let navs = analysis.parent_module(position).unwrap();
72 assert_eq!(navs.len(), 1); 68 let navs = navs
73 let nav = navs.pop().unwrap(); 69 .iter()
74 assert_eq!(expected, FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() }); 70 .map(|nav| FileRange { file_id: nav.file_id, range: nav.focus_or_full_range() })
71 .collect::<Vec<_>>();
72 assert_eq!(expected.into_iter().map(|(fr, _)| fr).collect::<Vec<_>>(), navs);
75 } 73 }
76 74
77 #[test] 75 #[test]
@@ -120,15 +118,46 @@ mod foo {
120 } 118 }
121 119
122 #[test] 120 #[test]
123 fn test_resolve_crate_root() { 121 fn test_resolve_multi_parent_module() {
124 let (analysis, file_id) = fixture::file( 122 check(
125 r#" 123 r#"
126//- /main.rs 124//- /main.rs
127mod foo; 125mod foo;
126 //^^^
127#[path = "foo.rs"]
128mod bar;
129 //^^^
130//- /foo.rs
131$0
132"#,
133 );
134 }
135
136 #[test]
137 fn test_resolve_crate_root() {
138 let (analysis, file_id) = fixture::file(
139 r#"
128//- /foo.rs 140//- /foo.rs
129$0 141$0
142//- /main.rs
143mod foo;
130"#, 144"#,
131 ); 145 );
132 assert_eq!(analysis.crate_for(file_id).unwrap().len(), 1); 146 assert_eq!(analysis.crate_for(file_id).unwrap().len(), 1);
133 } 147 }
148
149 #[test]
150 fn test_resolve_multi_parent_crate() {
151 let (analysis, file_id) = fixture::file(
152 r#"
153//- /baz.rs
154$0
155//- /foo.rs crate:foo
156mod baz;
157//- /bar.rs crate:bar
158mod baz;
159"#,
160 );
161 assert_eq!(analysis.crate_for(file_id).unwrap().len(), 2);
162 }
134} 163}
diff --git a/crates/ide/src/references.rs b/crates/ide/src/references.rs
index e10a3ccf7..379674530 100644
--- a/crates/ide/src/references.rs
+++ b/crates/ide/src/references.rs
@@ -148,14 +148,15 @@ fn decl_access(def: &Definition, syntax: &SyntaxNode, range: TextRange) -> Optio
148 148
149fn get_name_of_item_declaration(syntax: &SyntaxNode, position: FilePosition) -> Option<ast::Name> { 149fn get_name_of_item_declaration(syntax: &SyntaxNode, position: FilePosition) -> Option<ast::Name> {
150 let token = syntax.token_at_offset(position.offset).right_biased()?; 150 let token = syntax.token_at_offset(position.offset).right_biased()?;
151 let token_parent = token.parent()?;
151 let kind = token.kind(); 152 let kind = token.kind();
152 if kind == T![;] { 153 if kind == T![;] {
153 ast::Struct::cast(token.parent()) 154 ast::Struct::cast(token_parent)
154 .filter(|struct_| struct_.field_list().is_none()) 155 .filter(|struct_| struct_.field_list().is_none())
155 .and_then(|struct_| struct_.name()) 156 .and_then(|struct_| struct_.name())
156 } else if kind == T!['{'] { 157 } else if kind == T!['{'] {
157 match_ast! { 158 match_ast! {
158 match (token.parent()) { 159 match token_parent {
159 ast::RecordFieldList(rfl) => match_ast! { 160 ast::RecordFieldList(rfl) => match_ast! {
160 match (rfl.syntax().parent()?) { 161 match (rfl.syntax().parent()?) {
161 ast::Variant(it) => it.name(), 162 ast::Variant(it) => it.name(),
@@ -169,7 +170,7 @@ fn get_name_of_item_declaration(syntax: &SyntaxNode, position: FilePosition) ->
169 } 170 }
170 } 171 }
171 } else if kind == T!['('] { 172 } else if kind == T!['('] {
172 let tfl = ast::TupleFieldList::cast(token.parent())?; 173 let tfl = ast::TupleFieldList::cast(token_parent)?;
173 match_ast! { 174 match_ast! {
174 match (tfl.syntax().parent()?) { 175 match (tfl.syntax().parent()?) {
175 ast::Variant(it) => it.name(), 176 ast::Variant(it) => it.name(),
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs
index 0c7a8fbf8..397e2126b 100644
--- a/crates/ide/src/runnables.rs
+++ b/crates/ide/src/runnables.rs
@@ -167,8 +167,7 @@ fn find_related_tests(
167 let functions = refs.iter().filter_map(|(range, _)| { 167 let functions = refs.iter().filter_map(|(range, _)| {
168 let token = file.token_at_offset(range.start()).next()?; 168 let token = file.token_at_offset(range.start()).next()?;
169 let token = sema.descend_into_macros(token); 169 let token = sema.descend_into_macros(token);
170 let syntax = token.parent(); 170 token.ancestors().find_map(ast::Fn::cast)
171 syntax.ancestors().find_map(ast::Fn::cast)
172 }); 171 });
173 172
174 for fn_def in functions { 173 for fn_def in functions {
diff --git a/crates/ide/src/syntax_highlighting.rs b/crates/ide/src/syntax_highlighting.rs
index 9bed329d8..870146d24 100644
--- a/crates/ide/src/syntax_highlighting.rs
+++ b/crates/ide/src/syntax_highlighting.rs
@@ -64,7 +64,7 @@ pub(crate) fn highlight(
64 Some(range) => { 64 Some(range) => {
65 let node = match source_file.syntax().covering_element(range) { 65 let node = match source_file.syntax().covering_element(range) {
66 NodeOrToken::Node(it) => it, 66 NodeOrToken::Node(it) => it,
67 NodeOrToken::Token(it) => it.parent(), 67 NodeOrToken::Token(it) => it.parent().unwrap(),
68 }; 68 };
69 (node, range) 69 (node, range)
70 } 70 }
@@ -167,16 +167,19 @@ fn traverse(
167 let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT { 167 let element_to_highlight = if current_macro_call.is_some() && element.kind() != COMMENT {
168 // Inside a macro -- expand it first 168 // Inside a macro -- expand it first
169 let token = match element.clone().into_token() { 169 let token = match element.clone().into_token() {
170 Some(it) if it.parent().kind() == TOKEN_TREE => it, 170 Some(it) if it.parent().map_or(false, |it| it.kind() == TOKEN_TREE) => it,
171 _ => continue, 171 _ => continue,
172 }; 172 };
173 let token = sema.descend_into_macros(token.clone()); 173 let token = sema.descend_into_macros(token.clone());
174 let parent = token.parent(); 174 match token.parent() {
175 175 Some(parent) => {
176 // We only care Name and Name_ref 176 // We only care Name and Name_ref
177 match (token.kind(), parent.kind()) { 177 match (token.kind(), parent.kind()) {
178 (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(), 178 (IDENT, NAME) | (IDENT, NAME_REF) => parent.into(),
179 _ => token.into(), 179 _ => token.into(),
180 }
181 }
182 None => token.into(),
180 } 183 }
181 } else { 184 } else {
182 element.clone() 185 element.clone()
diff --git a/crates/ide/src/syntax_highlighting/format.rs b/crates/ide/src/syntax_highlighting/format.rs
index 8c67a0863..e503abc93 100644
--- a/crates/ide/src/syntax_highlighting/format.rs
+++ b/crates/ide/src/syntax_highlighting/format.rs
@@ -28,7 +28,7 @@ pub(super) fn highlight_format_string(
28} 28}
29 29
30fn is_format_string(string: &ast::String) -> Option<()> { 30fn is_format_string(string: &ast::String) -> Option<()> {
31 let parent = string.syntax().parent(); 31 let parent = string.syntax().parent()?;
32 32
33 let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?; 33 let name = parent.parent().and_then(ast::MacroCall::cast)?.path()?.segment()?.name_ref()?;
34 if !matches!(name.text(), "format_args" | "format_args_nl") { 34 if !matches!(name.text(), "format_args" | "format_args_nl") {
diff --git a/crates/ide/src/syntax_tree.rs b/crates/ide/src/syntax_tree.rs
index f979ba434..8979de528 100644
--- a/crates/ide/src/syntax_tree.rs
+++ b/crates/ide/src/syntax_tree.rs
@@ -27,7 +27,7 @@ pub(crate) fn syntax_tree(
27 if let Some(tree) = syntax_tree_for_string(&token, text_range) { 27 if let Some(tree) = syntax_tree_for_string(&token, text_range) {
28 return tree; 28 return tree;
29 } 29 }
30 token.parent() 30 token.parent().unwrap()
31 } 31 }
32 }; 32 };
33 33
diff --git a/crates/ide/src/typing.rs b/crates/ide/src/typing.rs
index a718faf63..e10b7d98e 100644
--- a/crates/ide/src/typing.rs
+++ b/crates/ide/src/typing.rs
@@ -108,7 +108,7 @@ fn on_dot_typed(file: &SourceFile, offset: TextSize) -> Option<TextEdit> {
108 }; 108 };
109 let current_indent_len = TextSize::of(current_indent); 109 let current_indent_len = TextSize::of(current_indent);
110 110
111 let parent = whitespace.syntax().parent(); 111 let parent = whitespace.syntax().parent()?;
112 // Make sure dot is a part of call chain 112 // Make sure dot is a part of call chain
113 if !matches!(parent.kind(), FIELD_EXPR | METHOD_CALL_EXPR) { 113 if !matches!(parent.kind(), FIELD_EXPR | METHOD_CALL_EXPR) {
114 return None; 114 return None;