Unnamed repository; edit this file 'description' to name the repository.
Diffstat (limited to 'crates/hir-expand/src/files.rs')
-rw-r--r--crates/hir-expand/src/files.rs155
1 files changed, 97 insertions, 58 deletions
diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs
index 45875d9498..57a7fa5ec3 100644
--- a/crates/hir-expand/src/files.rs
+++ b/crates/hir-expand/src/files.rs
@@ -1,15 +1,14 @@
use std::iter;
use base_db::{
- span::{HirFileId, HirFileIdRepr, MacroFile, SyntaxContextId},
- FileRange,
+ span::{HirFileId, HirFileIdRepr, MacroFileId, SyntaxContextId},
+ FileId, FileRange,
};
use either::Either;
use syntax::{AstNode, SyntaxNode, SyntaxToken, TextRange};
use crate::{db, ExpansionInfo, HirFileIdExt as _};
-// FIXME: Make an InRealFile wrapper
/// `InFile<T>` stores a value of `T` inside a particular file/syntax tree.
///
/// Typical usages are:
@@ -18,55 +17,91 @@ use crate::{db, ExpansionInfo, HirFileIdExt as _};
/// * `InFile<ast::FnDef>` -- ast node in a file
/// * `InFile<TextSize>` -- offset in a file
#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
-pub struct InFile<T> {
- pub file_id: HirFileId,
+pub struct InFileWrapper<FileKind, T> {
+ pub file_id: FileKind,
pub value: T,
}
+pub type InFile<T> = InFileWrapper<HirFileId, T>;
+pub type InMacroFile<T> = InFileWrapper<MacroFileId, T>;
+pub type InRealFile<T> = InFileWrapper<FileId, T>;
-impl<T> InFile<T> {
- pub fn new(file_id: HirFileId, value: T) -> InFile<T> {
- InFile { file_id, value }
+impl<FileKind, T> InFileWrapper<FileKind, T> {
+ pub fn new(file_id: FileKind, value: T) -> Self {
+ Self { file_id, value }
}
- pub fn with_value<U>(&self, value: U) -> InFile<U> {
- InFile::new(self.file_id, value)
+ pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> InFileWrapper<FileKind, U> {
+ InFileWrapper::new(self.file_id, f(self.value))
}
+}
- pub fn map<F: FnOnce(T) -> U, U>(self, f: F) -> InFile<U> {
- InFile::new(self.file_id, f(self.value))
+impl<FileKind: Copy, T> InFileWrapper<FileKind, T> {
+ pub fn with_value<U>(&self, value: U) -> InFileWrapper<FileKind, U> {
+ InFileWrapper::new(self.file_id, value)
}
- pub fn as_ref(&self) -> InFile<&T> {
+ pub fn as_ref(&self) -> InFileWrapper<FileKind, &T> {
self.with_value(&self.value)
}
+}
- pub fn file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
- db.parse_or_expand(self.file_id)
+impl<FileKind: Copy, T: Clone> InFileWrapper<FileKind, &T> {
+ pub fn cloned(&self) -> InFileWrapper<FileKind, T> {
+ self.with_value(self.value.clone())
}
}
-impl<T: Clone> InFile<&T> {
- pub fn cloned(&self) -> InFile<T> {
- self.with_value(self.value.clone())
+impl<T> From<InMacroFile<T>> for InFile<T> {
+ fn from(InMacroFile { file_id, value }: InMacroFile<T>) -> Self {
+ InFile { file_id: file_id.into(), value }
}
}
-impl<T> InFile<Option<T>> {
- pub fn transpose(self) -> Option<InFile<T>> {
- let value = self.value?;
- Some(InFile::new(self.file_id, value))
+impl<T> From<InRealFile<T>> for InFile<T> {
+ fn from(InRealFile { file_id, value }: InRealFile<T>) -> Self {
+ InFile { file_id: file_id.into(), value }
}
}
-impl<L, R> InFile<Either<L, R>> {
- pub fn transpose(self) -> Either<InFile<L>, InFile<R>> {
+// region:transpose impls
+
+impl<FileKind, T> InFileWrapper<FileKind, Option<T>> {
+ pub fn transpose(self) -> Option<InFileWrapper<FileKind, T>> {
+ Some(InFileWrapper::new(self.file_id, self.value?))
+ }
+}
+
+impl<FileKind, L, R> InFileWrapper<FileKind, Either<L, R>> {
+ pub fn transpose(self) -> Either<InFileWrapper<FileKind, L>, InFileWrapper<FileKind, R>> {
match self.value {
- Either::Left(l) => Either::Left(InFile::new(self.file_id, l)),
- Either::Right(r) => Either::Right(InFile::new(self.file_id, r)),
+ Either::Left(l) => Either::Left(InFileWrapper::new(self.file_id, l)),
+ Either::Right(r) => Either::Right(InFileWrapper::new(self.file_id, r)),
}
}
}
+// endregion:transpose impls
+
+// region:specific impls
+
+impl<T> InFile<T> {
+ pub fn file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
+ db.parse_or_expand(self.file_id)
+ }
+}
+
+impl<T> InRealFile<T> {
+ pub fn file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
+ db.parse(self.file_id).syntax_node()
+ }
+}
+
+impl<T> InMacroFile<T> {
+ pub fn file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
+ db.parse_macro_expansion(self.file_id).value.0.syntax_node()
+ }
+}
+
impl InFile<&SyntaxNode> {
pub fn ancestors_with_macros(
self,
@@ -159,11 +194,17 @@ impl InFile<&SyntaxNode> {
}
}
- pub fn original_syntax_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>> {
+ pub fn original_syntax_node(
+ self,
+ db: &dyn db::ExpandDatabase,
+ ) -> Option<InRealFile<SyntaxNode>> {
// This kind of upmapping can only be achieved in attribute expanded files,
// as we don't have node inputs otherwise and therefore can't find an `N` node in the input
- let Some(file_id) = self.file_id.macro_file() else {
- return Some(self.map(Clone::clone));
+ let file_id = match self.file_id.repr() {
+ HirFileIdRepr::FileId(file_id) => {
+ return Some(InRealFile { file_id, value: self.value.clone() })
+ }
+ HirFileIdRepr::MacroFile(m) => m,
};
if !self.file_id.is_attr_macro(db) {
return None;
@@ -182,7 +223,7 @@ impl InFile<&SyntaxNode> {
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)?;
- Some(InFile::new(file_id.into(), value))
+ Some(InRealFile::new(file_id, value))
}
}
@@ -230,29 +271,11 @@ impl InFile<SyntaxToken> {
}
}
-#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)]
-pub struct InMacroFile<T> {
- pub file_id: MacroFile,
- pub value: T,
-}
-
-impl<T> From<InMacroFile<T>> for InFile<T> {
- fn from(macro_file: InMacroFile<T>) -> Self {
- InFile { file_id: macro_file.file_id.into(), value: macro_file.value }
- }
-}
-
-pub fn ascend_range_up_macros(
- db: &dyn db::ExpandDatabase,
- range: InFile<TextRange>,
-) -> (FileRange, SyntaxContextId) {
- match range.file_id.repr() {
- HirFileIdRepr::FileId(file_id) => {
- (FileRange { file_id, range: range.value }, SyntaxContextId::ROOT)
- }
- HirFileIdRepr::MacroFile(m) => {
- ExpansionInfo::new(db, m).map_token_range_up(db, range.value)
- }
+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) = ascend_range_up_macros(db, self);
+ range
}
}
@@ -261,12 +284,14 @@ impl<N: AstNode> InFile<N> {
self.value.syntax().descendants().filter_map(T::cast).map(move |n| self.with_value(n))
}
- // FIXME: this should return `Option<InFileNotHirFile<N>>`
- pub fn original_ast_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<N>> {
+ pub fn original_ast_node(self, db: &dyn db::ExpandDatabase) -> Option<InRealFile<N>> {
// This kind of upmapping can only be achieved in attribute expanded files,
// as we don't have node inputs otherwise and therefore can't find an `N` node in the input
- let Some(file_id) = self.file_id.macro_file() else {
- return Some(self);
+ let file_id = match self.file_id.repr() {
+ HirFileIdRepr::FileId(file_id) => {
+ return Some(InRealFile { file_id, value: self.value })
+ }
+ HirFileIdRepr::MacroFile(m) => m,
};
if !self.file_id.is_attr_macro(db) {
return None;
@@ -284,10 +309,24 @@ impl<N: AstNode> InFile<N> {
// 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)?;
- return Some(InFile::new(file_id.into(), value));
+ Some(InRealFile::new(file_id, value))
}
pub fn syntax(&self) -> InFile<&SyntaxNode> {
self.with_value(self.value.syntax())
}
}
+
+fn ascend_range_up_macros(
+ db: &dyn db::ExpandDatabase,
+ range: InFile<TextRange>,
+) -> (FileRange, SyntaxContextId) {
+ match range.file_id.repr() {
+ HirFileIdRepr::FileId(file_id) => {
+ (FileRange { file_id, range: range.value }, SyntaxContextId::ROOT)
+ }
+ HirFileIdRepr::MacroFile(m) => {
+ ExpansionInfo::new(db, m).map_token_range_up(db, range.value)
+ }
+ }
+}