mirror of
https://github.com/rust-lang/rust-analyzer
synced 2025-01-13 05:38:46 +00:00
Auto merge of #16819 - Veykril:span-upmapping, r=Veykril
internal: Improve rooted upmapping cc https://github.com/rust-lang/rust-analyzer/issues/16235
This commit is contained in:
commit
a2e274142f
24 changed files with 231 additions and 153 deletions
|
@ -61,15 +61,16 @@ use std::ops::Deref;
|
|||
|
||||
use base_db::{CrateId, Edition, FileId};
|
||||
use hir_expand::{
|
||||
name::Name, proc_macro::ProcMacroKind, HirFileId, InFile, MacroCallId, MacroDefId,
|
||||
name::Name, proc_macro::ProcMacroKind, ErasedAstId, HirFileId, InFile, MacroCallId, MacroDefId,
|
||||
};
|
||||
use itertools::Itertools;
|
||||
use la_arena::Arena;
|
||||
use rustc_hash::{FxHashMap, FxHashSet};
|
||||
use span::FileAstId;
|
||||
use span::{FileAstId, ROOT_ERASED_FILE_AST_ID};
|
||||
use stdx::format_to;
|
||||
use syntax::{ast, SmolStr};
|
||||
use triomphe::Arc;
|
||||
use tt::TextRange;
|
||||
|
||||
use crate::{
|
||||
db::DefDatabase,
|
||||
|
@ -677,6 +678,25 @@ impl ModuleData {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn definition_source_range(&self, db: &dyn DefDatabase) -> InFile<TextRange> {
|
||||
match &self.origin {
|
||||
&ModuleOrigin::File { definition, .. } | &ModuleOrigin::CrateRoot { definition } => {
|
||||
InFile::new(
|
||||
definition.into(),
|
||||
ErasedAstId::new(definition.into(), ROOT_ERASED_FILE_AST_ID)
|
||||
.to_range(db.upcast()),
|
||||
)
|
||||
}
|
||||
&ModuleOrigin::Inline { definition, definition_tree_id } => InFile::new(
|
||||
definition_tree_id.file_id(),
|
||||
AstId::new(definition_tree_id.file_id(), definition).to_range(db.upcast()),
|
||||
),
|
||||
ModuleOrigin::BlockExpr { block, .. } => {
|
||||
InFile::new(block.file_id, block.to_range(db.upcast()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns a node which declares this module, either a `mod foo;` or a `mod foo {}`.
|
||||
/// `None` for the crate root or block.
|
||||
pub fn declaration_source(&self, db: &dyn DefDatabase) -> Option<InFile<ast::Module>> {
|
||||
|
@ -684,6 +704,13 @@ impl ModuleData {
|
|||
let value = decl.to_node(db.upcast());
|
||||
Some(InFile { file_id: decl.file_id, value })
|
||||
}
|
||||
|
||||
/// Returns the range which declares this module, either a `mod foo;` or a `mod foo {}`.
|
||||
/// `None` for the crate root or block.
|
||||
pub fn declaration_source_range(&self, db: &dyn DefDatabase) -> Option<InFile<TextRange>> {
|
||||
let decl = self.origin.declaration()?;
|
||||
Some(InFile { file_id: decl.file_id, value: decl.to_range(db.upcast()) })
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||
|
|
|
@ -10,7 +10,7 @@ use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange,
|
|||
|
||||
use crate::{
|
||||
db::{self, ExpandDatabase},
|
||||
map_node_range_up, span_for_offset, MacroFileIdExt,
|
||||
map_node_range_up, map_node_range_up_rooted, span_for_offset, MacroFileIdExt,
|
||||
};
|
||||
|
||||
/// `InFile<T>` stores a value of `T` inside a particular file/syntax tree.
|
||||
|
@ -38,6 +38,9 @@ impl<N: AstIdNode> AstId<N> {
|
|||
pub fn to_node(&self, db: &dyn ExpandDatabase) -> N {
|
||||
self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))
|
||||
}
|
||||
pub fn to_range(&self, db: &dyn ExpandDatabase) -> TextRange {
|
||||
self.to_ptr(db).text_range()
|
||||
}
|
||||
pub fn to_in_file_node(&self, db: &dyn ExpandDatabase) -> crate::InFile<N> {
|
||||
crate::InFile::new(self.file_id, self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)))
|
||||
}
|
||||
|
@ -49,6 +52,9 @@ impl<N: AstIdNode> AstId<N> {
|
|||
pub type ErasedAstId = crate::InFile<ErasedFileAstId>;
|
||||
|
||||
impl ErasedAstId {
|
||||
pub fn to_range(&self, db: &dyn ExpandDatabase) -> TextRange {
|
||||
self.to_ptr(db).text_range()
|
||||
}
|
||||
pub fn to_ptr(&self, db: &dyn ExpandDatabase) -> SyntaxNodePtr {
|
||||
db.ast_id_map(self.file_id).get_erased(self.value)
|
||||
}
|
||||
|
@ -173,24 +179,8 @@ impl InFile<&SyntaxNode> {
|
|||
///
|
||||
/// For attributes and derives, this will point back to the attribute only.
|
||||
/// For the entire item use [`InFile::original_file_range_full`].
|
||||
pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange {
|
||||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
if let Some((res, ctxt)) =
|
||||
map_node_range_up(db, &db.expansion_span_map(mac_file), self.value.text_range())
|
||||
{
|
||||
// FIXME: Figure out an API that makes proper use of ctx, this only exists to
|
||||
// keep pre-token map rewrite behaviour.
|
||||
if ctxt.is_root() {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
// Fall back to whole macro call.
|
||||
let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
|
||||
loc.kind.original_call_range(db)
|
||||
}
|
||||
}
|
||||
pub fn original_file_range_rooted(self, db: &dyn db::ExpandDatabase) -> FileRange {
|
||||
self.map(SyntaxNode::text_range).original_node_file_range_rooted(db)
|
||||
}
|
||||
|
||||
/// Falls back to the macro call range if the node cannot be mapped up fully.
|
||||
|
@ -198,23 +188,7 @@ impl InFile<&SyntaxNode> {
|
|||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
) -> FileRange {
|
||||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value.text_range() },
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
if let Some((res, ctxt)) =
|
||||
map_node_range_up(db, &db.expansion_span_map(mac_file), self.value.text_range())
|
||||
{
|
||||
// FIXME: Figure out an API that makes proper use of ctx, this only exists to
|
||||
// keep pre-token map rewrite behaviour.
|
||||
if ctxt.is_root() {
|
||||
return res;
|
||||
}
|
||||
}
|
||||
// Fall back to whole macro call.
|
||||
let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
|
||||
loc.kind.original_call_range_with_body(db)
|
||||
}
|
||||
}
|
||||
self.map(SyntaxNode::text_range).original_node_file_range_with_macro_call_body(db)
|
||||
}
|
||||
|
||||
/// Attempts to map the syntax node back up its macro calls.
|
||||
|
@ -222,17 +196,10 @@ impl InFile<&SyntaxNode> {
|
|||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
) -> Option<(FileRange, SyntaxContextId)> {
|
||||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => {
|
||||
Some((FileRange { file_id, range: self.value.text_range() }, SyntaxContextId::ROOT))
|
||||
}
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
map_node_range_up(db, &db.expansion_span_map(mac_file), self.value.text_range())
|
||||
}
|
||||
}
|
||||
self.map(SyntaxNode::text_range).original_node_file_range_opt(db)
|
||||
}
|
||||
|
||||
pub fn original_syntax_node(
|
||||
pub fn original_syntax_node_rooted(
|
||||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
) -> Option<InRealFile<SyntaxNode>> {
|
||||
|
@ -242,25 +209,21 @@ impl InFile<&SyntaxNode> {
|
|||
HirFileIdRepr::FileId(file_id) => {
|
||||
return Some(InRealFile { file_id, value: self.value.clone() })
|
||||
}
|
||||
HirFileIdRepr::MacroFile(m) => m,
|
||||
HirFileIdRepr::MacroFile(m) if m.is_attr_macro(db) => m,
|
||||
_ => return None,
|
||||
};
|
||||
if !file_id.is_attr_macro(db) {
|
||||
return None;
|
||||
}
|
||||
|
||||
let (FileRange { file_id, range }, ctx) =
|
||||
map_node_range_up(db, &db.expansion_span_map(file_id), self.value.text_range())?;
|
||||
let FileRange { file_id, range } =
|
||||
map_node_range_up_rooted(db, &db.expansion_span_map(file_id), self.value.text_range())?;
|
||||
|
||||
// FIXME: Figure out an API that makes proper use of ctx, this only exists to
|
||||
// keep pre-token map rewrite behavior.
|
||||
if !ctx.is_root() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let anc = db.parse(file_id).syntax_node().covering_element(range);
|
||||
let kind = self.value.kind();
|
||||
// FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes?
|
||||
let value = anc.ancestors().find(|it| it.kind() == kind)?;
|
||||
let value = db
|
||||
.parse(file_id)
|
||||
.syntax_node()
|
||||
.covering_element(range)
|
||||
.ancestors()
|
||||
.take_while(|it| it.text_range() == range)
|
||||
.find(|it| it.kind() == kind)?;
|
||||
Some(InRealFile::new(file_id, value))
|
||||
}
|
||||
}
|
||||
|
@ -355,8 +318,8 @@ impl InFile<TextRange> {
|
|||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value },
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
match map_node_range_up(db, &db.expansion_span_map(mac_file), self.value) {
|
||||
Some((it, SyntaxContextId::ROOT)) => it,
|
||||
match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) {
|
||||
Some(it) => it,
|
||||
_ => {
|
||||
let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
|
||||
loc.kind.original_call_range(db)
|
||||
|
@ -366,6 +329,24 @@ impl InFile<TextRange> {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn original_node_file_range_with_macro_call_body(
|
||||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
) -> FileRange {
|
||||
match self.file_id.repr() {
|
||||
HirFileIdRepr::FileId(file_id) => FileRange { file_id, range: self.value },
|
||||
HirFileIdRepr::MacroFile(mac_file) => {
|
||||
match map_node_range_up_rooted(db, &db.expansion_span_map(mac_file), self.value) {
|
||||
Some(it) => it,
|
||||
_ => {
|
||||
let loc = db.lookup_intern_macro_call(mac_file.macro_call_id);
|
||||
loc.kind.original_call_range_with_body(db)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn original_node_file_range_opt(
|
||||
self,
|
||||
db: &dyn db::ExpandDatabase,
|
||||
|
@ -395,18 +376,12 @@ impl<N: AstNode> InFile<N> {
|
|||
return None;
|
||||
}
|
||||
|
||||
let (FileRange { file_id, range }, ctx) = map_node_range_up(
|
||||
let FileRange { file_id, range } = map_node_range_up_rooted(
|
||||
db,
|
||||
&db.expansion_span_map(file_id),
|
||||
self.value.syntax().text_range(),
|
||||
)?;
|
||||
|
||||
// FIXME: Figure out an API that makes proper use of ctx, this only exists to
|
||||
// keep pre-token map rewrite behaviour.
|
||||
if !ctx.is_root() {
|
||||
return None;
|
||||
}
|
||||
|
||||
// FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes?
|
||||
let anc = db.parse(file_id).syntax_node().covering_element(range);
|
||||
let value = anc.ancestors().find_map(N::cast)?;
|
||||
|
|
|
@ -25,13 +25,16 @@ pub mod span_map;
|
|||
mod cfg_process;
|
||||
mod fixup;
|
||||
use attrs::collect_attrs;
|
||||
use rustc_hash::FxHashMap;
|
||||
use triomphe::Arc;
|
||||
|
||||
use std::{fmt, hash::Hash};
|
||||
|
||||
use base_db::{salsa::impl_intern_value_trivial, CrateId, Edition, FileId};
|
||||
use either::Either;
|
||||
use span::{ErasedFileAstId, FileRange, HirFileIdRepr, Span, SyntaxContextData, SyntaxContextId};
|
||||
use span::{
|
||||
ErasedFileAstId, FileRange, HirFileIdRepr, Span, SpanAnchor, SyntaxContextData, SyntaxContextId,
|
||||
};
|
||||
use syntax::{
|
||||
ast::{self, AstNode},
|
||||
SyntaxNode, SyntaxToken, TextRange, TextSize,
|
||||
|
@ -683,6 +686,8 @@ impl ExpansionInfo {
|
|||
}
|
||||
|
||||
/// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
|
||||
///
|
||||
/// Note this does a linear search through the entire backing vector of the spanmap.
|
||||
pub fn map_range_down(
|
||||
&self,
|
||||
span: Span,
|
||||
|
@ -793,7 +798,34 @@ impl ExpansionInfo {
|
|||
}
|
||||
}
|
||||
|
||||
/// Maps up the text range out of the expansion hierarchy back into the original file its from only
|
||||
/// considering the root spans contained.
|
||||
/// Unlike [`map_node_range_up`], this will not return `None` if any anchors or syntax contexts differ.
|
||||
pub fn map_node_range_up_rooted(
|
||||
db: &dyn ExpandDatabase,
|
||||
exp_map: &ExpansionSpanMap,
|
||||
range: TextRange,
|
||||
) -> Option<FileRange> {
|
||||
let mut spans = exp_map.spans_for_range(range).filter(|span| span.ctx.is_root());
|
||||
let Span { range, anchor, ctx: _ } = spans.next()?;
|
||||
let mut start = range.start();
|
||||
let mut end = range.end();
|
||||
|
||||
for span in spans {
|
||||
if span.anchor != anchor {
|
||||
return None;
|
||||
}
|
||||
start = start.min(span.range.start());
|
||||
end = end.max(span.range.end());
|
||||
}
|
||||
let anchor_offset =
|
||||
db.ast_id_map(anchor.file_id.into()).get_erased(anchor.ast_id).text_range().start();
|
||||
Some(FileRange { file_id: anchor.file_id, range: TextRange::new(start, end) + anchor_offset })
|
||||
}
|
||||
|
||||
/// Maps up the text range out of the expansion hierarchy back into the original file its from.
|
||||
///
|
||||
/// this will return `None` if any anchors or syntax contexts differ.
|
||||
pub fn map_node_range_up(
|
||||
db: &dyn ExpandDatabase,
|
||||
exp_map: &ExpansionSpanMap,
|
||||
|
@ -819,6 +851,29 @@ pub fn map_node_range_up(
|
|||
))
|
||||
}
|
||||
|
||||
/// Maps up the text range out of the expansion hierarchy back into the original file its from.
|
||||
/// This version will aggregate the ranges of all spans with the same anchor and syntax context.
|
||||
pub fn map_node_range_up_aggregated(
|
||||
db: &dyn ExpandDatabase,
|
||||
exp_map: &ExpansionSpanMap,
|
||||
range: TextRange,
|
||||
) -> FxHashMap<(SpanAnchor, SyntaxContextId), TextRange> {
|
||||
let mut map = FxHashMap::default();
|
||||
for span in exp_map.spans_for_range(range) {
|
||||
let range = map.entry((span.anchor, span.ctx)).or_insert_with(|| span.range);
|
||||
*range = TextRange::new(
|
||||
range.start().min(span.range.start()),
|
||||
range.end().max(span.range.end()),
|
||||
);
|
||||
}
|
||||
for ((anchor, _), range) in &mut map {
|
||||
let anchor_offset =
|
||||
db.ast_id_map(anchor.file_id.into()).get_erased(anchor.ast_id).text_range().start();
|
||||
*range += anchor_offset;
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
/// Looks up the span at the given offset.
|
||||
pub fn span_for_offset(
|
||||
db: &dyn ExpandDatabase,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
//! Span maps for real files and macro expansions.
|
||||
use span::{FileId, HirFileId, HirFileIdRepr, MacroFileId, Span};
|
||||
use span::{FileId, HirFileId, HirFileIdRepr, MacroFileId, Span, SyntaxContextId};
|
||||
use syntax::{AstNode, TextRange};
|
||||
use triomphe::Arc;
|
||||
|
||||
|
@ -7,7 +7,7 @@ pub use span::RealSpanMap;
|
|||
|
||||
use crate::db::ExpandDatabase;
|
||||
|
||||
pub type ExpansionSpanMap = span::SpanMap<Span>;
|
||||
pub type ExpansionSpanMap = span::SpanMap<SyntaxContextId>;
|
||||
|
||||
/// Spanmap for a macro file or a real file
|
||||
#[derive(Clone, Debug, PartialEq, Eq)]
|
||||
|
|
|
@ -164,7 +164,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
|
|||
Some(value) => value,
|
||||
None => continue,
|
||||
};
|
||||
let range = node.as_ref().original_file_range(&db);
|
||||
let range = node.as_ref().original_file_range_rooted(&db);
|
||||
if let Some(expected) = types.remove(&range) {
|
||||
let actual = if display_source {
|
||||
ty.display_source_code(&db, def.module(&db), true).unwrap()
|
||||
|
@ -180,7 +180,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
|
|||
Some(value) => value,
|
||||
None => continue,
|
||||
};
|
||||
let range = node.as_ref().original_file_range(&db);
|
||||
let range = node.as_ref().original_file_range_rooted(&db);
|
||||
if let Some(expected) = types.remove(&range) {
|
||||
let actual = if display_source {
|
||||
ty.display_source_code(&db, def.module(&db), true).unwrap()
|
||||
|
@ -211,7 +211,7 @@ fn check_impl(ra_fixture: &str, allow_none: bool, only_types: bool, display_sour
|
|||
}) else {
|
||||
continue;
|
||||
};
|
||||
let range = node.as_ref().original_file_range(&db);
|
||||
let range = node.as_ref().original_file_range_rooted(&db);
|
||||
let actual = format!(
|
||||
"expected {}, got {}",
|
||||
mismatch.expected.display_test(&db),
|
||||
|
|
|
@ -9,6 +9,7 @@ use hir_def::{
|
|||
};
|
||||
use hir_expand::{HirFileId, InFile};
|
||||
use syntax::ast;
|
||||
use tt::TextRange;
|
||||
|
||||
use crate::{
|
||||
db::HirDatabase, Adt, Const, Enum, ExternCrateDecl, Field, FieldSource, Function, Impl,
|
||||
|
@ -37,6 +38,12 @@ impl Module {
|
|||
def_map[self.id.local_id].definition_source(db.upcast())
|
||||
}
|
||||
|
||||
/// Returns a node which defines this module. That is, a file or a `mod foo {}` with items.
|
||||
pub fn definition_source_range(self, db: &dyn HirDatabase) -> InFile<TextRange> {
|
||||
let def_map = self.id.def_map(db.upcast());
|
||||
def_map[self.id.local_id].definition_source_range(db.upcast())
|
||||
}
|
||||
|
||||
pub fn definition_source_file_id(self, db: &dyn HirDatabase) -> HirFileId {
|
||||
let def_map = self.id.def_map(db.upcast());
|
||||
def_map[self.id.local_id].definition_source_file_id()
|
||||
|
@ -71,6 +78,13 @@ impl Module {
|
|||
let def_map = self.id.def_map(db.upcast());
|
||||
def_map[self.id.local_id].declaration_source(db.upcast())
|
||||
}
|
||||
|
||||
/// Returns a text range which declares this module, either a `mod foo;` or a `mod foo {}`.
|
||||
/// `None` for the crate root.
|
||||
pub fn declaration_source_range(self, db: &dyn HirDatabase) -> Option<InFile<TextRange>> {
|
||||
let def_map = self.id.def_map(db.upcast());
|
||||
def_map[self.id.local_id].declaration_source_range(db.upcast())
|
||||
}
|
||||
}
|
||||
|
||||
impl HasSource for Field {
|
||||
|
|
|
@ -960,7 +960,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
/// macro file the node resides in.
|
||||
pub fn original_range(&self, node: &SyntaxNode) -> FileRange {
|
||||
let node = self.find_file(node);
|
||||
node.original_file_range(self.db.upcast())
|
||||
node.original_file_range_rooted(self.db.upcast())
|
||||
}
|
||||
|
||||
/// Attempts to map the node out of macro expanded files returning the original file range.
|
||||
|
@ -984,9 +984,9 @@ impl<'db> SemanticsImpl<'db> {
|
|||
|
||||
/// Attempts to map the node out of macro expanded files.
|
||||
/// This only work for attribute expansions, as other ones do not have nodes as input.
|
||||
pub fn original_syntax_node(&self, node: &SyntaxNode) -> Option<SyntaxNode> {
|
||||
pub fn original_syntax_node_rooted(&self, node: &SyntaxNode) -> Option<SyntaxNode> {
|
||||
let InFile { file_id, .. } = self.find_file(node);
|
||||
InFile::new(file_id, node).original_syntax_node(self.db.upcast()).map(
|
||||
InFile::new(file_id, node).original_syntax_node_rooted(self.db.upcast()).map(
|
||||
|InRealFile { file_id, value }| {
|
||||
self.cache(find_root(&value), file_id.into());
|
||||
value
|
||||
|
@ -997,7 +997,7 @@ impl<'db> SemanticsImpl<'db> {
|
|||
pub fn diagnostics_display_range(&self, src: InFile<SyntaxNodePtr>) -> FileRange {
|
||||
let root = self.parse_or_expand(src.file_id);
|
||||
let node = src.map(|it| it.to_node(&root));
|
||||
node.as_ref().original_file_range(self.db.upcast())
|
||||
node.as_ref().original_file_range_rooted(self.db.upcast())
|
||||
}
|
||||
|
||||
fn token_ancestors_with_macros(
|
||||
|
|
|
@ -49,7 +49,7 @@ impl DeclarationLocation {
|
|||
return FileRange { file_id, range: self.ptr.text_range() };
|
||||
}
|
||||
let node = resolve_node(db, self.hir_file_id, &self.ptr);
|
||||
node.as_ref().original_file_range(db.upcast())
|
||||
node.as_ref().original_file_range_rooted(db.upcast())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -198,7 +198,7 @@ fn get_adt_source(
|
|||
adt: &hir::Adt,
|
||||
fn_name: &str,
|
||||
) -> Option<(Option<ast::Impl>, FileId)> {
|
||||
let range = adt.source(ctx.sema.db)?.syntax().original_file_range(ctx.sema.db);
|
||||
let range = adt.source(ctx.sema.db)?.syntax().original_file_range_rooted(ctx.sema.db);
|
||||
let file = ctx.sema.parse(range.file_id);
|
||||
let adt_source =
|
||||
ctx.sema.find_node_at_offset_with_macros(file.syntax(), range.range.start())?;
|
||||
|
|
|
@ -206,7 +206,7 @@ pub(crate) fn inline_call(acc: &mut Assists, ctx: &AssistContext<'_>) -> Option<
|
|||
let fn_body = fn_source.value.body()?;
|
||||
let param_list = fn_source.value.param_list()?;
|
||||
|
||||
let FileRange { file_id, range } = fn_source.syntax().original_file_range(ctx.sema.db);
|
||||
let FileRange { file_id, range } = fn_source.syntax().original_file_range_rooted(ctx.sema.db);
|
||||
if file_id == ctx.file_id() && range.contains(ctx.offset()) {
|
||||
cov_mark::hit!(inline_call_recursive);
|
||||
return None;
|
||||
|
|
|
@ -96,7 +96,7 @@ fn complete_trait_impl_name(
|
|||
.parent()
|
||||
}
|
||||
}?;
|
||||
let item = ctx.sema.original_syntax_node(&item)?;
|
||||
let item = ctx.sema.original_syntax_node_rooted(&item)?;
|
||||
// item -> ASSOC_ITEM_LIST -> IMPL
|
||||
let impl_def = ast::Impl::cast(item.parent()?.parent()?)?;
|
||||
let replacement_range = {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use std::iter;
|
||||
|
||||
use hir::{HirFileIdExt, Module, ModuleSource};
|
||||
use hir::{HirFileIdExt, Module};
|
||||
use ide_db::{
|
||||
base_db::{SourceDatabaseExt, VfsPath},
|
||||
FxHashSet, RootDatabase, SymbolKind,
|
||||
|
@ -57,7 +57,7 @@ pub(crate) fn complete_mod(
|
|||
.collect::<FxHashSet<_>>();
|
||||
|
||||
let module_declaration_file =
|
||||
current_module.declaration_source(ctx.db).map(|module_declaration_source_file| {
|
||||
current_module.declaration_source_range(ctx.db).map(|module_declaration_source_file| {
|
||||
module_declaration_source_file.file_id.original_file(ctx.db)
|
||||
});
|
||||
|
||||
|
@ -148,9 +148,7 @@ fn module_chain_to_containing_module_file(
|
|||
) -> Vec<Module> {
|
||||
let mut path =
|
||||
iter::successors(Some(current_module), |current_module| current_module.parent(db))
|
||||
.take_while(|current_module| {
|
||||
matches!(current_module.definition_source(db).value, ModuleSource::Module(_))
|
||||
})
|
||||
.take_while(|current_module| current_module.is_inline(db))
|
||||
.collect::<Vec<_>>();
|
||||
path.reverse();
|
||||
path
|
||||
|
|
|
@ -71,7 +71,7 @@ pub fn visit_file_defs(
|
|||
let mut defs: VecDeque<_> = module.declarations(db).into();
|
||||
while let Some(def) = defs.pop_front() {
|
||||
if let ModuleDef::Module(submodule) = def {
|
||||
if let hir::ModuleSource::Module(_) = submodule.definition_source(db).value {
|
||||
if submodule.is_inline(db) {
|
||||
defs.extend(submodule.declarations(db));
|
||||
submodule.impl_defs(db).into_iter().for_each(|impl_| cb(impl_.into()));
|
||||
}
|
||||
|
|
|
@ -190,22 +190,15 @@ impl SearchScope {
|
|||
let mut entries = IntMap::default();
|
||||
|
||||
let (file_id, range) = {
|
||||
let InFile { file_id, value } = module.definition_source(db);
|
||||
let InFile { file_id, value } = module.definition_source_range(db);
|
||||
if let Some(InRealFile { file_id, value: call_source }) = file_id.original_call_node(db)
|
||||
{
|
||||
(file_id, Some(call_source.text_range()))
|
||||
} else {
|
||||
(
|
||||
file_id.original_file(db),
|
||||
match value {
|
||||
ModuleSource::SourceFile(_) => None,
|
||||
ModuleSource::Module(it) => Some(it.syntax().text_range()),
|
||||
ModuleSource::BlockExpr(it) => Some(it.syntax().text_range()),
|
||||
},
|
||||
)
|
||||
(file_id.original_file(db), Some(value))
|
||||
}
|
||||
};
|
||||
entries.insert(file_id, range);
|
||||
entries.entry(file_id).or_insert(range);
|
||||
|
||||
let mut to_visit: Vec<_> = module.children(db).collect();
|
||||
while let Some(module) = to_visit.pop() {
|
||||
|
|
|
@ -38,7 +38,7 @@ fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::IncorrectCase) -> Option<Vec<Ass
|
|||
let def = NameClass::classify(&ctx.sema, &name_node)?.defined()?;
|
||||
|
||||
let name_node = InFile::new(d.file, name_node.syntax());
|
||||
let frange = name_node.original_file_range(ctx.sema.db);
|
||||
let frange = name_node.original_file_range_rooted(ctx.sema.db);
|
||||
|
||||
let label = format!("Rename to {}", d.suggested_text);
|
||||
let mut res = unresolved_fix("change_case", &label, frange.range);
|
||||
|
|
|
@ -114,10 +114,8 @@ fn add_variant_to_union(
|
|||
) -> Option<Assist> {
|
||||
let adt_source = adt_union.source(ctx.sema.db)?;
|
||||
let adt_syntax = adt_source.syntax();
|
||||
let Some(field_list) = adt_source.value.record_field_list() else {
|
||||
return None;
|
||||
};
|
||||
let range = adt_syntax.original_file_range(ctx.sema.db);
|
||||
let field_list = adt_source.value.record_field_list()?;
|
||||
let range = adt_syntax.original_file_range_rooted(ctx.sema.db);
|
||||
let field_name = make::name(field_name);
|
||||
|
||||
let (offset, record_field) =
|
||||
|
@ -144,7 +142,7 @@ fn add_field_to_struct_fix(
|
|||
) -> Option<Assist> {
|
||||
let struct_source = adt_struct.source(ctx.sema.db)?;
|
||||
let struct_syntax = struct_source.syntax();
|
||||
let struct_range = struct_syntax.original_file_range(ctx.sema.db);
|
||||
let struct_range = struct_syntax.original_file_range_rooted(ctx.sema.db);
|
||||
let field_list = struct_source.value.field_list();
|
||||
match field_list {
|
||||
Some(FieldList::RecordFieldList(field_list)) => {
|
||||
|
|
|
@ -125,9 +125,12 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
|
|||
let match_state = Matcher { sema, restrict_range: *restrict_range, rule };
|
||||
// First pass at matching, where we check that node types and idents match.
|
||||
match_state.attempt_match_node(&mut Phase::First, &rule.pattern.node, code)?;
|
||||
match_state.validate_range(&sema.original_range(code))?;
|
||||
let file_range = sema
|
||||
.original_range_opt(code)
|
||||
.ok_or(MatchFailed { reason: Some("def site definition".to_owned()) })?;
|
||||
match_state.validate_range(&file_range)?;
|
||||
let mut the_match = Match {
|
||||
range: sema.original_range(code),
|
||||
range: file_range,
|
||||
matched_node: code.clone(),
|
||||
placeholder_values: FxHashMap::default(),
|
||||
ignored_comments: Vec::new(),
|
||||
|
@ -175,7 +178,10 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
|
|||
self.check_constraint(constraint, code)?;
|
||||
}
|
||||
if let Phase::Second(matches_out) = phase {
|
||||
let original_range = self.sema.original_range(code);
|
||||
let original_range = self
|
||||
.sema
|
||||
.original_range_opt(code)
|
||||
.ok_or(MatchFailed { reason: Some("def site definition".to_owned()) })?;
|
||||
// We validated the range for the node when we started the match, so the placeholder
|
||||
// probably can't fail range validation, but just to be safe...
|
||||
self.validate_range(&original_range)?;
|
||||
|
@ -487,7 +493,13 @@ impl<'db, 'sema> Matcher<'db, 'sema> {
|
|||
match_out.placeholder_values.insert(
|
||||
placeholder.ident.clone(),
|
||||
PlaceholderMatch::from_range(FileRange {
|
||||
file_id: self.sema.original_range(code).file_id,
|
||||
file_id: self
|
||||
.sema
|
||||
.original_range_opt(code)
|
||||
.ok_or(MatchFailed {
|
||||
reason: Some("def site definition".to_owned()),
|
||||
})?
|
||||
.file_id,
|
||||
range: first_matched_token
|
||||
.text_range()
|
||||
.cover(last_matched_token.text_range()),
|
||||
|
|
|
@ -190,12 +190,9 @@ impl MatchFinder<'_> {
|
|||
// When matching within a macro expansion, we only want to allow matches of
|
||||
// nodes that originated entirely from within the token tree of the macro call.
|
||||
// i.e. we don't want to match something that came from the macro itself.
|
||||
self.slow_scan_node(
|
||||
&expanded,
|
||||
rule,
|
||||
&Some(self.sema.original_range(tt.syntax())),
|
||||
matches_out,
|
||||
);
|
||||
if let Some(range) = self.sema.original_range_opt(tt.syntax()) {
|
||||
self.slow_scan_node(&expanded, rule, &Some(range), matches_out);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -227,7 +224,7 @@ impl MatchFinder<'_> {
|
|||
// There is no range restriction.
|
||||
return true;
|
||||
}
|
||||
let node_range = self.sema.original_range(code);
|
||||
let Some(node_range) = self.sema.original_range_opt(code) else { return false };
|
||||
for range in &self.restrict_ranges {
|
||||
if range.file_id == node_range.file_id && range.range.contains_range(node_range.range) {
|
||||
return true;
|
||||
|
|
|
@ -852,7 +852,7 @@ fn orig_range(
|
|||
value: &SyntaxNode,
|
||||
) -> UpmappingResult<(FileRange, Option<TextRange>)> {
|
||||
UpmappingResult {
|
||||
call_site: (InFile::new(hir_file, value).original_file_range(db), None),
|
||||
call_site: (InFile::new(hir_file, value).original_file_range_rooted(db), None),
|
||||
def_site: None,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -138,7 +138,9 @@ pub(crate) fn runnables(db: &RootDatabase, file_id: FileId) -> Vec<Runnable> {
|
|||
}) {
|
||||
if let Some(def) = def {
|
||||
let file_id = match def {
|
||||
Definition::Module(it) => it.declaration_source(db).map(|src| src.file_id),
|
||||
Definition::Module(it) => {
|
||||
it.declaration_source_range(db).map(|src| src.file_id)
|
||||
}
|
||||
Definition::Function(it) => it.source(db).map(|src| src.file_id),
|
||||
_ => None,
|
||||
};
|
||||
|
@ -269,15 +271,10 @@ fn find_related_tests_in_module(
|
|||
Some(it) => it,
|
||||
_ => return,
|
||||
};
|
||||
let mod_source = parent_module.definition_source(sema.db);
|
||||
let range = match &mod_source.value {
|
||||
hir::ModuleSource::Module(m) => m.syntax().text_range(),
|
||||
hir::ModuleSource::BlockExpr(b) => b.syntax().text_range(),
|
||||
hir::ModuleSource::SourceFile(f) => f.syntax().text_range(),
|
||||
};
|
||||
let mod_source = parent_module.definition_source_range(sema.db);
|
||||
|
||||
let file_id = mod_source.file_id.original_file(sema.db);
|
||||
let mod_scope = SearchScope::file_range(FileRange { file_id, range });
|
||||
let mod_scope = SearchScope::file_range(FileRange { file_id, range: mod_source.value });
|
||||
let fn_pos = FilePosition { file_id, offset: fn_name.syntax().text_range().start() };
|
||||
find_related_tests(sema, syntax, fn_pos, Some(mod_scope), tests)
|
||||
}
|
||||
|
@ -405,14 +402,15 @@ fn runnable_mod_outline_definition(
|
|||
|
||||
let attrs = def.attrs(sema.db);
|
||||
let cfg = attrs.cfg();
|
||||
match def.definition_source(sema.db).value {
|
||||
hir::ModuleSource::SourceFile(_) => Some(Runnable {
|
||||
if def.as_source_file_id(sema.db).is_some() {
|
||||
Some(Runnable {
|
||||
use_name_in_title: false,
|
||||
nav: def.to_nav(sema.db).call_site(),
|
||||
kind: RunnableKind::TestMod { path },
|
||||
cfg,
|
||||
}),
|
||||
_ => None,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -23,8 +23,11 @@ pub trait SpanMapper<S: Span> {
|
|||
fn span_for(&self, range: TextRange) -> S;
|
||||
}
|
||||
|
||||
impl<S: Span> SpanMapper<S> for SpanMap<S> {
|
||||
fn span_for(&self, range: TextRange) -> S {
|
||||
impl<S> SpanMapper<SpanData<S>> for SpanMap<S>
|
||||
where
|
||||
SpanData<S>: Span,
|
||||
{
|
||||
fn span_for(&self, range: TextRange) -> SpanData<S> {
|
||||
self.span_at(range.start())
|
||||
}
|
||||
}
|
||||
|
@ -121,7 +124,7 @@ where
|
|||
pub fn token_tree_to_syntax_node<Ctx>(
|
||||
tt: &tt::Subtree<SpanData<Ctx>>,
|
||||
entry_point: parser::TopEntryPoint,
|
||||
) -> (Parse<SyntaxNode>, SpanMap<SpanData<Ctx>>)
|
||||
) -> (Parse<SyntaxNode>, SpanMap<Ctx>)
|
||||
where
|
||||
SpanData<Ctx>: Span,
|
||||
Ctx: Copy,
|
||||
|
@ -833,7 +836,7 @@ where
|
|||
cursor: Cursor<'a, SpanData<Ctx>>,
|
||||
text_pos: TextSize,
|
||||
inner: SyntaxTreeBuilder,
|
||||
token_map: SpanMap<SpanData<Ctx>>,
|
||||
token_map: SpanMap<Ctx>,
|
||||
}
|
||||
|
||||
impl<'a, Ctx> TtTreeSink<'a, Ctx>
|
||||
|
@ -850,7 +853,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
fn finish(mut self) -> (Parse<SyntaxNode>, SpanMap<SpanData<Ctx>>) {
|
||||
fn finish(mut self) -> (Parse<SyntaxNode>, SpanMap<Ctx>) {
|
||||
self.token_map.finish();
|
||||
(self.inner.finish(), self.token_map)
|
||||
}
|
||||
|
|
|
@ -1053,7 +1053,7 @@ fn location_csv_expr(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, expr_id:
|
|||
};
|
||||
let root = db.parse_or_expand(src.file_id);
|
||||
let node = src.map(|e| e.to_node(&root).syntax().clone());
|
||||
let original_range = node.as_ref().original_file_range(db);
|
||||
let original_range = node.as_ref().original_file_range_rooted(db);
|
||||
let path = vfs.file_path(original_range.file_id);
|
||||
let line_index = db.line_index(original_range.file_id);
|
||||
let text_range = original_range.range;
|
||||
|
@ -1069,7 +1069,7 @@ fn location_csv_pat(db: &RootDatabase, vfs: &Vfs, sm: &BodySourceMap, pat_id: Pa
|
|||
};
|
||||
let root = db.parse_or_expand(src.file_id);
|
||||
let node = src.map(|e| e.to_node(&root).syntax().clone());
|
||||
let original_range = node.as_ref().original_file_range(db);
|
||||
let original_range = node.as_ref().original_file_range_rooted(db);
|
||||
let path = vfs.file_path(original_range.file_id);
|
||||
let line_index = db.line_index(original_range.file_id);
|
||||
let text_range = original_range.range;
|
||||
|
@ -1088,7 +1088,7 @@ fn expr_syntax_range<'a>(
|
|||
if let Ok(src) = src {
|
||||
let root = db.parse_or_expand(src.file_id);
|
||||
let node = src.map(|e| e.to_node(&root).syntax().clone());
|
||||
let original_range = node.as_ref().original_file_range(db);
|
||||
let original_range = node.as_ref().original_file_range_rooted(db);
|
||||
let path = vfs.file_path(original_range.file_id);
|
||||
let line_index = db.line_index(original_range.file_id);
|
||||
let text_range = original_range.range;
|
||||
|
@ -1109,7 +1109,7 @@ fn pat_syntax_range<'a>(
|
|||
if let Ok(src) = src {
|
||||
let root = db.parse_or_expand(src.file_id);
|
||||
let node = src.map(|e| e.to_node(&root).syntax().clone());
|
||||
let original_range = node.as_ref().original_file_range(db);
|
||||
let original_range = node.as_ref().original_file_range_rooted(db);
|
||||
let path = vfs.file_path(original_range.file_id);
|
||||
let line_index = db.line_index(original_range.file_id);
|
||||
let text_range = original_range.range;
|
||||
|
|
|
@ -56,6 +56,12 @@ pub struct SpanData<Ctx> {
|
|||
pub ctx: Ctx,
|
||||
}
|
||||
|
||||
impl<Ctx: Copy> SpanData<Ctx> {
|
||||
pub fn eq_ignoring_ctx(self, other: Self) -> bool {
|
||||
self.anchor == other.anchor && self.range == other.range
|
||||
}
|
||||
}
|
||||
|
||||
impl Span {
|
||||
#[deprecated = "dummy spans will panic if surfaced incorrectly, as such they should be replaced appropriately"]
|
||||
pub const DUMMY: Self = SpanData {
|
||||
|
|
|
@ -7,17 +7,20 @@ use stdx::{always, itertools::Itertools};
|
|||
use syntax::{TextRange, TextSize};
|
||||
use vfs::FileId;
|
||||
|
||||
use crate::{ErasedFileAstId, Span, SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
|
||||
use crate::{
|
||||
ErasedFileAstId, Span, SpanAnchor, SpanData, SyntaxContextId, ROOT_ERASED_FILE_AST_ID,
|
||||
};
|
||||
|
||||
/// Maps absolute text ranges for the corresponding file to the relevant span data.
|
||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||
pub struct SpanMap<S> {
|
||||
spans: Vec<(TextSize, S)>,
|
||||
// FIXME: Should be
|
||||
// spans: Vec<(TextSize, crate::SyntaxContextId)>,
|
||||
spans: Vec<(TextSize, SpanData<S>)>,
|
||||
}
|
||||
|
||||
impl<S: Copy> SpanMap<S> {
|
||||
impl<S> SpanMap<S>
|
||||
where
|
||||
SpanData<S>: Copy,
|
||||
{
|
||||
/// Creates a new empty [`SpanMap`].
|
||||
pub fn empty() -> Self {
|
||||
Self { spans: Vec::new() }
|
||||
|
@ -34,7 +37,7 @@ impl<S: Copy> SpanMap<S> {
|
|||
}
|
||||
|
||||
/// Pushes a new span onto the [`SpanMap`].
|
||||
pub fn push(&mut self, offset: TextSize, span: S) {
|
||||
pub fn push(&mut self, offset: TextSize, span: SpanData<S>) {
|
||||
if cfg!(debug_assertions) {
|
||||
if let Some(&(last_offset, _)) = self.spans.last() {
|
||||
assert!(
|
||||
|
@ -49,13 +52,12 @@ impl<S: Copy> SpanMap<S> {
|
|||
/// Returns all [`TextRange`]s that correspond to the given span.
|
||||
///
|
||||
/// Note this does a linear search through the entire backing vector.
|
||||
pub fn ranges_with_span(&self, span: S) -> impl Iterator<Item = TextRange> + '_
|
||||
pub fn ranges_with_span(&self, span: SpanData<S>) -> impl Iterator<Item = TextRange> + '_
|
||||
where
|
||||
S: Eq,
|
||||
S: Copy,
|
||||
{
|
||||
// FIXME: This should ignore the syntax context!
|
||||
self.spans.iter().enumerate().filter_map(move |(idx, &(end, s))| {
|
||||
if s != span {
|
||||
if !s.eq_ignoring_ctx(span) {
|
||||
return None;
|
||||
}
|
||||
let start = idx.checked_sub(1).map_or(TextSize::new(0), |prev| self.spans[prev].0);
|
||||
|
@ -64,21 +66,21 @@ impl<S: Copy> SpanMap<S> {
|
|||
}
|
||||
|
||||
/// Returns the span at the given position.
|
||||
pub fn span_at(&self, offset: TextSize) -> S {
|
||||
pub fn span_at(&self, offset: TextSize) -> SpanData<S> {
|
||||
let entry = self.spans.partition_point(|&(it, _)| it <= offset);
|
||||
self.spans[entry].1
|
||||
}
|
||||
|
||||
/// Returns the spans associated with the given range.
|
||||
/// In other words, this will return all spans that correspond to all offsets within the given range.
|
||||
pub fn spans_for_range(&self, range: TextRange) -> impl Iterator<Item = S> + '_ {
|
||||
pub fn spans_for_range(&self, range: TextRange) -> impl Iterator<Item = SpanData<S>> + '_ {
|
||||
let (start, end) = (range.start(), range.end());
|
||||
let start_entry = self.spans.partition_point(|&(it, _)| it <= start);
|
||||
let end_entry = self.spans[start_entry..].partition_point(|&(it, _)| it <= end); // FIXME: this might be wrong?
|
||||
self.spans[start_entry..][..end_entry].iter().map(|&(_, s)| s)
|
||||
}
|
||||
|
||||
pub fn iter(&self) -> impl Iterator<Item = (TextSize, S)> + '_ {
|
||||
pub fn iter(&self) -> impl Iterator<Item = (TextSize, SpanData<S>)> + '_ {
|
||||
self.spans.iter().copied()
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue