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 c3d8ee1ae..eecccdae2 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, Name, Origin, Path, PathResolution, | 20 | Function, HirFileId, InFile, Local, MacroDef, Module, Name, Origin, Path, PathResolution, |
22 | ScopeDef, StructField, Trait, Type, TypeParam, VariantDef, | 21 | 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. |
@@ -337,22 +337,12 @@ impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> { | |||
337 | 337 | ||
338 | // FIXME: Change `HasSource` trait to work with `Semantics` and remove this? | 338 | // FIXME: Change `HasSource` trait to work with `Semantics` and remove this? |
339 | pub fn original_range(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> FileRange { | 339 | pub fn original_range(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> FileRange { |
340 | let mut elem: InFile<SyntaxElement> = node.map(|n| n.clone().into()); | 340 | if let Some(range) = original_range_opt(db, node) { |
341 | |||
342 | while let Some((range, Origin::Call)) = original_range_and_origin(db, elem.as_ref()) { | ||
343 | let original_file = range.file_id.original_file(db); | 341 | let original_file = range.file_id.original_file(db); |
344 | |||
345 | if range.file_id == original_file.into() { | 342 | if range.file_id == original_file.into() { |
346 | return FileRange { file_id: original_file, range: range.value }; | 343 | return FileRange { file_id: original_file, range: range.value }; |
347 | } | 344 | } |
348 | 345 | ||
349 | if range.file_id != elem.file_id { | ||
350 | if let Some(root) = db.parse_or_expand(range.file_id) { | ||
351 | elem = range.with_value(find_covering_element(&root, range.value)); | ||
352 | continue; | ||
353 | } | ||
354 | } | ||
355 | |||
356 | log::error!("Fail to mapping up more for {:?}", range); | 346 | log::error!("Fail to mapping up more for {:?}", range); |
357 | return FileRange { file_id: range.file_id.original_file(db), range: range.value }; | 347 | return FileRange { file_id: range.file_id.original_file(db), range: range.value }; |
358 | } | 348 | } |
@@ -370,19 +360,11 @@ pub fn original_range(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> FileR | |||
370 | FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } | 360 | FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() } |
371 | } | 361 | } |
372 | 362 | ||
373 | fn original_range_and_origin( | 363 | fn original_range_opt( |
374 | db: &impl HirDatabase, | 364 | db: &impl HirDatabase, |
375 | elem: InFile<&SyntaxElement>, | 365 | node: InFile<&SyntaxNode>, |
376 | ) -> Option<(InFile<TextRange>, Origin)> { | 366 | ) -> Option<InFile<TextRange>> { |
377 | let expansion = elem.file_id.expansion_info(db)?; | 367 | let expansion = node.file_id.expansion_info(db)?; |
378 | |||
379 | let node = match elem.as_ref().value { | ||
380 | NodeOrToken::Node(it) => elem.with_value(it), | ||
381 | NodeOrToken::Token(it) => { | ||
382 | let (tt, origin) = expansion.map_token_up(elem.with_value(it))?; | ||
383 | return Some((tt.map(|it| it.text_range()), origin)); | ||
384 | } | ||
385 | }; | ||
386 | 368 | ||
387 | // the input node has only one token ? | 369 | // the input node has only one token ? |
388 | let single = skip_trivia_token(node.value.first_token()?, Direction::Next)? | 370 | let single = skip_trivia_token(node.value.first_token()?, Direction::Next)? |
@@ -390,23 +372,30 @@ fn original_range_and_origin( | |||
390 | 372 | ||
391 | Some(node.value.descendants().find_map(|it| { | 373 | Some(node.value.descendants().find_map(|it| { |
392 | let first = skip_trivia_token(it.first_token()?, Direction::Next)?; | 374 | let first = skip_trivia_token(it.first_token()?, Direction::Next)?; |
393 | let last = skip_trivia_token(it.last_token()?, Direction::Prev)?; | 375 | let first = ascend_call_token(db, &expansion, node.with_value(first))?; |
394 | |||
395 | if !single && first == last { | ||
396 | return None; | ||
397 | } | ||
398 | 376 | ||
399 | // Try to map first and last tokens of node, and, if success, return the union range of mapped tokens | 377 | let last = skip_trivia_token(it.last_token()?, Direction::Prev)?; |
400 | let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?; | 378 | let last = ascend_call_token(db, &expansion, node.with_value(last))?; |
401 | let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?; | ||
402 | 379 | ||
403 | if first.file_id != last.file_id || first_origin != last_origin { | 380 | if (!single && first == last) || (first.file_id != last.file_id) { |
404 | return None; | 381 | return None; |
405 | } | 382 | } |
406 | 383 | ||
407 | Some(( | 384 | Some(first.with_value(first.value.text_range().extend_to(&last.value.text_range()))) |
408 | first.with_value(first.value.text_range().extend_to(&last.value.text_range())), | ||
409 | first_origin, | ||
410 | )) | ||
411 | })?) | 385 | })?) |
412 | } | 386 | } |
387 | |||
388 | fn ascend_call_token( | ||
389 | db: &impl HirDatabase, | ||
390 | expansion: &ExpansionInfo, | ||
391 | token: InFile<SyntaxToken>, | ||
392 | ) -> Option<InFile<SyntaxToken>> { | ||
393 | let (mapped, origin) = expansion.map_token_up(token.as_ref())?; | ||
394 | if origin != Origin::Call { | ||
395 | return None; | ||
396 | } | ||
397 | if let Some(info) = mapped.file_id.expansion_info(db) { | ||
398 | return ascend_call_token(db, &info, mapped); | ||
399 | } | ||
400 | Some(mapped) | ||
401 | } | ||
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs index 7ba4bfcac..cc79f1fab 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#" |