This commit is contained in:
Lukas Wirth 2023-12-06 14:36:39 +01:00
parent 9cb13b6efb
commit 634d588fd7
13 changed files with 137 additions and 176 deletions

View file

@ -6,9 +6,9 @@ use base_db::{
FileId, FileRange,
};
use either::Either;
use syntax::{AstNode, SyntaxNode, SyntaxToken, TextRange};
use syntax::{AstNode, SyntaxNode, SyntaxToken, TextRange, TextSize};
use crate::{db, ExpansionInfo, HirFileIdExt as _};
use crate::{db, ExpansionInfo, MacroFileIdExt};
/// `InFile<T>` stores a value of `T` inside a particular file/syntax tree.
///
@ -119,16 +119,6 @@ impl<FileId: Copy, N: AstNode> InFileWrapper<FileId, N> {
// region:specific impls
impl InFile<&SyntaxNode> {
pub fn ancestors_with_macros(
self,
db: &dyn db::ExpandDatabase,
) -> impl Iterator<Item = InFile<SyntaxNode>> + Clone + '_ {
iter::successors(Some(self.cloned()), move |node| match node.value.parent() {
Some(parent) => Some(node.with_value(parent)),
None => node.file_id.call_node(db),
})
}
/// Skips the attributed item that caused the macro invocation we are climbing up
pub fn ancestors_with_macros_skip_attr_item(
self,
@ -137,8 +127,9 @@ impl InFile<&SyntaxNode> {
let succ = move |node: &InFile<SyntaxNode>| match node.value.parent() {
Some(parent) => Some(node.with_value(parent)),
None => {
let parent_node = node.file_id.call_node(db)?;
if node.file_id.is_attr_macro(db) {
let macro_file_id = node.file_id.macro_file()?;
let parent_node = macro_file_id.call_node(db);
if macro_file_id.is_attr_macro(db) {
// macro call was an attributed item, skip it
// FIXME: does this fail if this is a direct expansion of another macro?
parent_node.map(|node| node.parent()).transpose()
@ -222,7 +213,7 @@ impl InFile<&SyntaxNode> {
}
HirFileIdRepr::MacroFile(m) => m,
};
if !self.file_id.is_attr_macro(db) {
if !file_id.is_attr_macro(db) {
return None;
}
@ -243,21 +234,23 @@ impl InFile<&SyntaxNode> {
}
}
impl InFile<SyntaxToken> {
impl InMacroFile<SyntaxToken> {
pub fn upmap_once(
self,
db: &dyn db::ExpandDatabase,
) -> Option<InFile<smallvec::SmallVec<[TextRange; 1]>>> {
Some(self.file_id.expansion_info(db)?.map_range_up_once(db, self.value.text_range()))
) -> InFile<smallvec::SmallVec<[TextRange; 1]>> {
self.file_id.expansion_info(db).map_range_up_once(db, self.value.text_range())
}
}
impl InFile<SyntaxToken> {
/// Falls back to the macro call range if the node cannot be mapped up fully.
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) => {
let (range, ctxt) = ExpansionInfo::new(db, mac_file)
.map_token_range_up(db, self.value.text_range());
.span_for_offset(db, self.value.text_range().start());
// FIXME: Figure out an API that makes proper use of ctx, this only exists to
// keep pre-token map rewrite behaviour.
@ -280,7 +273,7 @@ impl InFile<SyntaxToken> {
}
HirFileIdRepr::MacroFile(mac_file) => {
let (range, ctxt) = ExpansionInfo::new(db, mac_file)
.map_token_range_up(db, self.value.text_range());
.span_for_offset(db, self.value.text_range().start());
// FIXME: Figure out an API that makes proper use of ctx, this only exists to
// keep pre-token map rewrite behaviour.
@ -294,20 +287,13 @@ impl InFile<SyntaxToken> {
}
}
impl InFile<TextRange> {
/// Attempts to map the syntax node back up its macro calls.
pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> FileRange {
let (range, _ctxt) = match self.file_id.repr() {
HirFileIdRepr::FileId(file_id) => {
(FileRange { file_id, range: self.value }, SyntaxContextId::ROOT)
}
HirFileIdRepr::MacroFile(m) => {
ExpansionInfo::new(db, m).map_token_range_up(db, self.value)
}
};
range
impl InMacroFile<TextSize> {
pub fn original_file_range(self, db: &dyn db::ExpandDatabase) -> (FileRange, SyntaxContextId) {
ExpansionInfo::new(db, self.file_id).span_for_offset(db, self.value)
}
}
impl InFile<TextRange> {
pub fn original_node_file_range(
self,
db: &dyn db::ExpandDatabase,
@ -353,7 +339,7 @@ impl<N: AstNode> InFile<N> {
}
HirFileIdRepr::MacroFile(m) => m,
};
if !self.file_id.is_attr_macro(db) {
if !file_id.is_attr_macro(db) {
return None;
}

View file

@ -179,9 +179,6 @@ pub trait HirFileIdExt {
/// one of the calls comes from an `include!``.
fn original_file_respecting_includes(self, db: &dyn db::ExpandDatabase) -> FileId;
/// If this is a macro call, returns the syntax node of the call.
fn call_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>>;
/// If this is a macro call, returns the syntax node of the very first macro call this file resides in.
fn original_call_node(self, db: &dyn db::ExpandDatabase) -> Option<InRealFile<SyntaxNode>>;
@ -190,19 +187,6 @@ pub trait HirFileIdExt {
fn as_builtin_derive_attr_node(&self, db: &dyn db::ExpandDatabase)
-> Option<InFile<ast::Attr>>;
fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> bool;
fn is_custom_derive(&self, db: &dyn db::ExpandDatabase) -> bool;
/// Return whether this file is an include macro
fn is_include_macro(&self, db: &dyn db::ExpandDatabase) -> bool;
fn is_eager(&self, db: &dyn db::ExpandDatabase) -> bool;
/// Return whether this file is an attr macro
fn is_attr_macro(&self, db: &dyn db::ExpandDatabase) -> bool;
/// Return whether this file is the pseudo expansion of the derive attribute.
/// See [`crate::builtin_attr_macro::derive_attr_expand`].
fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::ExpandDatabase) -> bool;
}
impl HirFileIdExt for HirFileId {
@ -241,12 +225,6 @@ impl HirFileIdExt for HirFileId {
}
}
fn call_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>> {
let macro_file = self.macro_file()?;
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
Some(loc.to_node(db))
}
fn original_call_node(self, db: &dyn db::ExpandDatabase) -> Option<InRealFile<SyntaxNode>> {
let mut call = db.lookup_intern_macro_call(self.macro_file()?.macro_call_id).to_node(db);
loop {
@ -278,77 +256,34 @@ impl HirFileIdExt for HirFileId {
};
Some(attr.with_value(ast::Attr::cast(attr.value.clone())?))
}
fn is_custom_derive(&self, db: &dyn db::ExpandDatabase) -> bool {
match self.macro_file() {
Some(macro_file) => {
matches!(
db.lookup_intern_macro_call(macro_file.macro_call_id).def.kind,
MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _)
)
}
None => false,
}
}
fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> bool {
match self.macro_file() {
Some(macro_file) => {
matches!(
db.lookup_intern_macro_call(macro_file.macro_call_id).def.kind,
MacroDefKind::BuiltInDerive(..)
)
}
None => false,
}
}
fn is_include_macro(&self, db: &dyn db::ExpandDatabase) -> bool {
match self.macro_file() {
Some(macro_file) => {
db.lookup_intern_macro_call(macro_file.macro_call_id).def.is_include()
}
_ => false,
}
}
fn is_eager(&self, db: &dyn db::ExpandDatabase) -> bool {
match self.macro_file() {
Some(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
matches!(loc.def.kind, MacroDefKind::BuiltInEager(..))
}
_ => false,
}
}
fn is_attr_macro(&self, db: &dyn db::ExpandDatabase) -> bool {
match self.macro_file() {
Some(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
matches!(loc.kind, MacroCallKind::Attr { .. })
}
_ => false,
}
}
fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::ExpandDatabase) -> bool {
match self.macro_file() {
Some(macro_file) => {
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
loc.def.is_attribute_derive()
}
None => false,
}
}
}
pub trait MacroFileIdExt {
fn expansion_level(self, db: &dyn db::ExpandDatabase) -> u32;
/// If this is a macro call, returns the syntax node of the call.
fn call_node(self, db: &dyn db::ExpandDatabase) -> InFile<SyntaxNode>;
fn expansion_info(self, db: &dyn db::ExpandDatabase) -> ExpansionInfo;
fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> bool;
fn is_custom_derive(&self, db: &dyn db::ExpandDatabase) -> bool;
/// Return whether this file is an include macro
fn is_include_macro(&self, db: &dyn db::ExpandDatabase) -> bool;
fn is_eager(&self, db: &dyn db::ExpandDatabase) -> bool;
/// Return whether this file is an attr macro
fn is_attr_macro(&self, db: &dyn db::ExpandDatabase) -> bool;
/// Return whether this file is the pseudo expansion of the derive attribute.
/// See [`crate::builtin_attr_macro::derive_attr_expand`].
fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::ExpandDatabase) -> bool;
}
impl MacroFileIdExt for MacroFileId {
fn call_node(self, db: &dyn db::ExpandDatabase) -> InFile<SyntaxNode> {
db.lookup_intern_macro_call(self.macro_call_id).to_node(db)
}
fn expansion_level(self, db: &dyn db::ExpandDatabase) -> u32 {
let mut level = 0;
let mut macro_file = self;
@ -367,6 +302,39 @@ impl MacroFileIdExt for MacroFileId {
fn expansion_info(self, db: &dyn db::ExpandDatabase) -> ExpansionInfo {
ExpansionInfo::new(db, self)
}
fn is_custom_derive(&self, db: &dyn db::ExpandDatabase) -> bool {
matches!(
db.lookup_intern_macro_call(self.macro_call_id).def.kind,
MacroDefKind::ProcMacro(_, ProcMacroKind::CustomDerive, _)
)
}
fn is_builtin_derive(&self, db: &dyn db::ExpandDatabase) -> bool {
matches!(
db.lookup_intern_macro_call(self.macro_call_id).def.kind,
MacroDefKind::BuiltInDerive(..)
)
}
fn is_include_macro(&self, db: &dyn db::ExpandDatabase) -> bool {
db.lookup_intern_macro_call(self.macro_call_id).def.is_include()
}
fn is_eager(&self, db: &dyn db::ExpandDatabase) -> bool {
let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id);
matches!(loc.def.kind, MacroDefKind::BuiltInEager(..))
}
fn is_attr_macro(&self, db: &dyn db::ExpandDatabase) -> bool {
let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id);
matches!(loc.kind, MacroCallKind::Attr { .. })
}
fn is_derive_attr_pseudo_expansion(&self, db: &dyn db::ExpandDatabase) -> bool {
let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id);
loc.def.is_attribute_derive()
}
}
impl MacroDefId {
@ -653,14 +621,14 @@ impl ExpansionInfo {
Some(tokens.map(move |token| InMacroFile::new(self.expanded.file_id, token)))
}
/// Maps up the text range out of the expansion hierarchy back into the original file its from.
pub fn map_token_range_up(
/// Looks up the span at the given offset.
pub fn span_for_offset(
&self,
db: &dyn db::ExpandDatabase,
range: TextRange,
offset: TextSize,
) -> (FileRange, SyntaxContextId) {
debug_assert!(self.expanded.value.text_range().contains_range(range));
let span = self.exp_map.span_at(range.start());
debug_assert!(self.expanded.value.text_range().contains(offset));
let span = self.exp_map.span_at(offset);
let anchor_offset = db
.ast_id_map(span.anchor.file_id.into())
.get_erased(span.anchor.ast_id)