mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-11-15 17:28:09 +00:00
Merge #3366
3366: Simpilfy original_range logic r=matklad a=edwin0cheng This PR fixed another [bug](https://github.com/rust-analyzer/rust-analyzer/issues/3000#issuecomment-592474844) which incorrectly map the wrong range of `punct` in macro_call and simplify the logic a little bit by introducing an `ascend_call_token` function. Co-authored-by: Edwin Cheng <edwin0cheng@gmail.com>
This commit is contained in:
commit
93f632ca4e
2 changed files with 51 additions and 38 deletions
|
@ -8,8 +8,7 @@ use hir_def::{
|
||||||
};
|
};
|
||||||
use ra_db::{FileId, FileRange};
|
use ra_db::{FileId, FileRange};
|
||||||
use ra_syntax::{
|
use ra_syntax::{
|
||||||
algo::{find_covering_element, skip_trivia_token},
|
algo::skip_trivia_token, ast, match_ast, AstNode, Direction, SyntaxNode, SyntaxToken,
|
||||||
ast, match_ast, AstNode, Direction, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken,
|
|
||||||
TextRange, TextUnit,
|
TextRange, TextUnit,
|
||||||
};
|
};
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
|
@ -21,6 +20,7 @@ use crate::{
|
||||||
Function, HirFileId, InFile, Local, MacroDef, Module, Name, Origin, Path, PathResolution,
|
Function, HirFileId, InFile, Local, MacroDef, Module, Name, Origin, Path, PathResolution,
|
||||||
ScopeDef, StructField, Trait, Type, TypeParam, VariantDef,
|
ScopeDef, StructField, Trait, Type, TypeParam, VariantDef,
|
||||||
};
|
};
|
||||||
|
use hir_expand::ExpansionInfo;
|
||||||
use ra_prof::profile;
|
use ra_prof::profile;
|
||||||
|
|
||||||
/// Primary API to get semantic information, like types, from syntax trees.
|
/// Primary API to get semantic information, like types, from syntax trees.
|
||||||
|
@ -337,22 +337,12 @@ impl<'a, DB: HirDatabase> SemanticsScope<'a, DB> {
|
||||||
|
|
||||||
// FIXME: Change `HasSource` trait to work with `Semantics` and remove this?
|
// FIXME: Change `HasSource` trait to work with `Semantics` and remove this?
|
||||||
pub fn original_range(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> FileRange {
|
pub fn original_range(db: &impl HirDatabase, node: InFile<&SyntaxNode>) -> FileRange {
|
||||||
let mut elem: InFile<SyntaxElement> = node.map(|n| n.clone().into());
|
if let Some(range) = original_range_opt(db, node) {
|
||||||
|
|
||||||
while let Some((range, Origin::Call)) = original_range_and_origin(db, elem.as_ref()) {
|
|
||||||
let original_file = range.file_id.original_file(db);
|
let original_file = range.file_id.original_file(db);
|
||||||
|
|
||||||
if range.file_id == original_file.into() {
|
if range.file_id == original_file.into() {
|
||||||
return FileRange { file_id: original_file, range: range.value };
|
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);
|
log::error!("Fail to mapping up more for {:?}", range);
|
||||||
return FileRange { file_id: range.file_id.original_file(db), range: range.value };
|
return FileRange { file_id: range.file_id.original_file(db), range: range.value };
|
||||||
}
|
}
|
||||||
|
@ -370,19 +360,11 @@ 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() }
|
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,
|
db: &impl HirDatabase,
|
||||||
elem: InFile<&SyntaxElement>,
|
node: InFile<&SyntaxNode>,
|
||||||
) -> Option<(InFile<TextRange>, Origin)> {
|
) -> Option<InFile<TextRange>> {
|
||||||
let expansion = elem.file_id.expansion_info(db)?;
|
let expansion = node.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));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// the input node has only one token ?
|
// the input node has only one token ?
|
||||||
let single = skip_trivia_token(node.value.first_token()?, Direction::Next)?
|
let single = skip_trivia_token(node.value.first_token()?, Direction::Next)?
|
||||||
|
@ -390,23 +372,30 @@ fn original_range_and_origin(
|
||||||
|
|
||||||
Some(node.value.descendants().find_map(|it| {
|
Some(node.value.descendants().find_map(|it| {
|
||||||
let first = skip_trivia_token(it.first_token()?, Direction::Next)?;
|
let first = skip_trivia_token(it.first_token()?, Direction::Next)?;
|
||||||
|
let first = ascend_call_token(db, &expansion, node.with_value(first))?;
|
||||||
|
|
||||||
let last = skip_trivia_token(it.last_token()?, Direction::Prev)?;
|
let last = skip_trivia_token(it.last_token()?, Direction::Prev)?;
|
||||||
|
let last = ascend_call_token(db, &expansion, node.with_value(last))?;
|
||||||
|
|
||||||
if !single && first == last {
|
if (!single && first == last) || (first.file_id != last.file_id) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try to map first and last tokens of node, and, if success, return the union range of mapped tokens
|
Some(first.with_value(first.value.text_range().extend_to(&last.value.text_range())))
|
||||||
let (first, first_origin) = expansion.map_token_up(node.with_value(&first))?;
|
|
||||||
let (last, last_origin) = expansion.map_token_up(node.with_value(&last))?;
|
|
||||||
|
|
||||||
if first.file_id != last.file_id || first_origin != last_origin {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
Some((
|
|
||||||
first.with_value(first.value.text_range().extend_to(&last.value.text_range())),
|
|
||||||
first_origin,
|
|
||||||
))
|
|
||||||
})?)
|
})?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
|
|
@ -738,6 +738,30 @@ fn func(foo: i32) { if true { <|>foo; }; }
|
||||||
assert_eq!(hover_on, "bar")
|
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]
|
#[test]
|
||||||
fn test_hover_through_literal_string_in_macro() {
|
fn test_hover_through_literal_string_in_macro() {
|
||||||
let hover_on = check_hover_result(
|
let hover_on = check_hover_result(
|
||||||
|
|
Loading…
Reference in a new issue