Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 27 additions & 38 deletions crates/ra_hir/src/semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ use hir_def::{
};
use ra_db::{FileId, FileRange};
use ra_syntax::{
algo::{find_covering_element, skip_trivia_token},
ast, match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
algo::skip_trivia_token, ast, match_ast, AstNode, Direction, SyntaxNode, SyntaxToken,
TextRange, TextUnit,
};
use rustc_hash::{FxHashMap, FxHashSet};
Expand All @@ -21,6 +20,7 @@ use crate::{
Function, HirFileId, InFile, Local, MacroDef, Module, Name, Origin, Path, PathResolution,
ScopeDef, StructField, Trait, Type, TypeParam, VariantDef,
};
use hir_expand::ExpansionInfo;
use ra_prof::profile;

/// Primary API to get semantic information, like types, from syntax trees.
Expand Down Expand Up @@ -337,22 +337,12 @@ impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> {

// FIXME: Change `HasSource` trait to work with `Semantics` and remove this?
pub fn original_range(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> FileRange {
let mut elem: InFile<SyntaxElement> = node.map(|n| n.clone().into());

while let Some((range, Origin::Call)) = original_range_and_origin(db, elem.as_ref()) {
if let Some(range) = original_range_opt(db, node) {
let original_file = range.file_id.original_file(db);

if range.file_id == original_file.into() {
return FileRange { file_id: original_file, range: range.value };
}

if range.file_id != elem.file_id {
if let Some(root) = db.parse_or_expand(range.file_id) {
elem = range.with_value(find_covering_element(&root, range.value));
continue;
}
}

log::error!("Fail to mapping up more for {:?}", range);
return FileRange { file_id: range.file_id.original_file(db), range: range.value };
}
Expand All @@ -370,43 +360,42 @@ pub fn original_range(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> FileR
FileRange { file_id: node.file_id.original_file(db), range: node.value.text_range() }
}

fn original_range_and_origin(
fn original_range_opt(
db: &impl HirDatabase,
elem: InFile<&SyntaxElement>,
) -> Option<(InFile<TextRange>, Origin)> {
let expansion = elem.file_id.expansion_info(db)?;

let node = match elem.as_ref().value {
NodeOrToken::Node(it) => elem.with_value(it),
NodeOrToken::Token(it) => {
let (tt, origin) = expansion.map_token_up(elem.with_value(it))?;
return Some((tt.map(|it| it.text_range()), origin));
}
};
node: InFile<&SyntaxNode>,
) -> Option<InFile<TextRange>> {
let expansion = node.file_id.expansion_info(db)?;

// the input node has only one token ?
let single = skip_trivia_token(node.value.first_token()?, Direction::Next)?
== skip_trivia_token(node.value.last_token()?, Direction::Prev)?;

Some(node.value.descendants().find_map(|it| {
let first = skip_trivia_token(it.first_token()?, Direction::Next)?;
let last = skip_trivia_token(it.last_token()?, Direction::Prev)?;

if !single && first == last {
return None;
}
let first = ascend_call_token(db, &expansion, node.with_value(first))?;

// Try to map first and last tokens of node, and, if success, return the union range of mapped tokens
let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?;
let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?;
let last = skip_trivia_token(it.last_token()?, Direction::Prev)?;
let last = ascend_call_token(db, &expansion, node.with_value(last))?;

if first.file_id != last.file_id || first_origin != last_origin {
if (!single && first == last) || (first.file_id != last.file_id) {
return None;
}

Some((
first.with_value(first.value.text_range().extend_to(&last.value.text_range())),
first_origin,
))
Some(first.with_value(first.value.text_range().extend_to(&last.value.text_range())))
})?)
}

fn ascend_call_token(
db: &impl HirDatabase,
expansion: &ExpansionInfo,
token: InFile<SyntaxToken>,
) -> Option<InFile<SyntaxToken>> {
let (mapped, origin) = expansion.map_token_up(token.as_ref())?;
if origin != Origin::Call {
return None;
}
if let Some(info) = mapped.file_id.expansion_info(db) {
return ascend_call_token(db, &info, mapped);
}
Some(mapped)
}
24 changes: 24 additions & 0 deletions crates/ra_ide/src/hover.rs
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,30 @@ fn func(foo: i32) { if true { <|>foo; }; }
assert_eq!(hover_on, "bar")
}

#[test]
fn test_hover_through_func_in_macro_recursive() {
let hover_on = check_hover_result(
"
//- /lib.rs
macro_rules! id_deep {
($($tt:tt)*) => { $($tt)* }
}
macro_rules! id {
($($tt:tt)*) => { id_deep!($($tt)*) }
}
fn bar() -> u32 {
0
}
fn foo() {
let a = id!([0u32, bar(<|>)] );
}
",
&["u32"],
);

assert_eq!(hover_on, "bar()")
}

#[test]
fn test_hover_through_literal_string_in_macro() {
let hover_on = check_hover_result(
Expand Down