diff options
-rw-r--r-- | crates/ra_hir/src/semantics.rs | 65 | ||||
-rw-r--r-- | crates/ra_ide/src/hover.rs | 24 |
2 files changed, 51 insertions, 38 deletions
diff --git a/crates/ra_hir/src/semantics.rs b/crates/ra_hir/src/semantics.rs index 154adedb3..178d74be1 100644 --- a/crates/ra_hir/src/semantics.rs +++ b/crates/ra_hir/src/semantics.rs | |||
@@ -8,8 +8,7 @@ use hir_def::{ | |||
8 | }; | 8 | }; |
9 | use ra_db::{FileId, FileRange}; | 9 | use ra_db::{FileId, FileRange}; |
10 | use ra_syntax::{ | 10 | use ra_syntax::{ |
11 | algo::{find_covering_element, skip_trivia_token}, | 11 | algo::skip_trivia_token, ast, match_ast, AstNode, Direction, SyntaxNode, SyntaxToken, |
12 | ast, match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken, | ||
13 | TextRange, TextUnit, | 12 | TextRange, TextUnit, |
14 | }; | 13 | }; |
15 | use rustc_hash::{FxHashMap, FxHashSet}; | 14 | use rustc_hash::{FxHashMap, FxHashSet}; |
@@ -21,6 +20,7 @@ use crate::{ | |||
21 | Function, HirFileId, InFile, Local, MacroDef, Module, ModuleDef, Name, Origin, Path, | 20 | Function, HirFileId, InFile, Local, MacroDef, Module, ModuleDef, Name, Origin, Path, |
22 | PathResolution, ScopeDef, StructField, Trait, Type, TypeParam, VariantDef, | 21 | PathResolution, ScopeDef, StructField, Trait, Type, TypeParam, VariantDef, |
23 | }; | 22 | }; |
23 | use hir_expand::ExpansionInfo; | ||
24 | use ra_prof::profile; | 24 | use ra_prof::profile; |
25 | 25 | ||
26 | /// Primary API to get semantic information, like types, from syntax trees. | 26 | /// Primary API to get semantic information, like types, from syntax trees. |
@@ -341,22 +341,12 @@ impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> { | |||
341 | 341 | ||
342 | // FIXME: Change `HasSource` trait to work with `Semantics` and remove this? | 342 | // FIXME: Change `HasSource` trait to work with `Semantics` and remove this? |
343 | pub fn original_range(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> FileRange { | 343 | pub fn original_range(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> FileRange { |
344 | let mut elem: InFile<SyntaxElement> = node.map(|n| n.clone().into()); | 344 | if let Some(range) = original_range_opt(db, node) { |
345 | |||
346 | while let Some((range, Origin::Call)) = original_range_and_origin(db, elem.as_ref()) { | ||
347 | let original_file = range.file_id.original_file(db); | 345 | let original_file = range.file_id.original_file(db); |
348 | |||
349 | if range.file_id == original_file.into() { | 346 | if range.file_id == original_file.into() { |
350 | return FileRange { file_id: original_file, range: range.value }; | 347 | return FileRange { file_id: original_file, range: range.value }; |
351 | } | 348 | } |
352 | 349 | ||
353 | if range.file_id != elem.file_id { | ||
354 | if let Some(root) = db.parse_or_expand(range.file_id) { | ||
355 | elem = range.with_value(find_covering_element(&root, range.value)); | ||
356 | continue; | ||
357 | } | ||
358 | } | ||
359 | |||
360 | log::error!("Fail to mapping up more for {:?}", range); | 350 | log::error!("Fail to mapping up more for {:?}", range); |
361 | return FileRange { file_id: range.file_id.original_file(db), range: range.value }; | 351 | return FileRange { file_id: range.file_id.original_file(db), range: range.value }; |
362 | } | 352 | } |
@@ -374,19 +364,11 @@ pub fn original_range(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> FileR | |||
374 | FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } | 364 | FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } |
375 | } | 365 | } |
376 | 366 | ||
377 | fn original_range_and_origin( | 367 | fn original_range_opt( |
378 | db: &impl HirDatabase, | 368 | db: &impl HirDatabase, |
379 | elem: InFile<&SyntaxElement>, | 369 | node: InFile<&SyntaxNode>, |
380 | ) -> Option<(InFile<TextRange>, Origin)> { | 370 | ) -> Option<InFile<TextRange>> { |
381 | let expansion = elem.file_id.expansion_info(db)?; | 371 | let expansion = node.file_id.expansion_info(db)?; |
382 | |||
383 | let node = match elem.as_ref().value { | ||
384 | NodeOrToken::Node(it) => elem.with_value(it), | ||
385 | NodeOrToken::Token(it) => { | ||
386 | let (tt, origin) = expansion.map_token_up(elem.with_value(it))?; | ||
387 | return Some((tt.map(|it| it.text_range()), origin)); | ||
388 | } | ||
389 | }; | ||
390 | 372 | ||
391 | // the input node has only one token ? | 373 | // the input node has only one token ? |
392 | let single = skip_trivia_token(node.value.first_token()?, Direction::Next)? | 374 | let single = skip_trivia_token(node.value.first_token()?, Direction::Next)? |
@@ -394,23 +376,30 @@ fn original_range_and_origin( | |||
394 | 376 | ||
395 | Some(node.value.descendants().find_map(|it| { | 377 | Some(node.value.descendants().find_map(|it| { |
396 | let first = skip_trivia_token(it.first_token()?, Direction::Next)?; | 378 | let first = skip_trivia_token(it.first_token()?, Direction::Next)?; |
397 | let last = skip_trivia_token(it.last_token()?, Direction::Prev)?; | 379 | let first = ascend_call_token(db, &expansion, node.with_value(first))?; |
398 | |||
399 | if !single && first == last { | ||
400 | return None; | ||
401 | } | ||
402 | 380 | ||
403 | // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens | 381 | let last = skip_trivia_token(it.last_token()?, Direction::Prev)?; |
404 | let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?; | 382 | let last = ascend_call_token(db, &expansion, node.with_value(last))?; |
405 | let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?; | ||
406 | 383 | ||
407 | if first.file_id != last.file_id || first_origin != last_origin { | 384 | if (!single && first == last) || (first.file_id != last.file_id) { |
408 | return None; | 385 | return None; |
409 | } | 386 | } |
410 | 387 | ||
411 | Some(( | 388 | Some(first.with_value(first.value.text_range().extend_to(&last.value.text_range()))) |
412 | first.with_value(first.value.text_range().extend_to(&last.value.text_range())), | ||
413 | first_origin, | ||
414 | )) | ||
415 | })?) | 389 | })?) |
416 | } | 390 | } |
391 | |||
392 | fn ascend_call_token( | ||
393 | db: &impl HirDatabase, | ||
394 | expansion: &ExpansionInfo, | ||
395 | token: InFile<SyntaxToken>, | ||
396 | ) -> Option<InFile<SyntaxToken>> { | ||
397 | let (mapped, origin) = expansion.map_token_up(token.as_ref())?; | ||
398 | if origin != Origin::Call { | ||
399 | return None; | ||
400 | } | ||
401 | if let Some(info) = mapped.file_id.expansion_info(db) { | ||
402 | return ascend_call_token(db, &info, mapped); | ||
403 | } | ||
404 | Some(mapped) | ||
405 | } | ||
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 1e4fcdefb..5073bb1cf 100644 --- a/crates/ra_ide/src/hover.rs +++ b/crates/ra_ide/src/hover.rs | |||
@@ -739,6 +739,30 @@ fn func(foo: i32) { if true { <|>foo; }; } | |||
739 | } | 739 | } |
740 | 740 | ||
741 | #[test] | 741 | #[test] |
742 | fn test_hover_through_func_in_macro_recursive() { | ||
743 | let hover_on = check_hover_result( | ||
744 | " | ||
745 | //- /lib.rs | ||
746 | macro_rules! id_deep { | ||
747 | ($($tt:tt)*) => { $($tt)* } | ||
748 | } | ||
749 | macro_rules! id { | ||
750 | ($($tt:tt)*) => { id_deep!($($tt)*) } | ||
751 | } | ||
752 | fn bar() -> u32 { | ||
753 | 0 | ||
754 | } | ||
755 | fn foo() { | ||
756 | let a = id!([0u32, bar(<|>)] ); | ||
757 | } | ||
758 | ", | ||
759 | &["u32"], | ||
760 | ); | ||
761 | |||
762 | assert_eq!(hover_on, "bar()") | ||
763 | } | ||
764 | |||
765 | #[test] | ||
742 | fn test_hover_through_literal_string_in_macro() { | 766 | fn test_hover_through_literal_string_in_macro() { |
743 | let hover_on = check_hover_result( | 767 | let hover_on = check_hover_result( |
744 | r#" | 768 | r#" |