aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/ra_hir/src/semantics.rs65
-rw-r--r--crates/ra_ide/src/hover.rs24
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};
9use ra_db::{FileId, FileRange}; 9use ra_db::{FileId, FileRange};
10use ra_syntax::{ 10use 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};
15use rustc_hash::{FxHashMap, FxHashSet}; 14use 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};
23use hir_expand::ExpansionInfo;
24use ra_prof::profile; 24use 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?
339pub fn original_range(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> FileRange { 339pub 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
373fn original_range_and_origin( 363fn 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
388fn 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#"