mirror of
https://github.com/rust-lang/rust-analyzer
synced 2024-12-27 13:33:31 +00:00
spans always come from real file
This commit is contained in:
parent
394d11b0fa
commit
30093a6d81
57 changed files with 1369 additions and 1224 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -1752,6 +1752,7 @@ dependencies = [
|
||||||
"always-assert",
|
"always-assert",
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"crossbeam-channel",
|
"crossbeam-channel",
|
||||||
|
"itertools 0.12.0",
|
||||||
"jod-thread",
|
"jod-thread",
|
||||||
"libc",
|
"libc",
|
||||||
"miow",
|
"miow",
|
||||||
|
|
|
@ -35,11 +35,15 @@ impl SyntaxContextId {
|
||||||
// FIXME: This is very much UB, salsa exposes no way to create an InternId in a const context
|
// FIXME: This is very much UB, salsa exposes no way to create an InternId in a const context
|
||||||
// currently (which kind of makes sense but we need it here!)
|
// currently (which kind of makes sense but we need it here!)
|
||||||
pub const SELF_REF: Self = SyntaxContextId(unsafe { core::mem::transmute(!0u32) });
|
pub const SELF_REF: Self = SyntaxContextId(unsafe { core::mem::transmute(!0u32) });
|
||||||
|
|
||||||
|
pub fn is_root(self) -> bool {
|
||||||
|
self == Self::ROOT
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct SpanAnchor {
|
pub struct SpanAnchor {
|
||||||
pub file_id: HirFileId,
|
pub file_id: FileId,
|
||||||
pub ast_id: ErasedFileAstId,
|
pub ast_id: ErasedFileAstId,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +54,7 @@ impl fmt::Debug for SpanAnchor {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl tt::SpanAnchor for SpanAnchor {
|
impl tt::SpanAnchor for SpanAnchor {
|
||||||
const DUMMY: Self = SpanAnchor { file_id: HirFileId(0), ast_id: ROOT_ERASED_FILE_AST_ID };
|
const DUMMY: Self = SpanAnchor { file_id: FileId(0), ast_id: ROOT_ERASED_FILE_AST_ID };
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Input to the analyzer is a set of files, where each file is identified by
|
/// Input to the analyzer is a set of files, where each file is identified by
|
||||||
|
@ -101,7 +105,6 @@ impl fmt::Debug for HirFileId {
|
||||||
pub struct MacroFile {
|
pub struct MacroFile {
|
||||||
pub macro_call_id: MacroCallId,
|
pub macro_call_id: MacroCallId,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `MacroCallId` identifies a particular macro invocation, like
|
/// `MacroCallId` identifies a particular macro invocation, like
|
||||||
/// `println!("Hello, {}", world)`.
|
/// `println!("Hello, {}", world)`.
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
use arbitrary::{Arbitrary, Unstructured};
|
use arbitrary::{Arbitrary, Unstructured};
|
||||||
use expect_test::{expect, Expect};
|
use expect_test::{expect, Expect};
|
||||||
use mbe::syntax_node_to_token_tree;
|
use mbe::{syntax_node_to_token_tree, SpanMapper};
|
||||||
use syntax::{ast, AstNode};
|
use syntax::{ast, AstNode};
|
||||||
use tt::{SpanAnchor, SyntaxContext};
|
use tt::{SpanAnchor, SyntaxContext};
|
||||||
|
|
||||||
use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
|
use crate::{CfgAtom, CfgExpr, CfgOptions, DnfExpr};
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
|
||||||
struct DummyFile;
|
struct DummyFile;
|
||||||
impl SpanAnchor for DummyFile {
|
impl SpanAnchor for DummyFile {
|
||||||
const DUMMY: Self = DummyFile;
|
const DUMMY: Self = DummyFile;
|
||||||
|
@ -17,15 +17,18 @@ impl SyntaxContext for DummyCtx {
|
||||||
const DUMMY: Self = DummyCtx;
|
const DUMMY: Self = DummyCtx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct NoOpMap;
|
||||||
|
|
||||||
|
impl SpanMapper<tt::SpanData<DummyFile, DummyCtx>> for NoOpMap {
|
||||||
|
fn span_for(&self, range: syntax::TextRange) -> tt::SpanData<DummyFile, DummyCtx> {
|
||||||
|
tt::SpanData { range, anchor: DummyFile, ctx: DummyCtx }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn assert_parse_result(input: &str, expected: CfgExpr) {
|
fn assert_parse_result(input: &str, expected: CfgExpr) {
|
||||||
let source_file = ast::SourceFile::parse(input).ok().unwrap();
|
let source_file = ast::SourceFile::parse(input).ok().unwrap();
|
||||||
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
||||||
let tt = syntax_node_to_token_tree::<_, DummyCtx>(
|
let tt = syntax_node_to_token_tree(tt.syntax(), NoOpMap);
|
||||||
tt.syntax(),
|
|
||||||
DummyFile,
|
|
||||||
0.into(),
|
|
||||||
&Default::default(),
|
|
||||||
);
|
|
||||||
let cfg = CfgExpr::parse(&tt);
|
let cfg = CfgExpr::parse(&tt);
|
||||||
assert_eq!(cfg, expected);
|
assert_eq!(cfg, expected);
|
||||||
}
|
}
|
||||||
|
@ -33,12 +36,7 @@ fn assert_parse_result(input: &str, expected: CfgExpr) {
|
||||||
fn check_dnf(input: &str, expect: Expect) {
|
fn check_dnf(input: &str, expect: Expect) {
|
||||||
let source_file = ast::SourceFile::parse(input).ok().unwrap();
|
let source_file = ast::SourceFile::parse(input).ok().unwrap();
|
||||||
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
||||||
let tt = syntax_node_to_token_tree::<_, DummyCtx>(
|
let tt = syntax_node_to_token_tree(tt.syntax(), NoOpMap);
|
||||||
tt.syntax(),
|
|
||||||
DummyFile,
|
|
||||||
0.into(),
|
|
||||||
&Default::default(),
|
|
||||||
);
|
|
||||||
let cfg = CfgExpr::parse(&tt);
|
let cfg = CfgExpr::parse(&tt);
|
||||||
let actual = format!("#![cfg({})]", DnfExpr::new(cfg));
|
let actual = format!("#![cfg({})]", DnfExpr::new(cfg));
|
||||||
expect.assert_eq(&actual);
|
expect.assert_eq(&actual);
|
||||||
|
@ -47,12 +45,7 @@ fn check_dnf(input: &str, expect: Expect) {
|
||||||
fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
|
fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
|
||||||
let source_file = ast::SourceFile::parse(input).ok().unwrap();
|
let source_file = ast::SourceFile::parse(input).ok().unwrap();
|
||||||
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
||||||
let tt = syntax_node_to_token_tree::<_, DummyCtx>(
|
let tt = syntax_node_to_token_tree(tt.syntax(), NoOpMap);
|
||||||
tt.syntax(),
|
|
||||||
DummyFile,
|
|
||||||
0.into(),
|
|
||||||
&Default::default(),
|
|
||||||
);
|
|
||||||
let cfg = CfgExpr::parse(&tt);
|
let cfg = CfgExpr::parse(&tt);
|
||||||
let dnf = DnfExpr::new(cfg);
|
let dnf = DnfExpr::new(cfg);
|
||||||
let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
|
let why_inactive = dnf.why_inactive(opts).unwrap().to_string();
|
||||||
|
@ -63,12 +56,7 @@ fn check_why_inactive(input: &str, opts: &CfgOptions, expect: Expect) {
|
||||||
fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
|
fn check_enable_hints(input: &str, opts: &CfgOptions, expected_hints: &[&str]) {
|
||||||
let source_file = ast::SourceFile::parse(input).ok().unwrap();
|
let source_file = ast::SourceFile::parse(input).ok().unwrap();
|
||||||
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
||||||
let tt = syntax_node_to_token_tree::<_, DummyCtx>(
|
let tt = syntax_node_to_token_tree(tt.syntax(), NoOpMap);
|
||||||
tt.syntax(),
|
|
||||||
DummyFile,
|
|
||||||
0.into(),
|
|
||||||
&Default::default(),
|
|
||||||
);
|
|
||||||
let cfg = CfgExpr::parse(&tt);
|
let cfg = CfgExpr::parse(&tt);
|
||||||
let dnf = DnfExpr::new(cfg);
|
let dnf = DnfExpr::new(cfg);
|
||||||
let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>();
|
let hints = dnf.compute_enable_hints(opts).map(|diff| diff.to_string()).collect::<Vec<_>>();
|
||||||
|
|
|
@ -7,10 +7,7 @@ mod tests;
|
||||||
|
|
||||||
use std::{hash::Hash, ops, slice::Iter as SliceIter};
|
use std::{hash::Hash, ops, slice::Iter as SliceIter};
|
||||||
|
|
||||||
use base_db::{
|
use base_db::CrateId;
|
||||||
span::{ErasedFileAstId, SpanAnchor},
|
|
||||||
CrateId,
|
|
||||||
};
|
|
||||||
use cfg::{CfgExpr, CfgOptions};
|
use cfg::{CfgExpr, CfgOptions};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
|
@ -31,8 +28,8 @@ use crate::{
|
||||||
lang_item::LangItem,
|
lang_item::LangItem,
|
||||||
nameres::{ModuleOrigin, ModuleSource},
|
nameres::{ModuleOrigin, ModuleSource},
|
||||||
src::{HasChildSource, HasSource},
|
src::{HasChildSource, HasSource},
|
||||||
AdtId, AssocItemLoc, AttrDefId, EnumId, GenericDefId, GenericParamId, ItemLoc,
|
AdtId, AssocItemLoc, AttrDefId, EnumId, GenericParamId, ItemLoc, LocalEnumVariantId,
|
||||||
LocalEnumVariantId, LocalFieldId, Lookup, MacroId, VariantId,
|
LocalFieldId, Lookup, MacroId, VariantId,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
#[derive(Default, Debug, Clone, PartialEq, Eq)]
|
||||||
|
@ -419,43 +416,30 @@ impl AttrsWithOwner {
|
||||||
AttrDefId::FunctionId(it) => attrs_from_item_tree_assoc(db, it),
|
AttrDefId::FunctionId(it) => attrs_from_item_tree_assoc(db, it),
|
||||||
AttrDefId::TypeAliasId(it) => attrs_from_item_tree_assoc(db, it),
|
AttrDefId::TypeAliasId(it) => attrs_from_item_tree_assoc(db, it),
|
||||||
AttrDefId::GenericParamId(it) => {
|
AttrDefId::GenericParamId(it) => {
|
||||||
let ast_id = |p| match p {
|
// FIXME: we could probably just make these relative to the params?
|
||||||
GenericDefId::AdtId(AdtId::StructId(it)) => {
|
|
||||||
erased_ast_id_from_item_tree(db, it)
|
|
||||||
}
|
|
||||||
GenericDefId::AdtId(AdtId::EnumId(it)) => erased_ast_id_from_item_tree(db, it),
|
|
||||||
GenericDefId::AdtId(AdtId::UnionId(it)) => erased_ast_id_from_item_tree(db, it),
|
|
||||||
GenericDefId::TraitId(it) => erased_ast_id_from_item_tree(db, it),
|
|
||||||
GenericDefId::TraitAliasId(it) => erased_ast_id_from_item_tree(db, it),
|
|
||||||
GenericDefId::ImplId(it) => erased_ast_id_from_item_tree(db, it),
|
|
||||||
GenericDefId::EnumVariantId(it) => erased_ast_id_from_item_tree(db, it.parent),
|
|
||||||
GenericDefId::TypeAliasId(it) => erased_ast_id_from_item_tree_assoc(db, it),
|
|
||||||
GenericDefId::FunctionId(it) => erased_ast_id_from_item_tree_assoc(db, it),
|
|
||||||
GenericDefId::ConstId(it) => erased_ast_id_from_item_tree_assoc(db, it),
|
|
||||||
};
|
|
||||||
match it {
|
match it {
|
||||||
GenericParamId::ConstParamId(it) => {
|
GenericParamId::ConstParamId(it) => {
|
||||||
let src = it.parent().child_source(db);
|
let src = it.parent().child_source(db);
|
||||||
RawAttrs::from_attrs_owner(
|
RawAttrs::from_attrs_owner(
|
||||||
db.upcast(),
|
db.upcast(),
|
||||||
SpanAnchor { file_id: src.file_id, ast_id: ast_id(it.parent()) },
|
|
||||||
src.with_value(&src.value[it.local_id()]),
|
src.with_value(&src.value[it.local_id()]),
|
||||||
|
db.span_map(src.file_id).as_ref(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
GenericParamId::TypeParamId(it) => {
|
GenericParamId::TypeParamId(it) => {
|
||||||
let src = it.parent().child_source(db);
|
let src = it.parent().child_source(db);
|
||||||
RawAttrs::from_attrs_owner(
|
RawAttrs::from_attrs_owner(
|
||||||
db.upcast(),
|
db.upcast(),
|
||||||
SpanAnchor { file_id: src.file_id, ast_id: ast_id(it.parent()) },
|
|
||||||
src.with_value(&src.value[it.local_id()]),
|
src.with_value(&src.value[it.local_id()]),
|
||||||
|
db.span_map(src.file_id).as_ref(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
GenericParamId::LifetimeParamId(it) => {
|
GenericParamId::LifetimeParamId(it) => {
|
||||||
let src = it.parent.child_source(db);
|
let src = it.parent.child_source(db);
|
||||||
RawAttrs::from_attrs_owner(
|
RawAttrs::from_attrs_owner(
|
||||||
db.upcast(),
|
db.upcast(),
|
||||||
SpanAnchor { file_id: src.file_id, ast_id: ast_id(it.parent) },
|
|
||||||
src.with_value(&src.value[it.local_id]),
|
src.with_value(&src.value[it.local_id]),
|
||||||
|
db.span_map(src.file_id).as_ref(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -663,26 +647,6 @@ fn any_has_attrs(
|
||||||
id.lookup(db).source(db).map(ast::AnyHasAttrs::new)
|
id.lookup(db).source(db).map(ast::AnyHasAttrs::new)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn erased_ast_id_from_item_tree<N: ItemTreeNode>(
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
lookup: impl Lookup<Data = ItemLoc<N>>,
|
|
||||||
) -> ErasedFileAstId {
|
|
||||||
let id = lookup.lookup(db).id;
|
|
||||||
let tree = id.item_tree(db);
|
|
||||||
let mod_item = N::id_to_mod_item(id.value);
|
|
||||||
mod_item.ast_id(&tree).erase()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn erased_ast_id_from_item_tree_assoc<N: ItemTreeNode>(
|
|
||||||
db: &dyn DefDatabase,
|
|
||||||
lookup: impl Lookup<Data = AssocItemLoc<N>>,
|
|
||||||
) -> ErasedFileAstId {
|
|
||||||
let id = lookup.lookup(db).id;
|
|
||||||
let tree = id.item_tree(db);
|
|
||||||
let mod_item = N::id_to_mod_item(id.value);
|
|
||||||
mod_item.ast_id(&tree).erase()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn attrs_from_item_tree<N: ItemTreeNode>(db: &dyn DefDatabase, id: ItemTreeId<N>) -> RawAttrs {
|
fn attrs_from_item_tree<N: ItemTreeNode>(db: &dyn DefDatabase, id: ItemTreeId<N>) -> RawAttrs {
|
||||||
let tree = id.item_tree(db);
|
let tree = id.item_tree(db);
|
||||||
let mod_item = N::id_to_mod_item(id.value);
|
let mod_item = N::id_to_mod_item(id.value);
|
||||||
|
|
|
@ -1,27 +1,19 @@
|
||||||
//! This module contains tests for doc-expression parsing.
|
//! This module contains tests for doc-expression parsing.
|
||||||
//! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`.
|
//! Currently, it tests `#[doc(hidden)]` and `#[doc(alias)]`.
|
||||||
|
|
||||||
use base_db::span::SpanAnchor;
|
use base_db::FileId;
|
||||||
|
use hir_expand::span::{RealSpanMap, SpanMapRef};
|
||||||
use mbe::syntax_node_to_token_tree;
|
use mbe::syntax_node_to_token_tree;
|
||||||
use syntax::{ast, AstNode};
|
use syntax::{ast, AstNode};
|
||||||
use tt::{SpanAnchor as _, SyntaxContext};
|
|
||||||
|
|
||||||
use crate::attr::{DocAtom, DocExpr};
|
use crate::attr::{DocAtom, DocExpr};
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
|
|
||||||
struct DummyCtx;
|
|
||||||
impl SyntaxContext for DummyCtx {
|
|
||||||
const DUMMY: Self = DummyCtx;
|
|
||||||
}
|
|
||||||
|
|
||||||
fn assert_parse_result(input: &str, expected: DocExpr) {
|
fn assert_parse_result(input: &str, expected: DocExpr) {
|
||||||
let source_file = ast::SourceFile::parse(input).ok().unwrap();
|
let source_file = ast::SourceFile::parse(input).ok().unwrap();
|
||||||
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
||||||
let tt = syntax_node_to_token_tree::<_, DummyCtx>(
|
let tt = syntax_node_to_token_tree(
|
||||||
tt.syntax(),
|
tt.syntax(),
|
||||||
SpanAnchor::DUMMY,
|
SpanMapRef::RealSpanMap(&RealSpanMap::empty(FileId(0))),
|
||||||
0.into(),
|
|
||||||
&Default::default(),
|
|
||||||
);
|
);
|
||||||
let cfg = DocExpr::parse(&tt);
|
let cfg = DocExpr::parse(&tt);
|
||||||
assert_eq!(cfg, expected);
|
assert_eq!(cfg, expected);
|
||||||
|
|
|
@ -1,28 +1,24 @@
|
||||||
//! Macro expansion utilities.
|
//! Macro expansion utilities.
|
||||||
|
|
||||||
use base_db::{
|
use base_db::CrateId;
|
||||||
span::{SpanAnchor, ROOT_ERASED_FILE_AST_ID},
|
|
||||||
CrateId,
|
|
||||||
};
|
|
||||||
use cfg::CfgOptions;
|
use cfg::CfgOptions;
|
||||||
use drop_bomb::DropBomb;
|
use drop_bomb::DropBomb;
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
attrs::RawAttrs, mod_path::ModPath, ExpandError, ExpandResult, HirFileId, InFile, MacroCallId,
|
attrs::RawAttrs, mod_path::ModPath, span::SpanMap, ExpandError, ExpandResult, HirFileId,
|
||||||
SpanMap, UnresolvedMacro,
|
InFile, MacroCallId,
|
||||||
};
|
};
|
||||||
use limit::Limit;
|
use limit::Limit;
|
||||||
use syntax::{ast, Parse, SyntaxNode};
|
use syntax::{ast, Parse, SyntaxNode};
|
||||||
use triomphe::Arc;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
attr::Attrs, db::DefDatabase, lower::LowerCtx, macro_id_to_def_id, path::Path, AsMacroCall,
|
attr::Attrs, db::DefDatabase, lower::LowerCtx, macro_id_to_def_id, path::Path, AsMacroCall,
|
||||||
MacroId, ModuleId,
|
MacroId, ModuleId, UnresolvedMacro,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Expander {
|
pub struct Expander {
|
||||||
cfg_options: CfgOptions,
|
cfg_options: CfgOptions,
|
||||||
hygiene: Arc<SpanMap>,
|
hygiene: SpanMap,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
pub(crate) current_file_id: HirFileId,
|
pub(crate) current_file_id: HirFileId,
|
||||||
pub(crate) module: ModuleId,
|
pub(crate) module: ModuleId,
|
||||||
|
@ -122,17 +118,7 @@ impl Expander {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
|
pub(crate) fn parse_attrs(&self, db: &dyn DefDatabase, owner: &dyn ast::HasAttrs) -> Attrs {
|
||||||
Attrs::filter(
|
Attrs::filter(db, self.krate, RawAttrs::new(db.upcast(), owner, self.hygiene.as_ref()))
|
||||||
db,
|
|
||||||
self.krate,
|
|
||||||
RawAttrs::new(
|
|
||||||
db.upcast(),
|
|
||||||
// Usin `ROOT_ERASED_FILE_AST_ID` here is fine as this is only used for cfg checking
|
|
||||||
SpanAnchor { file_id: self.current_file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
|
|
||||||
owner,
|
|
||||||
&self.hygiene,
|
|
||||||
),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn cfg_options(&self) -> &CfgOptions {
|
pub(crate) fn cfg_options(&self) -> &CfgOptions {
|
||||||
|
|
|
@ -586,7 +586,7 @@ fn find_local_import_locations(
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
use base_db::fixture::WithFixture;
|
use base_db::fixture::WithFixture;
|
||||||
use hir_expand::SpanMap;
|
use hir_expand::db::ExpandDatabase;
|
||||||
use syntax::ast::AstNode;
|
use syntax::ast::AstNode;
|
||||||
|
|
||||||
use crate::test_db::TestDB;
|
use crate::test_db::TestDB;
|
||||||
|
@ -608,7 +608,8 @@ mod tests {
|
||||||
let parsed_path_file = syntax::SourceFile::parse(&format!("use {path};"));
|
let parsed_path_file = syntax::SourceFile::parse(&format!("use {path};"));
|
||||||
let ast_path =
|
let ast_path =
|
||||||
parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
|
parsed_path_file.syntax_node().descendants().find_map(syntax::ast::Path::cast).unwrap();
|
||||||
let mod_path = ModPath::from_src(&db, ast_path, &SpanMap::default()).unwrap();
|
let mod_path =
|
||||||
|
ModPath::from_src(&db, ast_path, db.span_map(pos.file_id.into()).as_ref()).unwrap();
|
||||||
|
|
||||||
let def_map = module.def_map(&db);
|
let def_map = module.def_map(&db);
|
||||||
let resolved = def_map
|
let resolved = def_map
|
||||||
|
|
|
@ -112,6 +112,7 @@ pub struct ItemScope {
|
||||||
#[derive(Debug, PartialEq, Eq)]
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
struct DeriveMacroInvocation {
|
struct DeriveMacroInvocation {
|
||||||
attr_id: AttrId,
|
attr_id: AttrId,
|
||||||
|
/// The `#[derive]` call
|
||||||
attr_call_id: MacroCallId,
|
attr_call_id: MacroCallId,
|
||||||
derive_call_ids: SmallVec<[Option<MacroCallId>; 1]>,
|
derive_call_ids: SmallVec<[Option<MacroCallId>; 1]>,
|
||||||
}
|
}
|
||||||
|
@ -401,6 +402,14 @@ impl ItemScope {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn derive_macro_invoc(
|
||||||
|
&self,
|
||||||
|
ast_id: AstId<ast::Adt>,
|
||||||
|
attr_id: AttrId,
|
||||||
|
) -> Option<MacroCallId> {
|
||||||
|
Some(self.derive_macros.get(&ast_id)?.iter().find(|it| it.attr_id == attr_id)?.attr_call_id)
|
||||||
|
}
|
||||||
|
|
||||||
// FIXME: This is only used in collection, we should move the relevant parts of it out of ItemScope
|
// FIXME: This is only used in collection, we should move the relevant parts of it out of ItemScope
|
||||||
pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
|
pub(crate) fn unnamed_trait_vis(&self, tr: TraitId) -> Option<Visibility> {
|
||||||
self.unnamed_trait_imports.get(&tr).copied().map(|(a, _)| a)
|
self.unnamed_trait_imports.get(&tr).copied().map(|(a, _)| a)
|
||||||
|
|
|
@ -43,10 +43,7 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
use ast::{AstNode, HasName, StructKind};
|
use ast::{AstNode, HasName, StructKind};
|
||||||
use base_db::{
|
use base_db::{span::SyntaxContextId, CrateId};
|
||||||
span::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID},
|
|
||||||
CrateId,
|
|
||||||
};
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
ast_id_map::{AstIdNode, FileAstId},
|
ast_id_map::{AstIdNode, FileAstId},
|
||||||
|
@ -121,7 +118,7 @@ impl ItemTree {
|
||||||
let mut item_tree = match_ast! {
|
let mut item_tree = match_ast! {
|
||||||
match syntax {
|
match syntax {
|
||||||
ast::SourceFile(file) => {
|
ast::SourceFile(file) => {
|
||||||
top_attrs = Some(RawAttrs::new(db.upcast(), SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID }, &file, ctx.span_map()));
|
top_attrs = Some(RawAttrs::new(db.upcast(), &file, ctx.span_map()));
|
||||||
ctx.lower_module_items(&file)
|
ctx.lower_module_items(&file)
|
||||||
},
|
},
|
||||||
ast::MacroItems(items) => {
|
ast::MacroItems(items) => {
|
||||||
|
@ -780,8 +777,8 @@ impl Use {
|
||||||
let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast());
|
let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast());
|
||||||
let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
|
let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
|
||||||
let hygiene = db.span_map(file_id);
|
let hygiene = db.span_map(file_id);
|
||||||
let (_, source_map) =
|
let (_, source_map) = lower::lower_use_tree(db, hygiene.as_ref(), ast_use_tree)
|
||||||
lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree");
|
.expect("failed to lower use tree");
|
||||||
source_map[index].clone()
|
source_map[index].clone()
|
||||||
}
|
}
|
||||||
/// Maps a `UseTree` contained in this import back to its AST node.
|
/// Maps a `UseTree` contained in this import back to its AST node.
|
||||||
|
@ -795,7 +792,9 @@ impl Use {
|
||||||
let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast());
|
let ast = InFile::new(file_id, self.ast_id).to_node(db.upcast());
|
||||||
let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
|
let ast_use_tree = ast.use_tree().expect("missing `use_tree`");
|
||||||
let hygiene = db.span_map(file_id);
|
let hygiene = db.span_map(file_id);
|
||||||
lower::lower_use_tree(db, &hygiene, ast_use_tree).expect("failed to lower use tree").1
|
lower::lower_use_tree(db, hygiene.as_ref(), ast_use_tree)
|
||||||
|
.expect("failed to lower use tree")
|
||||||
|
.1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
use std::collections::hash_map::Entry;
|
use std::collections::hash_map::Entry;
|
||||||
|
|
||||||
use base_db::span::ErasedFileAstId;
|
use hir_expand::{ast_id_map::AstIdMap, span::SpanMapRef, HirFileId};
|
||||||
use hir_expand::{ast_id_map::AstIdMap, HirFileId, SpanMap};
|
|
||||||
use syntax::ast::{self, HasModuleItem, HasTypeBounds};
|
use syntax::ast::{self, HasModuleItem, HasTypeBounds};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
@ -23,7 +22,6 @@ pub(super) struct Ctx<'a> {
|
||||||
tree: ItemTree,
|
tree: ItemTree,
|
||||||
source_ast_id_map: Arc<AstIdMap>,
|
source_ast_id_map: Arc<AstIdMap>,
|
||||||
body_ctx: crate::lower::LowerCtx<'a>,
|
body_ctx: crate::lower::LowerCtx<'a>,
|
||||||
file: HirFileId,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Ctx<'a> {
|
impl<'a> Ctx<'a> {
|
||||||
|
@ -33,11 +31,10 @@ impl<'a> Ctx<'a> {
|
||||||
tree: ItemTree::default(),
|
tree: ItemTree::default(),
|
||||||
source_ast_id_map: db.ast_id_map(file),
|
source_ast_id_map: db.ast_id_map(file),
|
||||||
body_ctx: crate::lower::LowerCtx::with_file_id(db, file),
|
body_ctx: crate::lower::LowerCtx::with_file_id(db, file),
|
||||||
file,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn span_map(&self) -> &SpanMap {
|
pub(super) fn span_map(&self) -> SpanMapRef<'_> {
|
||||||
self.body_ctx.span_map()
|
self.body_ctx.span_map()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,18 +78,9 @@ impl<'a> Ctx<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> ItemTree {
|
pub(super) fn lower_block(mut self, block: &ast::BlockExpr) -> ItemTree {
|
||||||
self.tree.attrs.insert(
|
self.tree
|
||||||
AttrOwner::TopLevel,
|
.attrs
|
||||||
RawAttrs::new(
|
.insert(AttrOwner::TopLevel, RawAttrs::new(self.db.upcast(), block, self.span_map()));
|
||||||
self.db.upcast(),
|
|
||||||
SpanAnchor {
|
|
||||||
file_id: self.file,
|
|
||||||
ast_id: self.source_ast_id_map.ast_id(block).erase(),
|
|
||||||
},
|
|
||||||
block,
|
|
||||||
self.span_map(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
self.tree.top_level = block
|
self.tree.top_level = block
|
||||||
.statements()
|
.statements()
|
||||||
.filter_map(|stmt| match stmt {
|
.filter_map(|stmt| match stmt {
|
||||||
|
@ -141,12 +129,7 @@ impl<'a> Ctx<'a> {
|
||||||
ast::Item::MacroDef(ast) => self.lower_macro_def(ast)?.into(),
|
ast::Item::MacroDef(ast) => self.lower_macro_def(ast)?.into(),
|
||||||
ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(),
|
ast::Item::ExternBlock(ast) => self.lower_extern_block(ast).into(),
|
||||||
};
|
};
|
||||||
let attrs = RawAttrs::new(
|
let attrs = RawAttrs::new(self.db.upcast(), item, self.span_map());
|
||||||
self.db.upcast(),
|
|
||||||
SpanAnchor { file_id: self.file, ast_id: mod_item.ast_id(&self.tree).erase() },
|
|
||||||
item,
|
|
||||||
self.span_map(),
|
|
||||||
);
|
|
||||||
self.add_attrs(mod_item.into(), attrs);
|
self.add_attrs(mod_item.into(), attrs);
|
||||||
|
|
||||||
Some(mod_item)
|
Some(mod_item)
|
||||||
|
@ -170,12 +153,7 @@ impl<'a> Ctx<'a> {
|
||||||
ast::AssocItem::Const(ast) => Some(self.lower_const(ast).into()),
|
ast::AssocItem::Const(ast) => Some(self.lower_const(ast).into()),
|
||||||
ast::AssocItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
|
ast::AssocItem::MacroCall(ast) => self.lower_macro_call(ast).map(Into::into),
|
||||||
}?;
|
}?;
|
||||||
let attrs = RawAttrs::new(
|
let attrs = RawAttrs::new(self.db.upcast(), item_node, self.span_map());
|
||||||
self.db.upcast(),
|
|
||||||
SpanAnchor { file_id: self.file, ast_id: item.ast_id(&self.tree).erase() },
|
|
||||||
item_node,
|
|
||||||
self.span_map(),
|
|
||||||
);
|
|
||||||
self.add_attrs(
|
self.add_attrs(
|
||||||
match item {
|
match item {
|
||||||
AssocItem::Function(it) => AttrOwner::ModItem(ModItem::Function(it)),
|
AssocItem::Function(it) => AttrOwner::ModItem(ModItem::Function(it)),
|
||||||
|
@ -192,7 +170,7 @@ impl<'a> Ctx<'a> {
|
||||||
let visibility = self.lower_visibility(strukt);
|
let visibility = self.lower_visibility(strukt);
|
||||||
let name = strukt.name()?.as_name();
|
let name = strukt.name()?.as_name();
|
||||||
let ast_id = self.source_ast_id_map.ast_id(strukt);
|
let ast_id = self.source_ast_id_map.ast_id(strukt);
|
||||||
let generic_params = self.lower_generic_params(HasImplicitSelf::No, strukt, ast_id.erase());
|
let generic_params = self.lower_generic_params(HasImplicitSelf::No, strukt);
|
||||||
let fields = self.lower_fields(&strukt.kind());
|
let fields = self.lower_fields(&strukt.kind());
|
||||||
let res = Struct { name, visibility, generic_params, fields, ast_id };
|
let res = Struct { name, visibility, generic_params, fields, ast_id };
|
||||||
Some(id(self.data().structs.alloc(res)))
|
Some(id(self.data().structs.alloc(res)))
|
||||||
|
@ -216,19 +194,10 @@ impl<'a> Ctx<'a> {
|
||||||
let start = self.next_field_idx();
|
let start = self.next_field_idx();
|
||||||
for field in fields.fields() {
|
for field in fields.fields() {
|
||||||
if let Some(data) = self.lower_record_field(&field) {
|
if let Some(data) = self.lower_record_field(&field) {
|
||||||
let ast_id = match data.ast_id {
|
|
||||||
FieldAstId::Record(it) => it.erase(),
|
|
||||||
FieldAstId::Tuple(it) => it.erase(),
|
|
||||||
};
|
|
||||||
let idx = self.data().fields.alloc(data);
|
let idx = self.data().fields.alloc(data);
|
||||||
self.add_attrs(
|
self.add_attrs(
|
||||||
idx.into(),
|
idx.into(),
|
||||||
RawAttrs::new(
|
RawAttrs::new(self.db.upcast(), &field, self.span_map()),
|
||||||
self.db.upcast(),
|
|
||||||
SpanAnchor { file_id: self.file, ast_id },
|
|
||||||
&field,
|
|
||||||
self.span_map(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -249,20 +218,8 @@ impl<'a> Ctx<'a> {
|
||||||
let start = self.next_field_idx();
|
let start = self.next_field_idx();
|
||||||
for (i, field) in fields.fields().enumerate() {
|
for (i, field) in fields.fields().enumerate() {
|
||||||
let data = self.lower_tuple_field(i, &field);
|
let data = self.lower_tuple_field(i, &field);
|
||||||
let ast_id = match data.ast_id {
|
|
||||||
FieldAstId::Record(it) => it.erase(),
|
|
||||||
FieldAstId::Tuple(it) => it.erase(),
|
|
||||||
};
|
|
||||||
let idx = self.data().fields.alloc(data);
|
let idx = self.data().fields.alloc(data);
|
||||||
self.add_attrs(
|
self.add_attrs(idx.into(), RawAttrs::new(self.db.upcast(), &field, self.span_map()));
|
||||||
idx.into(),
|
|
||||||
RawAttrs::new(
|
|
||||||
self.db.upcast(),
|
|
||||||
SpanAnchor { file_id: self.file, ast_id },
|
|
||||||
&field,
|
|
||||||
self.span_map(),
|
|
||||||
),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
let end = self.next_field_idx();
|
let end = self.next_field_idx();
|
||||||
IdxRange::new(start..end)
|
IdxRange::new(start..end)
|
||||||
|
@ -280,7 +237,7 @@ impl<'a> Ctx<'a> {
|
||||||
let visibility = self.lower_visibility(union);
|
let visibility = self.lower_visibility(union);
|
||||||
let name = union.name()?.as_name();
|
let name = union.name()?.as_name();
|
||||||
let ast_id = self.source_ast_id_map.ast_id(union);
|
let ast_id = self.source_ast_id_map.ast_id(union);
|
||||||
let generic_params = self.lower_generic_params(HasImplicitSelf::No, union, ast_id.erase());
|
let generic_params = self.lower_generic_params(HasImplicitSelf::No, union);
|
||||||
let fields = match union.record_field_list() {
|
let fields = match union.record_field_list() {
|
||||||
Some(record_field_list) => self.lower_fields(&StructKind::Record(record_field_list)),
|
Some(record_field_list) => self.lower_fields(&StructKind::Record(record_field_list)),
|
||||||
None => Fields::Record(IdxRange::new(self.next_field_idx()..self.next_field_idx())),
|
None => Fields::Record(IdxRange::new(self.next_field_idx()..self.next_field_idx())),
|
||||||
|
@ -293,7 +250,7 @@ impl<'a> Ctx<'a> {
|
||||||
let visibility = self.lower_visibility(enum_);
|
let visibility = self.lower_visibility(enum_);
|
||||||
let name = enum_.name()?.as_name();
|
let name = enum_.name()?.as_name();
|
||||||
let ast_id = self.source_ast_id_map.ast_id(enum_);
|
let ast_id = self.source_ast_id_map.ast_id(enum_);
|
||||||
let generic_params = self.lower_generic_params(HasImplicitSelf::No, enum_, ast_id.erase());
|
let generic_params = self.lower_generic_params(HasImplicitSelf::No, enum_);
|
||||||
let variants = match &enum_.variant_list() {
|
let variants = match &enum_.variant_list() {
|
||||||
Some(variant_list) => self.lower_variants(variant_list),
|
Some(variant_list) => self.lower_variants(variant_list),
|
||||||
None => IdxRange::new(self.next_variant_idx()..self.next_variant_idx()),
|
None => IdxRange::new(self.next_variant_idx()..self.next_variant_idx()),
|
||||||
|
@ -306,16 +263,10 @@ impl<'a> Ctx<'a> {
|
||||||
let start = self.next_variant_idx();
|
let start = self.next_variant_idx();
|
||||||
for variant in variants.variants() {
|
for variant in variants.variants() {
|
||||||
if let Some(data) = self.lower_variant(&variant) {
|
if let Some(data) = self.lower_variant(&variant) {
|
||||||
let ast_id = data.ast_id.erase();
|
|
||||||
let idx = self.data().variants.alloc(data);
|
let idx = self.data().variants.alloc(data);
|
||||||
self.add_attrs(
|
self.add_attrs(
|
||||||
idx.into(),
|
idx.into(),
|
||||||
RawAttrs::new(
|
RawAttrs::new(self.db.upcast(), &variant, self.span_map()),
|
||||||
self.db.upcast(),
|
|
||||||
SpanAnchor { file_id: self.file, ast_id },
|
|
||||||
&variant,
|
|
||||||
self.span_map(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -366,12 +317,7 @@ impl<'a> Ctx<'a> {
|
||||||
});
|
});
|
||||||
self.add_attrs(
|
self.add_attrs(
|
||||||
idx.into(),
|
idx.into(),
|
||||||
RawAttrs::new(
|
RawAttrs::new(self.db.upcast(), &self_param, self.span_map()),
|
||||||
self.db.upcast(),
|
|
||||||
SpanAnchor { file_id: self.file, ast_id: ast_id.erase() },
|
|
||||||
&self_param,
|
|
||||||
self.span_map(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
has_self_param = true;
|
has_self_param = true;
|
||||||
}
|
}
|
||||||
|
@ -392,12 +338,7 @@ impl<'a> Ctx<'a> {
|
||||||
};
|
};
|
||||||
self.add_attrs(
|
self.add_attrs(
|
||||||
idx.into(),
|
idx.into(),
|
||||||
RawAttrs::new(
|
RawAttrs::new(self.db.upcast(), ¶m, self.span_map()),
|
||||||
self.db.upcast(),
|
|
||||||
SpanAnchor { file_id: self.file, ast_id: ast_id.erase() },
|
|
||||||
¶m,
|
|
||||||
self.span_map(),
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -455,8 +396,7 @@ impl<'a> Ctx<'a> {
|
||||||
ast_id,
|
ast_id,
|
||||||
flags,
|
flags,
|
||||||
};
|
};
|
||||||
res.explicit_generic_params =
|
res.explicit_generic_params = self.lower_generic_params(HasImplicitSelf::No, func);
|
||||||
self.lower_generic_params(HasImplicitSelf::No, func, ast_id.erase());
|
|
||||||
|
|
||||||
Some(id(self.data().functions.alloc(res)))
|
Some(id(self.data().functions.alloc(res)))
|
||||||
}
|
}
|
||||||
|
@ -470,8 +410,7 @@ impl<'a> Ctx<'a> {
|
||||||
let visibility = self.lower_visibility(type_alias);
|
let visibility = self.lower_visibility(type_alias);
|
||||||
let bounds = self.lower_type_bounds(type_alias);
|
let bounds = self.lower_type_bounds(type_alias);
|
||||||
let ast_id = self.source_ast_id_map.ast_id(type_alias);
|
let ast_id = self.source_ast_id_map.ast_id(type_alias);
|
||||||
let generic_params =
|
let generic_params = self.lower_generic_params(HasImplicitSelf::No, type_alias);
|
||||||
self.lower_generic_params(HasImplicitSelf::No, type_alias, ast_id.erase());
|
|
||||||
let res = TypeAlias { name, visibility, bounds, generic_params, type_ref, ast_id };
|
let res = TypeAlias { name, visibility, bounds, generic_params, type_ref, ast_id };
|
||||||
Some(id(self.data().type_aliases.alloc(res)))
|
Some(id(self.data().type_aliases.alloc(res)))
|
||||||
}
|
}
|
||||||
|
@ -520,11 +459,8 @@ impl<'a> Ctx<'a> {
|
||||||
let name = trait_def.name()?.as_name();
|
let name = trait_def.name()?.as_name();
|
||||||
let visibility = self.lower_visibility(trait_def);
|
let visibility = self.lower_visibility(trait_def);
|
||||||
let ast_id = self.source_ast_id_map.ast_id(trait_def);
|
let ast_id = self.source_ast_id_map.ast_id(trait_def);
|
||||||
let generic_params = self.lower_generic_params(
|
let generic_params =
|
||||||
HasImplicitSelf::Yes(trait_def.type_bound_list()),
|
self.lower_generic_params(HasImplicitSelf::Yes(trait_def.type_bound_list()), trait_def);
|
||||||
trait_def,
|
|
||||||
ast_id.erase(),
|
|
||||||
);
|
|
||||||
let is_auto = trait_def.auto_token().is_some();
|
let is_auto = trait_def.auto_token().is_some();
|
||||||
let is_unsafe = trait_def.unsafe_token().is_some();
|
let is_unsafe = trait_def.unsafe_token().is_some();
|
||||||
|
|
||||||
|
@ -549,7 +485,6 @@ impl<'a> Ctx<'a> {
|
||||||
let generic_params = self.lower_generic_params(
|
let generic_params = self.lower_generic_params(
|
||||||
HasImplicitSelf::Yes(trait_alias_def.type_bound_list()),
|
HasImplicitSelf::Yes(trait_alias_def.type_bound_list()),
|
||||||
trait_alias_def,
|
trait_alias_def,
|
||||||
ast_id.erase(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let alias = TraitAlias { name, visibility, generic_params, ast_id };
|
let alias = TraitAlias { name, visibility, generic_params, ast_id };
|
||||||
|
@ -560,8 +495,7 @@ impl<'a> Ctx<'a> {
|
||||||
let ast_id = self.source_ast_id_map.ast_id(impl_def);
|
let ast_id = self.source_ast_id_map.ast_id(impl_def);
|
||||||
// Note that trait impls don't get implicit `Self` unlike traits, because here they are a
|
// Note that trait impls don't get implicit `Self` unlike traits, because here they are a
|
||||||
// type alias rather than a type parameter, so this is handled by the resolver.
|
// type alias rather than a type parameter, so this is handled by the resolver.
|
||||||
let generic_params =
|
let generic_params = self.lower_generic_params(HasImplicitSelf::No, impl_def);
|
||||||
self.lower_generic_params(HasImplicitSelf::No, impl_def, ast_id.erase());
|
|
||||||
// FIXME: If trait lowering fails, due to a non PathType for example, we treat this impl
|
// FIXME: If trait lowering fails, due to a non PathType for example, we treat this impl
|
||||||
// as if it was an non-trait impl. Ideally we want to create a unique missing ref that only
|
// as if it was an non-trait impl. Ideally we want to create a unique missing ref that only
|
||||||
// equals itself.
|
// equals itself.
|
||||||
|
@ -615,9 +549,7 @@ impl<'a> Ctx<'a> {
|
||||||
path,
|
path,
|
||||||
ast_id,
|
ast_id,
|
||||||
expand_to,
|
expand_to,
|
||||||
call_site: span_map
|
call_site: span_map.span_for_range(m.syntax().text_range()).ctx,
|
||||||
.span_for_range(m.syntax().text_range())
|
|
||||||
.map_or(SyntaxContextId::ROOT, |s| s.ctx),
|
|
||||||
};
|
};
|
||||||
Some(id(self.data().macro_calls.alloc(res)))
|
Some(id(self.data().macro_calls.alloc(res)))
|
||||||
}
|
}
|
||||||
|
@ -656,15 +588,7 @@ impl<'a> Ctx<'a> {
|
||||||
ast::ExternItem::TypeAlias(ty) => self.lower_type_alias(ty)?.into(),
|
ast::ExternItem::TypeAlias(ty) => self.lower_type_alias(ty)?.into(),
|
||||||
ast::ExternItem::MacroCall(call) => self.lower_macro_call(call)?.into(),
|
ast::ExternItem::MacroCall(call) => self.lower_macro_call(call)?.into(),
|
||||||
};
|
};
|
||||||
let attrs = RawAttrs::new(
|
let attrs = RawAttrs::new(self.db.upcast(), &item, self.span_map());
|
||||||
self.db.upcast(),
|
|
||||||
SpanAnchor {
|
|
||||||
file_id: self.file,
|
|
||||||
ast_id: mod_item.ast_id(&self.tree).erase(),
|
|
||||||
},
|
|
||||||
&item,
|
|
||||||
self.span_map(),
|
|
||||||
);
|
|
||||||
self.add_attrs(mod_item.into(), attrs);
|
self.add_attrs(mod_item.into(), attrs);
|
||||||
Some(mod_item)
|
Some(mod_item)
|
||||||
})
|
})
|
||||||
|
@ -679,7 +603,6 @@ impl<'a> Ctx<'a> {
|
||||||
&mut self,
|
&mut self,
|
||||||
has_implicit_self: HasImplicitSelf,
|
has_implicit_self: HasImplicitSelf,
|
||||||
node: &dyn ast::HasGenericParams,
|
node: &dyn ast::HasGenericParams,
|
||||||
owner_ast_id: ErasedFileAstId,
|
|
||||||
) -> Interned<GenericParams> {
|
) -> Interned<GenericParams> {
|
||||||
let mut generics = GenericParams::default();
|
let mut generics = GenericParams::default();
|
||||||
|
|
||||||
|
@ -701,12 +624,7 @@ impl<'a> Ctx<'a> {
|
||||||
|
|
||||||
let add_param_attrs = |item: Either<LocalTypeOrConstParamId, LocalLifetimeParamId>,
|
let add_param_attrs = |item: Either<LocalTypeOrConstParamId, LocalLifetimeParamId>,
|
||||||
param| {
|
param| {
|
||||||
let attrs = RawAttrs::new(
|
let attrs = RawAttrs::new(self.db.upcast(), ¶m, self.body_ctx.span_map());
|
||||||
self.db.upcast(),
|
|
||||||
SpanAnchor { file_id: self.file, ast_id: owner_ast_id },
|
|
||||||
¶m,
|
|
||||||
self.body_ctx.span_map(),
|
|
||||||
);
|
|
||||||
// This is identical to the body of `Ctx::add_attrs()` but we can't call that here
|
// This is identical to the body of `Ctx::add_attrs()` but we can't call that here
|
||||||
// because it requires `&mut self` and the call to `generics.fill()` below also
|
// because it requires `&mut self` and the call to `generics.fill()` below also
|
||||||
// references `self`.
|
// references `self`.
|
||||||
|
@ -817,7 +735,7 @@ fn lower_abi(abi: ast::Abi) -> Interned<str> {
|
||||||
|
|
||||||
struct UseTreeLowering<'a> {
|
struct UseTreeLowering<'a> {
|
||||||
db: &'a dyn DefDatabase,
|
db: &'a dyn DefDatabase,
|
||||||
hygiene: &'a SpanMap,
|
hygiene: SpanMapRef<'a>,
|
||||||
mapping: Arena<ast::UseTree>,
|
mapping: Arena<ast::UseTree>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -885,7 +803,7 @@ impl UseTreeLowering<'_> {
|
||||||
|
|
||||||
pub(crate) fn lower_use_tree(
|
pub(crate) fn lower_use_tree(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
hygiene: &SpanMap,
|
hygiene: SpanMapRef<'_>,
|
||||||
tree: ast::UseTree,
|
tree: ast::UseTree,
|
||||||
) -> Option<(UseTree, Arena<ast::UseTree>)> {
|
) -> Option<(UseTree, Arena<ast::UseTree>)> {
|
||||||
let mut lowering = UseTreeLowering { db, hygiene, mapping: Arena::new() };
|
let mut lowering = UseTreeLowering { db, hygiene, mapping: Arena::new() };
|
||||||
|
|
|
@ -75,7 +75,7 @@ use hir_expand::{
|
||||||
name::Name,
|
name::Name,
|
||||||
proc_macro::ProcMacroExpander,
|
proc_macro::ProcMacroExpander,
|
||||||
AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
|
AstId, ExpandError, ExpandResult, ExpandTo, HirFileId, InFile, MacroCallId, MacroCallKind,
|
||||||
MacroDefId, MacroDefKind, UnresolvedMacro,
|
MacroDefId, MacroDefKind,
|
||||||
};
|
};
|
||||||
use item_tree::ExternBlock;
|
use item_tree::ExternBlock;
|
||||||
use la_arena::Idx;
|
use la_arena::Idx;
|
||||||
|
@ -1166,15 +1166,14 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
|
||||||
let expands_to = hir_expand::ExpandTo::from_call_site(self.value);
|
let expands_to = hir_expand::ExpandTo::from_call_site(self.value);
|
||||||
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
|
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
|
||||||
let span_map = db.span_map(self.file_id);
|
let span_map = db.span_map(self.file_id);
|
||||||
let path = self.value.path().and_then(|path| path::ModPath::from_src(db, path, &span_map));
|
let path =
|
||||||
|
self.value.path().and_then(|path| path::ModPath::from_src(db, path, span_map.as_ref()));
|
||||||
|
|
||||||
let Some(path) = path else {
|
let Some(path) = path else {
|
||||||
return Ok(ExpandResult::only_err(ExpandError::other("malformed macro invocation")));
|
return Ok(ExpandResult::only_err(ExpandError::other("malformed macro invocation")));
|
||||||
};
|
};
|
||||||
|
|
||||||
let call_site = span_map
|
let call_site = span_map.span_for_range(self.value.syntax().text_range()).ctx;
|
||||||
.span_for_range(self.value.syntax().text_range())
|
|
||||||
.map_or(SyntaxContextId::ROOT, |s| s.ctx);
|
|
||||||
|
|
||||||
macro_call_as_call_id_with_eager(
|
macro_call_as_call_id_with_eager(
|
||||||
db,
|
db,
|
||||||
|
@ -1228,7 +1227,7 @@ fn macro_call_as_call_id_with_eager(
|
||||||
let res = match def.kind {
|
let res = match def.kind {
|
||||||
MacroDefKind::BuiltInEager(..) => {
|
MacroDefKind::BuiltInEager(..) => {
|
||||||
let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db));
|
let macro_call = InFile::new(call.ast_id.file_id, call.ast_id.to_node(db));
|
||||||
expand_eager_macro_input(db, krate, macro_call, def, &|path| {
|
expand_eager_macro_input(db, krate, macro_call, def, call_site, &|path| {
|
||||||
eager_resolver(path).filter(MacroDefId::is_fn_like)
|
eager_resolver(path).filter(MacroDefId::is_fn_like)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1370,6 +1369,12 @@ fn attr_macro_as_call_id(
|
||||||
macro_attr.ctxt,
|
macro_attr.ctxt,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct UnresolvedMacro {
|
||||||
|
pub path: hir_expand::mod_path::ModPath,
|
||||||
|
}
|
||||||
|
|
||||||
intern::impl_internable!(
|
intern::impl_internable!(
|
||||||
crate::type_ref::TypeRef,
|
crate::type_ref::TypeRef,
|
||||||
crate::type_ref::TraitRef,
|
crate::type_ref::TraitRef,
|
||||||
|
|
|
@ -3,7 +3,8 @@ use std::cell::OnceCell;
|
||||||
|
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
ast_id_map::{AstIdMap, AstIdNode},
|
ast_id_map::{AstIdMap, AstIdNode},
|
||||||
AstId, HirFileId, InFile, SpanMap,
|
span::{SpanMap, SpanMapRef},
|
||||||
|
AstId, HirFileId, InFile,
|
||||||
};
|
};
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
@ -12,13 +13,13 @@ use crate::{db::DefDatabase, path::Path};
|
||||||
|
|
||||||
pub struct LowerCtx<'a> {
|
pub struct LowerCtx<'a> {
|
||||||
pub db: &'a dyn DefDatabase,
|
pub db: &'a dyn DefDatabase,
|
||||||
hygiene: Arc<SpanMap>,
|
hygiene: SpanMap,
|
||||||
// FIXME: This optimization is probably pointless, ast id map should pretty much always exist anyways.
|
// FIXME: This optimization is probably pointless, ast id map should pretty much always exist anyways.
|
||||||
ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
|
ast_id_map: Option<(HirFileId, OnceCell<Arc<AstIdMap>>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> LowerCtx<'a> {
|
impl<'a> LowerCtx<'a> {
|
||||||
pub fn new(db: &'a dyn DefDatabase, hygiene: Arc<SpanMap>, file_id: HirFileId) -> Self {
|
pub fn new(db: &'a dyn DefDatabase, hygiene: SpanMap, file_id: HirFileId) -> Self {
|
||||||
LowerCtx { db, hygiene, ast_id_map: Some((file_id, OnceCell::new())) }
|
LowerCtx { db, hygiene, ast_id_map: Some((file_id, OnceCell::new())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -26,12 +27,12 @@ impl<'a> LowerCtx<'a> {
|
||||||
LowerCtx { db, hygiene: db.span_map(file_id), ast_id_map: Some((file_id, OnceCell::new())) }
|
LowerCtx { db, hygiene: db.span_map(file_id), ast_id_map: Some((file_id, OnceCell::new())) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: Arc<SpanMap>) -> Self {
|
pub fn with_hygiene(db: &'a dyn DefDatabase, hygiene: SpanMap) -> Self {
|
||||||
LowerCtx { db, hygiene, ast_id_map: None }
|
LowerCtx { db, hygiene, ast_id_map: None }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn span_map(&self) -> &SpanMap {
|
pub(crate) fn span_map(&self) -> SpanMapRef<'_> {
|
||||||
&self.hygiene
|
self.hygiene.as_ref()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
|
pub(crate) fn lower_path(&self, ast: ast::Path) -> Option<Path> {
|
||||||
|
|
|
@ -23,12 +23,9 @@ macro_rules! f {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// +spans
|
// +spans+syntaxctxt
|
||||||
f!(struct MyTraitMap2);
|
f!(struct MyTraitMap2);
|
||||||
"#,
|
"#,
|
||||||
// FIXME: #SpanAnchor(FileId(0), 1)@91..92\2# why is there whitespace annotated with a span
|
|
||||||
// here? Presumably because the leading `::` is getting two spans instead of one? Sounds
|
|
||||||
// liek glueing might be failing here
|
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
macro_rules! f {
|
macro_rules! f {
|
||||||
( struct $ident:ident ) => {
|
( struct $ident:ident ) => {
|
||||||
|
@ -38,8 +35,8 @@ macro_rules! f {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
struct#FileId(0):1@58..64\2# MyTraitMap2#FileId(0):2@20..31\0# {#FileId(0):1@72..73\2#
|
struct#FileId(0):1@58..64\2# MyTraitMap2#FileId(0):2@31..42\0# {#FileId(0):1@72..73\2#
|
||||||
map#FileId(0):1@86..89\2#:#FileId(0):1@89..90\2# #FileId(0):1@91..92\2#::#FileId(0):1@92..93\2#std#FileId(0):1@93..96\2#::#FileId(0):1@97..98\2#collections#FileId(0):1@98..109\2#::#FileId(0):1@110..111\2#HashSet#FileId(0):1@111..118\2#<#FileId(0):1@118..119\2#(#FileId(0):1@119..120\2#)#FileId(0):1@120..121\2#>#FileId(0):1@121..122\2#,#FileId(0):1@122..123\2#
|
map#FileId(0):1@86..89\2#:#FileId(0):1@89..90\2# #FileId(0):1@89..90\2#::#FileId(0):1@92..93\2#std#FileId(0):1@93..96\2#::#FileId(0):1@97..98\2#collections#FileId(0):1@98..109\2#::#FileId(0):1@110..111\2#HashSet#FileId(0):1@111..118\2#<#FileId(0):1@118..119\2#(#FileId(0):1@119..120\2#)#FileId(0):1@120..121\2#>#FileId(0):1@121..122\2#,#FileId(0):1@122..123\2#
|
||||||
}#FileId(0):1@132..133\2#
|
}#FileId(0):1@132..133\2#
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
|
@ -51,14 +48,14 @@ fn token_mapping_floats() {
|
||||||
// (and related issues)
|
// (and related issues)
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
// +spans
|
// +spans+syntaxctxt
|
||||||
macro_rules! f {
|
macro_rules! f {
|
||||||
($($tt:tt)*) => {
|
($($tt:tt)*) => {
|
||||||
$($tt)*
|
$($tt)*
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// +spans
|
// +spans+syntaxctxt
|
||||||
f! {
|
f! {
|
||||||
fn main() {
|
fn main() {
|
||||||
1;
|
1;
|
||||||
|
@ -71,19 +68,19 @@ f! {
|
||||||
|
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
// +spans
|
// +spans+syntaxctxt
|
||||||
macro_rules! f {
|
macro_rules! f {
|
||||||
($($tt:tt)*) => {
|
($($tt:tt)*) => {
|
||||||
$($tt)*
|
$($tt)*
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn#FileId(0):2@19..21\0# main#FileId(0):2@22..26\0#(#FileId(0):2@26..27\0#)#FileId(0):2@27..28\0# {#FileId(0):2@29..30\0#
|
fn#FileId(0):2@30..32\0# main#FileId(0):2@33..37\0#(#FileId(0):2@37..38\0#)#FileId(0):2@38..39\0# {#FileId(0):2@40..41\0#
|
||||||
1#FileId(0):2@39..40\0#;#FileId(0):2@40..41\0#
|
1#FileId(0):2@50..51\0#;#FileId(0):2@51..52\0#
|
||||||
1.0#FileId(0):2@50..53\0#;#FileId(0):2@53..54\0#
|
1.0#FileId(0):2@61..64\0#;#FileId(0):2@64..65\0#
|
||||||
(#FileId(0):2@63..64\0#(#FileId(0):2@64..65\0#1#FileId(0):2@65..66\0#,#FileId(0):2@66..67\0# )#FileId(0):2@67..68\0#,#FileId(0):2@68..69\0# )#FileId(0):2@69..70\0#.#FileId(0):2@70..71\0#0#FileId(0):2@71..74\0#.#FileId(0):2@71..74\0#0#FileId(0):2@71..74\0#;#FileId(0):2@74..75\0#
|
(#FileId(0):2@74..75\0#(#FileId(0):2@75..76\0#1#FileId(0):2@76..77\0#,#FileId(0):2@77..78\0# )#FileId(0):2@78..79\0#,#FileId(0):2@79..80\0# )#FileId(0):2@80..81\0#.#FileId(0):2@81..82\0#0#FileId(0):2@82..85\0#.#FileId(0):2@82..85\0#0#FileId(0):2@82..85\0#;#FileId(0):2@85..86\0#
|
||||||
let#FileId(0):2@84..87\0# x#FileId(0):2@88..89\0# =#FileId(0):2@90..91\0# 1#FileId(0):2@92..93\0#;#FileId(0):2@93..94\0#
|
let#FileId(0):2@95..98\0# x#FileId(0):2@99..100\0# =#FileId(0):2@101..102\0# 1#FileId(0):2@103..104\0#;#FileId(0):2@104..105\0#
|
||||||
}#FileId(0):2@99..100\0#
|
}#FileId(0):2@110..111\0#
|
||||||
|
|
||||||
|
|
||||||
"#]],
|
"#]],
|
||||||
|
@ -127,7 +124,7 @@ macro_rules! identity {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main(foo: ()) {
|
fn main(foo: ()) {
|
||||||
format_args/*+spans*/!("{} {} {}", format_args!("{}", 0), foo, identity!(10), "bar")
|
format_args/*+spans+syntaxctxt*/!("{} {} {}", format_args!("{}", 0), foo, identity!(10), "bar")
|
||||||
}
|
}
|
||||||
|
|
||||||
"#,
|
"#,
|
||||||
|
@ -141,7 +138,7 @@ macro_rules! identity {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main(foo: ()) {
|
fn main(foo: ()) {
|
||||||
builtin#FileId(0):0@0..0\0# ##FileId(0):0@0..0\0#format_args#FileId(0):0@0..0\0# (#FileId(0):6@22..23\0#"{} {} {}"#FileId(0):6@23..33\0#,#FileId(0):6@33..34\0# format_args#FileId(0):6@35..46\0#!#FileId(0):6@46..47\0#(#FileId(0):6@47..48\0#"{}"#FileId(0):6@48..52\0#,#FileId(0):6@52..53\0# 0#FileId(0):6@54..55\0#)#FileId(0):6@55..56\0#,#FileId(0):6@56..57\0# foo#FileId(0):6@58..61\0#,#FileId(0):6@61..62\0# identity#FileId(0):6@63..71\0#!#FileId(0):6@71..72\0#(#FileId(0):6@72..73\0#10#FileId(0):6@73..75\0#)#FileId(0):6@75..76\0#,#FileId(0):6@76..77\0# "bar"#FileId(0):6@78..83\0#)#FileId(0):6@83..84\0#
|
builtin#FileId(0):0@0..0\0# ##FileId(0):0@0..0\0#format_args#FileId(0):0@0..0\0# (#FileId(0):3@56..57\0#"{} {} {}"#FileId(0):3@57..67\0#,#FileId(0):3@67..68\0# format_args#FileId(0):3@69..80\0#!#FileId(0):3@80..81\0#(#FileId(0):3@81..82\0#"{}"#FileId(0):3@82..86\0#,#FileId(0):3@86..87\0# 0#FileId(0):3@88..89\0#)#FileId(0):3@89..90\0#,#FileId(0):3@90..91\0# foo#FileId(0):3@92..95\0#,#FileId(0):3@95..96\0# identity#FileId(0):3@97..105\0#!#FileId(0):3@105..106\0#(#FileId(0):3@106..107\0#10#FileId(0):3@107..109\0#)#FileId(0):3@109..110\0#,#FileId(0):3@110..111\0# "bar"#FileId(0):3@112..117\0#)#FileId(0):3@117..118\0#
|
||||||
}
|
}
|
||||||
|
|
||||||
"##]],
|
"##]],
|
||||||
|
@ -156,7 +153,7 @@ fn token_mapping_across_files() {
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod foo;
|
mod foo;
|
||||||
|
|
||||||
mk_struct/*+spans*/!(Foo with u32);
|
mk_struct/*+spans+syntaxctxt*/!(Foo with u32);
|
||||||
//- /foo.rs
|
//- /foo.rs
|
||||||
macro_rules! mk_struct {
|
macro_rules! mk_struct {
|
||||||
($foo:ident with $ty:ty) => { struct $foo($ty); }
|
($foo:ident with $ty:ty) => { struct $foo($ty); }
|
||||||
|
@ -166,7 +163,7 @@ macro_rules! mk_struct {
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod foo;
|
mod foo;
|
||||||
|
|
||||||
struct#FileId(1):1@59..65\2# Foo#FileId(0):2@21..24\0#(#FileId(1):1@70..71\2#u32#FileId(0):2@30..33\0#)#FileId(1):1@74..75\2#;#FileId(1):1@75..76\2#
|
struct#FileId(1):1@59..65\2# Foo#FileId(0):2@32..35\0#(#FileId(1):1@70..71\2#u32#FileId(0):2@41..44\0#)#FileId(1):1@74..75\2#;#FileId(1):1@75..76\2#
|
||||||
"#]],
|
"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,7 +18,7 @@ use std::{iter, ops::Range, sync};
|
||||||
|
|
||||||
use base_db::{fixture::WithFixture, ProcMacro, SourceDatabase};
|
use base_db::{fixture::WithFixture, ProcMacro, SourceDatabase};
|
||||||
use expect_test::Expect;
|
use expect_test::Expect;
|
||||||
use hir_expand::{db::ExpandDatabase, HirFileIdExt, InFile, MacroFile, SpanMap};
|
use hir_expand::{db::ExpandDatabase, span::SpanMapRef, HirFileIdExt, InFile, MacroFile};
|
||||||
use stdx::format_to;
|
use stdx::format_to;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, edit::IndentLevel},
|
ast::{self, edit::IndentLevel},
|
||||||
|
@ -104,10 +104,12 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
||||||
let mut tree = false;
|
let mut tree = false;
|
||||||
let mut expect_errors = false;
|
let mut expect_errors = false;
|
||||||
let mut show_spans = false;
|
let mut show_spans = false;
|
||||||
|
let mut show_ctxt = false;
|
||||||
for comment in call.syntax().children_with_tokens().filter(|it| it.kind() == COMMENT) {
|
for comment in call.syntax().children_with_tokens().filter(|it| it.kind() == COMMENT) {
|
||||||
tree |= comment.to_string().contains("+tree");
|
tree |= comment.to_string().contains("+tree");
|
||||||
expect_errors |= comment.to_string().contains("+errors");
|
expect_errors |= comment.to_string().contains("+errors");
|
||||||
show_spans |= comment.to_string().contains("+spans");
|
show_spans |= comment.to_string().contains("+spans");
|
||||||
|
show_ctxt |= comment.to_string().contains("+syntaxctxt");
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut expn_text = String::new();
|
let mut expn_text = String::new();
|
||||||
|
@ -128,8 +130,12 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
||||||
parse.syntax_node(),
|
parse.syntax_node(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
let pp =
|
let pp = pretty_print_macro_expansion(
|
||||||
pretty_print_macro_expansion(parse.syntax_node(), show_spans.then_some(&*token_map));
|
parse.syntax_node(),
|
||||||
|
SpanMapRef::ExpansionSpanMap(&token_map),
|
||||||
|
show_spans,
|
||||||
|
show_ctxt,
|
||||||
|
);
|
||||||
let indent = IndentLevel::from_node(call.syntax());
|
let indent = IndentLevel::from_node(call.syntax());
|
||||||
let pp = reindent(indent, pp);
|
let pp = reindent(indent, pp);
|
||||||
format_to!(expn_text, "{}", pp);
|
format_to!(expn_text, "{}", pp);
|
||||||
|
@ -169,12 +175,16 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
||||||
if src.file_id.is_attr_macro(&db) || src.file_id.is_custom_derive(&db) {
|
if src.file_id.is_attr_macro(&db) || src.file_id.is_custom_derive(&db) {
|
||||||
let call = src.file_id.call_node(&db).expect("macro file");
|
let call = src.file_id.call_node(&db).expect("macro file");
|
||||||
let mut show_spans = false;
|
let mut show_spans = false;
|
||||||
|
let mut show_ctxt = false;
|
||||||
for comment in call.value.children_with_tokens().filter(|it| it.kind() == COMMENT) {
|
for comment in call.value.children_with_tokens().filter(|it| it.kind() == COMMENT) {
|
||||||
show_spans |= comment.to_string().contains("+spans");
|
show_spans |= comment.to_string().contains("+spans");
|
||||||
|
show_ctxt |= comment.to_string().contains("+syntaxctxt");
|
||||||
}
|
}
|
||||||
let pp = pretty_print_macro_expansion(
|
let pp = pretty_print_macro_expansion(
|
||||||
src.value,
|
src.value,
|
||||||
show_spans.then_some(&db.span_map(src.file_id)),
|
db.span_map(src.file_id).as_ref(),
|
||||||
|
show_spans,
|
||||||
|
show_ctxt,
|
||||||
);
|
);
|
||||||
format_to!(expanded_text, "\n{}", pp)
|
format_to!(expanded_text, "\n{}", pp)
|
||||||
}
|
}
|
||||||
|
@ -184,7 +194,12 @@ pub fn identity_when_valid(_attr: TokenStream, item: TokenStream) -> TokenStream
|
||||||
for impl_id in def_map[local_id].scope.impls() {
|
for impl_id in def_map[local_id].scope.impls() {
|
||||||
let src = impl_id.lookup(&db).source(&db);
|
let src = impl_id.lookup(&db).source(&db);
|
||||||
if src.file_id.is_builtin_derive(&db) {
|
if src.file_id.is_builtin_derive(&db) {
|
||||||
let pp = pretty_print_macro_expansion(src.value.syntax().clone(), None);
|
let pp = pretty_print_macro_expansion(
|
||||||
|
src.value.syntax().clone(),
|
||||||
|
db.span_map(src.file_id).as_ref(),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
);
|
||||||
format_to!(expanded_text, "\n{}", pp)
|
format_to!(expanded_text, "\n{}", pp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -209,7 +224,12 @@ fn reindent(indent: IndentLevel, pp: String) -> String {
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pretty_print_macro_expansion(expn: SyntaxNode, map: Option<&SpanMap>) -> String {
|
fn pretty_print_macro_expansion(
|
||||||
|
expn: SyntaxNode,
|
||||||
|
map: SpanMapRef<'_>,
|
||||||
|
show_spans: bool,
|
||||||
|
show_ctxt: bool,
|
||||||
|
) -> String {
|
||||||
let mut res = String::new();
|
let mut res = String::new();
|
||||||
let mut prev_kind = EOF;
|
let mut prev_kind = EOF;
|
||||||
let mut indent_level = 0;
|
let mut indent_level = 0;
|
||||||
|
@ -255,17 +275,22 @@ fn pretty_print_macro_expansion(expn: SyntaxNode, map: Option<&SpanMap>) -> Stri
|
||||||
}
|
}
|
||||||
prev_kind = curr_kind;
|
prev_kind = curr_kind;
|
||||||
format_to!(res, "{}", token);
|
format_to!(res, "{}", token);
|
||||||
if let Some(map) = map {
|
if show_spans || show_ctxt {
|
||||||
if let Some(span) = map.span_for_range(token.text_range()) {
|
let span = map.span_for_range(token.text_range());
|
||||||
|
format_to!(res, "#");
|
||||||
|
if show_spans {
|
||||||
format_to!(
|
format_to!(
|
||||||
res,
|
res,
|
||||||
"#{:?}:{:?}@{:?}\\{}#",
|
"{:?}:{:?}@{:?}",
|
||||||
span.anchor.file_id,
|
span.anchor.file_id,
|
||||||
span.anchor.ast_id.into_raw(),
|
span.anchor.ast_id.into_raw(),
|
||||||
span.range,
|
span.range,
|
||||||
span.ctx
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
if show_ctxt {
|
||||||
|
format_to!(res, "\\{}", span.ctx);
|
||||||
|
}
|
||||||
|
format_to!(res, "#");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
res
|
res
|
||||||
|
|
|
@ -74,6 +74,7 @@ fn foo() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore] // TODO
|
||||||
fn attribute_macro_syntax_completion_2() {
|
fn attribute_macro_syntax_completion_2() {
|
||||||
// common case of dot completion while typing
|
// common case of dot completion while typing
|
||||||
check(
|
check(
|
||||||
|
@ -168,21 +169,21 @@ fn float_attribute_mapping() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
//- proc_macros: identity
|
//- proc_macros: identity
|
||||||
//+spans
|
//+spans+syntaxctxt
|
||||||
#[proc_macros::identity]
|
#[proc_macros::identity]
|
||||||
fn foo(&self) {
|
fn foo(&self) {
|
||||||
self.0. 1;
|
self.0. 1;
|
||||||
}
|
}
|
||||||
"#,
|
"#,
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
//+spans
|
//+spans+syntaxctxt
|
||||||
#[proc_macros::identity]
|
#[proc_macros::identity]
|
||||||
fn foo(&self) {
|
fn foo(&self) {
|
||||||
self.0. 1;
|
self.0. 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn#FileId(0):1@34..36\0# foo#FileId(0):1@37..40\0#(#FileId(0):1@40..41\0#&#FileId(0):1@41..42\0#self#FileId(0):1@42..46\0# )#FileId(0):1@46..47\0# {#FileId(0):1@48..49\0#
|
fn#FileId(0):1@45..47\0# foo#FileId(0):1@48..51\0#(#FileId(0):1@51..52\0#&#FileId(0):1@52..53\0#self#FileId(0):1@53..57\0# )#FileId(0):1@57..58\0# {#FileId(0):1@59..60\0#
|
||||||
self#FileId(0):1@54..58\0# .#FileId(0):1@58..59\0#0#FileId(0):1@59..60\0#.#FileId(0):1@60..61\0#1#FileId(0):1@62..63\0#;#FileId(0):1@63..64\0#
|
self#FileId(0):1@65..69\0# .#FileId(0):1@69..70\0#0#FileId(0):1@70..71\0#.#FileId(0):1@71..72\0#1#FileId(0):1@73..74\0#;#FileId(0):1@74..75\0#
|
||||||
}#FileId(0):1@65..66\0#"#]],
|
}#FileId(0):1@76..77\0#"#]],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,6 @@ use std::iter;
|
||||||
|
|
||||||
use crate::{lower::LowerCtx, type_ref::ConstRef};
|
use crate::{lower::LowerCtx, type_ref::ConstRef};
|
||||||
|
|
||||||
use base_db::span::SyntaxContextId;
|
|
||||||
use hir_expand::{
|
use hir_expand::{
|
||||||
mod_path::resolve_crate_root,
|
mod_path::resolve_crate_root,
|
||||||
name::{name, AsName},
|
name::{name, AsName},
|
||||||
|
@ -40,11 +39,11 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
|
||||||
let name = if name_ref.text() == "$crate" {
|
let name = if name_ref.text() == "$crate" {
|
||||||
kind = resolve_crate_root(
|
kind = resolve_crate_root(
|
||||||
ctx.db.upcast(),
|
ctx.db.upcast(),
|
||||||
hygiene
|
hygiene.span_for_range(name_ref.syntax().text_range()).ctx,
|
||||||
.span_for_range(name_ref.syntax().text_range())
|
|
||||||
.map_or(SyntaxContextId::ROOT, |s| s.ctx),
|
|
||||||
)
|
)
|
||||||
.map(PathKind::DollarCrate)?;
|
.map(PathKind::DollarCrate)
|
||||||
|
.unwrap_or(PathKind::Crate);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
} else {
|
} else {
|
||||||
name_ref.as_name()
|
name_ref.as_name()
|
||||||
|
@ -160,14 +159,12 @@ pub(super) fn lower_path(mut path: ast::Path, ctx: &LowerCtx<'_>) -> Option<Path
|
||||||
// We follow what it did anyway :)
|
// We follow what it did anyway :)
|
||||||
if segments.len() == 1 && kind == PathKind::Plain {
|
if segments.len() == 1 && kind == PathKind::Plain {
|
||||||
if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
|
if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
|
||||||
let syn_ctxt = hygiene
|
let syn_ctxt = hygiene.span_for_range(path.segment()?.syntax().text_range()).ctx;
|
||||||
.span_for_range(path.segment()?.syntax().text_range())
|
|
||||||
.map_or(SyntaxContextId::ROOT, |s| s.ctx);
|
|
||||||
if let Some(macro_call_id) = ctx.db.lookup_intern_syntax_context(syn_ctxt).outer_expn {
|
if let Some(macro_call_id) = ctx.db.lookup_intern_syntax_context(syn_ctxt).outer_expn {
|
||||||
if ctx.db.lookup_intern_macro_call(macro_call_id).def.local_inner {
|
if ctx.db.lookup_intern_macro_call(macro_call_id).def.local_inner {
|
||||||
dbg!("local_inner_macros");
|
kind = match resolve_crate_root(ctx.db.upcast(), syn_ctxt) {
|
||||||
if let Some(crate_root) = resolve_crate_root(ctx.db.upcast(), syn_ctxt) {
|
Some(crate_root) => PathKind::DollarCrate(crate_root),
|
||||||
kind = PathKind::DollarCrate(crate_root);
|
None => PathKind::Crate,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use hir_expand::{InFile, SpanMap};
|
use hir_expand::{span::SpanMapRef, InFile};
|
||||||
use la_arena::ArenaMap;
|
use la_arena::ArenaMap;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
@ -34,13 +34,13 @@ impl RawVisibility {
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
node: InFile<Option<ast::Visibility>>,
|
node: InFile<Option<ast::Visibility>>,
|
||||||
) -> RawVisibility {
|
) -> RawVisibility {
|
||||||
Self::from_ast_with_hygiene(db, node.value, &db.span_map(node.file_id))
|
Self::from_ast_with_hygiene(db, node.value, db.span_map(node.file_id).as_ref())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn from_ast_with_hygiene(
|
pub(crate) fn from_ast_with_hygiene(
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
node: Option<ast::Visibility>,
|
node: Option<ast::Visibility>,
|
||||||
hygiene: &SpanMap,
|
hygiene: SpanMapRef<'_>,
|
||||||
) -> RawVisibility {
|
) -> RawVisibility {
|
||||||
Self::from_ast_with_hygiene_and_default(db, node, RawVisibility::private(), hygiene)
|
Self::from_ast_with_hygiene_and_default(db, node, RawVisibility::private(), hygiene)
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ impl RawVisibility {
|
||||||
db: &dyn DefDatabase,
|
db: &dyn DefDatabase,
|
||||||
node: Option<ast::Visibility>,
|
node: Option<ast::Visibility>,
|
||||||
default: RawVisibility,
|
default: RawVisibility,
|
||||||
hygiene: &SpanMap,
|
hygiene: SpanMapRef<'_>,
|
||||||
) -> RawVisibility {
|
) -> RawVisibility {
|
||||||
let node = match node {
|
let node = match node {
|
||||||
None => return default,
|
None => return default,
|
||||||
|
|
|
@ -19,6 +19,33 @@ use syntax::{ast, AstNode, AstPtr, SyntaxNode, SyntaxNodePtr};
|
||||||
|
|
||||||
pub use base_db::span::ErasedFileAstId;
|
pub use base_db::span::ErasedFileAstId;
|
||||||
|
|
||||||
|
use crate::db;
|
||||||
|
|
||||||
|
/// `AstId` points to an AST node in any file.
|
||||||
|
///
|
||||||
|
/// It is stable across reparses, and can be used as salsa key/value.
|
||||||
|
pub type AstId<N> = crate::InFile<FileAstId<N>>;
|
||||||
|
|
||||||
|
impl<N: AstIdNode> AstId<N> {
|
||||||
|
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> N {
|
||||||
|
self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))
|
||||||
|
}
|
||||||
|
pub fn to_in_file_node(&self, db: &dyn db::ExpandDatabase) -> crate::InFile<N> {
|
||||||
|
crate::InFile::new(self.file_id, self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)))
|
||||||
|
}
|
||||||
|
pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> AstPtr<N> {
|
||||||
|
db.ast_id_map(self.file_id).get(self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type ErasedAstId = crate::InFile<ErasedFileAstId>;
|
||||||
|
|
||||||
|
impl ErasedAstId {
|
||||||
|
pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> SyntaxNodePtr {
|
||||||
|
db.ast_id_map(self.file_id).get_erased(self.value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// `AstId` points to an AST node in a specific file.
|
/// `AstId` points to an AST node in a specific file.
|
||||||
pub struct FileAstId<N: AstIdNode> {
|
pub struct FileAstId<N: AstIdNode> {
|
||||||
raw: ErasedFileAstId,
|
raw: ErasedFileAstId,
|
||||||
|
@ -141,9 +168,9 @@ impl AstIdMap {
|
||||||
bdfs(node, |it| {
|
bdfs(node, |it| {
|
||||||
if should_alloc_id(it.kind()) {
|
if should_alloc_id(it.kind()) {
|
||||||
res.alloc(&it);
|
res.alloc(&it);
|
||||||
true
|
TreeOrder::BreadthFirst
|
||||||
} else {
|
} else {
|
||||||
false
|
TreeOrder::DepthFirst
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
res.map = hashbrown::HashMap::with_capacity_and_hasher(res.arena.len(), ());
|
res.map = hashbrown::HashMap::with_capacity_and_hasher(res.arena.len(), ());
|
||||||
|
@ -174,7 +201,7 @@ impl AstIdMap {
|
||||||
AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap()
|
AstPtr::try_from_raw(self.arena[id.raw].clone()).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_raw(&self, id: ErasedFileAstId) -> SyntaxNodePtr {
|
pub fn get_erased(&self, id: ErasedFileAstId) -> SyntaxNodePtr {
|
||||||
self.arena[id].clone()
|
self.arena[id].clone()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -202,14 +229,20 @@ fn hash_ptr(ptr: &SyntaxNodePtr) -> u64 {
|
||||||
hasher.finish()
|
hasher.finish()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone, PartialEq, Eq)]
|
||||||
|
enum TreeOrder {
|
||||||
|
BreadthFirst,
|
||||||
|
DepthFirst,
|
||||||
|
}
|
||||||
|
|
||||||
/// Walks the subtree in bdfs order, calling `f` for each node. What is bdfs
|
/// Walks the subtree in bdfs order, calling `f` for each node. What is bdfs
|
||||||
/// order? It is a mix of breadth-first and depth first orders. Nodes for which
|
/// order? It is a mix of breadth-first and depth first orders. Nodes for which
|
||||||
/// `f` returns true are visited breadth-first, all the other nodes are explored
|
/// `f` returns [`TreeOrder::BreadthFirst`] are visited breadth-first, all the other nodes are explored
|
||||||
/// depth-first.
|
/// [`TreeOrder::DepthFirst`].
|
||||||
///
|
///
|
||||||
/// In other words, the size of the bfs queue is bound by the number of "true"
|
/// In other words, the size of the bfs queue is bound by the number of "true"
|
||||||
/// nodes.
|
/// nodes.
|
||||||
fn bdfs(node: &SyntaxNode, mut f: impl FnMut(SyntaxNode) -> bool) {
|
fn bdfs(node: &SyntaxNode, mut f: impl FnMut(SyntaxNode) -> TreeOrder) {
|
||||||
let mut curr_layer = vec![node.clone()];
|
let mut curr_layer = vec![node.clone()];
|
||||||
let mut next_layer = vec![];
|
let mut next_layer = vec![];
|
||||||
while !curr_layer.is_empty() {
|
while !curr_layer.is_empty() {
|
||||||
|
@ -218,7 +251,7 @@ fn bdfs(node: &SyntaxNode, mut f: impl FnMut(SyntaxNode) -> bool) {
|
||||||
while let Some(event) = preorder.next() {
|
while let Some(event) = preorder.next() {
|
||||||
match event {
|
match event {
|
||||||
syntax::WalkEvent::Enter(node) => {
|
syntax::WalkEvent::Enter(node) => {
|
||||||
if f(node.clone()) {
|
if f(node.clone()) == TreeOrder::BreadthFirst {
|
||||||
next_layer.extend(node.children());
|
next_layer.extend(node.children());
|
||||||
preorder.skip_subtree();
|
preorder.skip_subtree();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,11 +1,7 @@
|
||||||
//! A higher level attributes based on TokenTree, with also some shortcuts.
|
//! A higher level attributes based on TokenTree, with also some shortcuts.
|
||||||
use std::{fmt, ops};
|
use std::{fmt, ops};
|
||||||
|
|
||||||
use ::tt::SpanAnchor as _;
|
use base_db::{span::SyntaxContextId, CrateId};
|
||||||
use base_db::{
|
|
||||||
span::{SpanAnchor, SyntaxContextId},
|
|
||||||
CrateId,
|
|
||||||
};
|
|
||||||
use cfg::CfgExpr;
|
use cfg::CfgExpr;
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use intern::Interned;
|
use intern::Interned;
|
||||||
|
@ -17,8 +13,9 @@ use triomphe::Arc;
|
||||||
use crate::{
|
use crate::{
|
||||||
db::ExpandDatabase,
|
db::ExpandDatabase,
|
||||||
mod_path::ModPath,
|
mod_path::ModPath,
|
||||||
|
span::SpanMapRef,
|
||||||
tt::{self, Subtree},
|
tt::{self, Subtree},
|
||||||
InFile, SpanMap,
|
InFile,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Syntactical attributes, without filtering of `cfg_attr`s.
|
/// Syntactical attributes, without filtering of `cfg_attr`s.
|
||||||
|
@ -44,22 +41,19 @@ impl RawAttrs {
|
||||||
|
|
||||||
pub fn new(
|
pub fn new(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
span_anchor: SpanAnchor,
|
|
||||||
owner: &dyn ast::HasAttrs,
|
owner: &dyn ast::HasAttrs,
|
||||||
hygiene: &SpanMap,
|
hygiene: SpanMapRef<'_>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let entries = collect_attrs(owner)
|
let entries = collect_attrs(owner)
|
||||||
.filter_map(|(id, attr)| match attr {
|
.filter_map(|(id, attr)| match attr {
|
||||||
Either::Left(attr) => {
|
Either::Left(attr) => {
|
||||||
attr.meta().and_then(|meta| Attr::from_src(db, span_anchor, meta, hygiene, id))
|
attr.meta().and_then(|meta| Attr::from_src(db, meta, hygiene, id))
|
||||||
}
|
}
|
||||||
Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
|
Either::Right(comment) => comment.doc_comment().map(|doc| Attr {
|
||||||
id,
|
id,
|
||||||
input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))),
|
input: Some(Interned::new(AttrInput::Literal(SmolStr::new(doc)))),
|
||||||
path: Interned::new(ModPath::from(crate::name!(doc))),
|
path: Interned::new(ModPath::from(crate::name!(doc))),
|
||||||
ctxt: hygiene
|
ctxt: hygiene.span_for_range(comment.syntax().text_range()).ctx,
|
||||||
.span_for_range(comment.syntax().text_range())
|
|
||||||
.map_or(SyntaxContextId::ROOT, |s| s.ctx),
|
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
|
@ -71,10 +65,10 @@ impl RawAttrs {
|
||||||
|
|
||||||
pub fn from_attrs_owner(
|
pub fn from_attrs_owner(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
span_anchor: SpanAnchor,
|
|
||||||
owner: InFile<&dyn ast::HasAttrs>,
|
owner: InFile<&dyn ast::HasAttrs>,
|
||||||
|
hygiene: SpanMapRef<'_>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Self::new(db, span_anchor, owner.value, &db.span_map(owner.file_id))
|
Self::new(db, owner.value, hygiene)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn merge(&self, other: Self) -> Self {
|
pub fn merge(&self, other: Self) -> Self {
|
||||||
|
@ -221,9 +215,8 @@ impl fmt::Display for AttrInput {
|
||||||
impl Attr {
|
impl Attr {
|
||||||
fn from_src(
|
fn from_src(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
span_anchor: SpanAnchor,
|
|
||||||
ast: ast::Meta,
|
ast: ast::Meta,
|
||||||
hygiene: &SpanMap,
|
hygiene: SpanMapRef<'_>,
|
||||||
id: AttrId,
|
id: AttrId,
|
||||||
) -> Option<Attr> {
|
) -> Option<Attr> {
|
||||||
let path = Interned::new(ModPath::from_src(db, ast.path()?, hygiene)?);
|
let path = Interned::new(ModPath::from_src(db, ast.path()?, hygiene)?);
|
||||||
|
@ -234,31 +227,20 @@ impl Attr {
|
||||||
};
|
};
|
||||||
Some(Interned::new(AttrInput::Literal(value)))
|
Some(Interned::new(AttrInput::Literal(value)))
|
||||||
} else if let Some(tt) = ast.token_tree() {
|
} else if let Some(tt) = ast.token_tree() {
|
||||||
// FIXME: We could also allocate ids for attributes and use the attribute itself as an anchor
|
let tree = syntax_node_to_token_tree(tt.syntax(), hygiene);
|
||||||
let offset =
|
|
||||||
db.ast_id_map(span_anchor.file_id).get_raw(span_anchor.ast_id).text_range().start();
|
|
||||||
let tree = syntax_node_to_token_tree(tt.syntax(), span_anchor, offset, hygiene);
|
|
||||||
Some(Interned::new(AttrInput::TokenTree(Box::new(tree))))
|
Some(Interned::new(AttrInput::TokenTree(Box::new(tree))))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
Some(Attr {
|
Some(Attr { id, path, input, ctxt: hygiene.span_for_range(ast.syntax().text_range()).ctx })
|
||||||
id,
|
|
||||||
path,
|
|
||||||
input,
|
|
||||||
ctxt: hygiene
|
|
||||||
.span_for_range(ast.syntax().text_range())
|
|
||||||
.map_or(SyntaxContextId::ROOT, |s| s.ctx),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_tt(db: &dyn ExpandDatabase, tt: &tt::Subtree, id: AttrId) -> Option<Attr> {
|
fn from_tt(db: &dyn ExpandDatabase, tt: &tt::Subtree, id: AttrId) -> Option<Attr> {
|
||||||
// FIXME: Unecessary roundtrip tt -> ast -> tt
|
// FIXME: Unecessary roundtrip tt -> ast -> tt
|
||||||
let (parse, _map) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MetaItem);
|
let (parse, map) = mbe::token_tree_to_syntax_node(tt, mbe::TopEntryPoint::MetaItem);
|
||||||
let ast = ast::Meta::cast(parse.syntax_node())?;
|
let ast = ast::Meta::cast(parse.syntax_node())?;
|
||||||
|
|
||||||
// FIXME: we discard spans here!
|
Self::from_src(db, ast, SpanMapRef::ExpansionSpanMap(&map), id)
|
||||||
Self::from_src(db, SpanAnchor::DUMMY, ast, &SpanMap::default(), id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn path(&self) -> &ModPath {
|
pub fn path(&self) -> &ModPath {
|
||||||
|
@ -331,7 +313,10 @@ impl Attr {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let path = meta.path()?;
|
let path = meta.path()?;
|
||||||
Some((ModPath::from_src(db, path, &span_map)?, call_site))
|
Some((
|
||||||
|
ModPath::from_src(db, path, SpanMapRef::ExpansionSpanMap(&span_map))?,
|
||||||
|
call_site,
|
||||||
|
))
|
||||||
});
|
});
|
||||||
|
|
||||||
Some(paths)
|
Some(paths)
|
||||||
|
|
|
@ -79,9 +79,8 @@ fn dummy_attr_expand(
|
||||||
///
|
///
|
||||||
/// As such, we expand `#[derive(Foo, bar::Bar)]` into
|
/// As such, we expand `#[derive(Foo, bar::Bar)]` into
|
||||||
/// ```
|
/// ```
|
||||||
/// #[Foo]
|
/// #![Foo]
|
||||||
/// #[bar::Bar]
|
/// #![bar::Bar]
|
||||||
/// ();
|
|
||||||
/// ```
|
/// ```
|
||||||
/// which allows fallback path resolution in hir::Semantics to properly identify our derives.
|
/// which allows fallback path resolution in hir::Semantics to properly identify our derives.
|
||||||
/// Since we do not expand the attribute in nameres though, we keep the original item.
|
/// Since we do not expand the attribute in nameres though, we keep the original item.
|
||||||
|
@ -124,12 +123,10 @@ pub fn pseudo_derive_attr_expansion(
|
||||||
.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))))
|
.split(|tt| matches!(tt, tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct { char: ',', .. }))))
|
||||||
{
|
{
|
||||||
token_trees.push(mk_leaf('#'));
|
token_trees.push(mk_leaf('#'));
|
||||||
|
token_trees.push(mk_leaf('!'));
|
||||||
token_trees.push(mk_leaf('['));
|
token_trees.push(mk_leaf('['));
|
||||||
token_trees.extend(tt.iter().cloned());
|
token_trees.extend(tt.iter().cloned());
|
||||||
token_trees.push(mk_leaf(']'));
|
token_trees.push(mk_leaf(']'));
|
||||||
}
|
}
|
||||||
token_trees.push(mk_leaf('('));
|
|
||||||
token_trees.push(mk_leaf(')'));
|
|
||||||
token_trees.push(mk_leaf(';'));
|
|
||||||
ExpandResult::ok(tt::Subtree { delimiter: tt.delimiter, token_trees })
|
ExpandResult::ok(tt::Subtree { delimiter: tt.delimiter, token_trees })
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,20 +1,19 @@
|
||||||
//! Builtin derives.
|
//! Builtin derives.
|
||||||
|
|
||||||
use ::tt::Span;
|
use ::tt::Span;
|
||||||
use base_db::{CrateOrigin, LangCrateOrigin};
|
use base_db::{span::SpanData, CrateOrigin, LangCrateOrigin};
|
||||||
use itertools::izip;
|
use itertools::izip;
|
||||||
use rustc_hash::FxHashSet;
|
use rustc_hash::FxHashSet;
|
||||||
use stdx::never;
|
use stdx::never;
|
||||||
use tracing::debug;
|
use tracing::debug;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
hygiene::span_with_def_site_ctxt,
|
||||||
name::{AsName, Name},
|
name::{AsName, Name},
|
||||||
tt, SpanMap,
|
span::SpanMapRef,
|
||||||
};
|
tt,
|
||||||
use syntax::{
|
|
||||||
ast::{self, AstNode, FieldList, HasAttrs, HasGenericParams, HasName, HasTypeBounds},
|
|
||||||
TextSize,
|
|
||||||
};
|
};
|
||||||
|
use syntax::ast::{self, AstNode, FieldList, HasAttrs, HasGenericParams, HasName, HasTypeBounds};
|
||||||
|
|
||||||
use crate::{db::ExpandDatabase, name, quote, ExpandError, ExpandResult, MacroCallId};
|
use crate::{db::ExpandDatabase, name, quote, ExpandError, ExpandResult, MacroCallId};
|
||||||
|
|
||||||
|
@ -31,12 +30,15 @@ macro_rules! register_builtin {
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
id: MacroCallId,
|
id: MacroCallId,
|
||||||
tt: &ast::Adt,
|
tt: &ast::Adt,
|
||||||
token_map: &SpanMap,
|
token_map: SpanMapRef<'_>,
|
||||||
) -> ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
let expander = match *self {
|
let expander = match *self {
|
||||||
$( BuiltinDeriveExpander::$trait => $expand, )*
|
$( BuiltinDeriveExpander::$trait => $expand, )*
|
||||||
};
|
};
|
||||||
expander(db, id, tt, token_map)
|
|
||||||
|
let span = db.lookup_intern_macro_call(id).span(db);
|
||||||
|
let span = span_with_def_site_ctxt(db, span, id);
|
||||||
|
expander(db, id, span, tt, token_map)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_by_name(name: &name::Name) -> Option<Self> {
|
fn find_by_name(name: &name::Name) -> Option<Self> {
|
||||||
|
@ -119,7 +121,7 @@ impl VariantShape {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from(tm: &SpanMap, value: Option<FieldList>) -> Result<Self, ExpandError> {
|
fn from(tm: SpanMapRef<'_>, value: Option<FieldList>) -> Result<Self, ExpandError> {
|
||||||
let r = match value {
|
let r = match value {
|
||||||
None => VariantShape::Unit,
|
None => VariantShape::Unit,
|
||||||
Some(FieldList::RecordFieldList(it)) => VariantShape::Struct(
|
Some(FieldList::RecordFieldList(it)) => VariantShape::Struct(
|
||||||
|
@ -191,7 +193,7 @@ struct BasicAdtInfo {
|
||||||
associated_types: Vec<tt::Subtree>,
|
associated_types: Vec<tt::Subtree>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_adt(tm: &SpanMap, adt: &ast::Adt) -> Result<BasicAdtInfo, ExpandError> {
|
fn parse_adt(tm: SpanMapRef<'_>, adt: &ast::Adt) -> Result<BasicAdtInfo, ExpandError> {
|
||||||
let (name, generic_param_list, shape) = match adt {
|
let (name, generic_param_list, shape) = match adt {
|
||||||
ast::Adt::Struct(it) => (
|
ast::Adt::Struct(it) => (
|
||||||
it.name(),
|
it.name(),
|
||||||
|
@ -236,44 +238,21 @@ fn parse_adt(tm: &SpanMap, adt: &ast::Adt) -> Result<BasicAdtInfo, ExpandError>
|
||||||
match this {
|
match this {
|
||||||
Some(it) => {
|
Some(it) => {
|
||||||
param_type_set.insert(it.as_name());
|
param_type_set.insert(it.as_name());
|
||||||
mbe::syntax_node_to_token_tree(
|
mbe::syntax_node_to_token_tree(it.syntax(), tm)
|
||||||
it.syntax(),
|
|
||||||
tm.span_for_range(it.syntax().first_token().unwrap().text_range())
|
|
||||||
.unwrap()
|
|
||||||
.anchor,
|
|
||||||
TextSize::from(0),
|
|
||||||
tm,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
None => tt::Subtree::empty(),
|
None => tt::Subtree::empty(),
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let bounds = match ¶m {
|
let bounds = match ¶m {
|
||||||
ast::TypeOrConstParam::Type(it) => it.type_bound_list().map(|it| {
|
ast::TypeOrConstParam::Type(it) => {
|
||||||
mbe::syntax_node_to_token_tree(
|
it.type_bound_list().map(|it| mbe::syntax_node_to_token_tree(it.syntax(), tm))
|
||||||
it.syntax(),
|
}
|
||||||
tm.span_for_range(it.syntax().first_token().unwrap().text_range())
|
|
||||||
.unwrap()
|
|
||||||
.anchor,
|
|
||||||
TextSize::from(0),
|
|
||||||
tm,
|
|
||||||
)
|
|
||||||
}),
|
|
||||||
ast::TypeOrConstParam::Const(_) => None,
|
ast::TypeOrConstParam::Const(_) => None,
|
||||||
};
|
};
|
||||||
let ty = if let ast::TypeOrConstParam::Const(param) = param {
|
let ty = if let ast::TypeOrConstParam::Const(param) = param {
|
||||||
let ty = param
|
let ty = param
|
||||||
.ty()
|
.ty()
|
||||||
.map(|ty| {
|
.map(|ty| mbe::syntax_node_to_token_tree(ty.syntax(), tm))
|
||||||
mbe::syntax_node_to_token_tree(
|
|
||||||
ty.syntax(),
|
|
||||||
tm.span_for_range(ty.syntax().first_token().unwrap().text_range())
|
|
||||||
.unwrap()
|
|
||||||
.anchor,
|
|
||||||
TextSize::from(0),
|
|
||||||
tm,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.unwrap_or_else(tt::Subtree::empty);
|
.unwrap_or_else(tt::Subtree::empty);
|
||||||
Some(ty)
|
Some(ty)
|
||||||
} else {
|
} else {
|
||||||
|
@ -307,25 +286,21 @@ fn parse_adt(tm: &SpanMap, adt: &ast::Adt) -> Result<BasicAdtInfo, ExpandError>
|
||||||
let name = p.path()?.qualifier()?.as_single_name_ref()?.as_name();
|
let name = p.path()?.qualifier()?.as_single_name_ref()?.as_name();
|
||||||
param_type_set.contains(&name).then_some(p)
|
param_type_set.contains(&name).then_some(p)
|
||||||
})
|
})
|
||||||
.map(|it| {
|
.map(|it| mbe::syntax_node_to_token_tree(it.syntax(), tm))
|
||||||
mbe::syntax_node_to_token_tree(
|
|
||||||
it.syntax(),
|
|
||||||
tm.span_for_range(it.syntax().first_token().unwrap().text_range()).unwrap().anchor,
|
|
||||||
TextSize::from(0),
|
|
||||||
tm,
|
|
||||||
)
|
|
||||||
})
|
|
||||||
.collect();
|
.collect();
|
||||||
let name_token = name_to_token(&tm, name)?;
|
let name_token = name_to_token(tm, name)?;
|
||||||
Ok(BasicAdtInfo { name: name_token, shape, param_types, associated_types })
|
Ok(BasicAdtInfo { name: name_token, shape, param_types, associated_types })
|
||||||
}
|
}
|
||||||
|
|
||||||
fn name_to_token(token_map: &SpanMap, name: Option<ast::Name>) -> Result<tt::Ident, ExpandError> {
|
fn name_to_token(
|
||||||
|
token_map: SpanMapRef<'_>,
|
||||||
|
name: Option<ast::Name>,
|
||||||
|
) -> Result<tt::Ident, ExpandError> {
|
||||||
let name = name.ok_or_else(|| {
|
let name = name.ok_or_else(|| {
|
||||||
debug!("parsed item has no name");
|
debug!("parsed item has no name");
|
||||||
ExpandError::other("missing name")
|
ExpandError::other("missing name")
|
||||||
})?;
|
})?;
|
||||||
let span = token_map.span_for_range(name.syntax().text_range()).unwrap();
|
let span = token_map.span_for_range(name.syntax().text_range());
|
||||||
let name_token = tt::Ident { span, text: name.text().into() };
|
let name_token = tt::Ident { span, text: name.text().into() };
|
||||||
Ok(name_token)
|
Ok(name_token)
|
||||||
}
|
}
|
||||||
|
@ -362,8 +337,10 @@ fn name_to_token(token_map: &SpanMap, name: Option<ast::Name>) -> Result<tt::Ide
|
||||||
/// where B1, ..., BN are the bounds given by `bounds_paths`. Z is a phantom type, and
|
/// where B1, ..., BN are the bounds given by `bounds_paths`. Z is a phantom type, and
|
||||||
/// therefore does not get bound by the derived trait.
|
/// therefore does not get bound by the derived trait.
|
||||||
fn expand_simple_derive(
|
fn expand_simple_derive(
|
||||||
|
// FIXME: use
|
||||||
|
_invoc_span: SpanData,
|
||||||
tt: &ast::Adt,
|
tt: &ast::Adt,
|
||||||
tm: &SpanMap,
|
tm: SpanMapRef<'_>,
|
||||||
trait_path: tt::Subtree,
|
trait_path: tt::Subtree,
|
||||||
make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::Subtree,
|
make_trait_body: impl FnOnce(&BasicAdtInfo) -> tt::Subtree,
|
||||||
) -> ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
|
@ -423,21 +400,23 @@ fn find_builtin_crate(db: &dyn ExpandDatabase, id: MacroCallId) -> tt::TokenTree
|
||||||
fn copy_expand(
|
fn copy_expand(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
id: MacroCallId,
|
id: MacroCallId,
|
||||||
|
span: SpanData,
|
||||||
tt: &ast::Adt,
|
tt: &ast::Adt,
|
||||||
tm: &SpanMap,
|
tm: SpanMapRef<'_>,
|
||||||
) -> ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
let krate = find_builtin_crate(db, id);
|
let krate = find_builtin_crate(db, id);
|
||||||
expand_simple_derive(tt, tm, quote! { #krate::marker::Copy }, |_| quote! {})
|
expand_simple_derive(span, tt, tm, quote! { #krate::marker::Copy }, |_| quote! {})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn clone_expand(
|
fn clone_expand(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
id: MacroCallId,
|
id: MacroCallId,
|
||||||
|
span: SpanData,
|
||||||
tt: &ast::Adt,
|
tt: &ast::Adt,
|
||||||
tm: &SpanMap,
|
tm: SpanMapRef<'_>,
|
||||||
) -> ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
let krate = find_builtin_crate(db, id);
|
let krate = find_builtin_crate(db, id);
|
||||||
expand_simple_derive(tt, tm, quote! { #krate::clone::Clone }, |adt| {
|
expand_simple_derive(span, tt, tm, quote! { #krate::clone::Clone }, |adt| {
|
||||||
if matches!(adt.shape, AdtShape::Union) {
|
if matches!(adt.shape, AdtShape::Union) {
|
||||||
let star =
|
let star =
|
||||||
tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span: tt::SpanData::DUMMY };
|
tt::Punct { char: '*', spacing: ::tt::Spacing::Alone, span: tt::SpanData::DUMMY };
|
||||||
|
@ -491,11 +470,12 @@ fn and_and() -> tt::Subtree {
|
||||||
fn default_expand(
|
fn default_expand(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
id: MacroCallId,
|
id: MacroCallId,
|
||||||
|
span: SpanData,
|
||||||
tt: &ast::Adt,
|
tt: &ast::Adt,
|
||||||
tm: &SpanMap,
|
tm: SpanMapRef<'_>,
|
||||||
) -> ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
let krate = &find_builtin_crate(db, id);
|
let krate = &find_builtin_crate(db, id);
|
||||||
expand_simple_derive(tt, tm, quote! { #krate::default::Default }, |adt| {
|
expand_simple_derive(span, tt, tm, quote! { #krate::default::Default }, |adt| {
|
||||||
let body = match &adt.shape {
|
let body = match &adt.shape {
|
||||||
AdtShape::Struct(fields) => {
|
AdtShape::Struct(fields) => {
|
||||||
let name = &adt.name;
|
let name = &adt.name;
|
||||||
|
@ -531,11 +511,12 @@ fn default_expand(
|
||||||
fn debug_expand(
|
fn debug_expand(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
id: MacroCallId,
|
id: MacroCallId,
|
||||||
|
span: SpanData,
|
||||||
tt: &ast::Adt,
|
tt: &ast::Adt,
|
||||||
tm: &SpanMap,
|
tm: SpanMapRef<'_>,
|
||||||
) -> ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
let krate = &find_builtin_crate(db, id);
|
let krate = &find_builtin_crate(db, id);
|
||||||
expand_simple_derive(tt, tm, quote! { #krate::fmt::Debug }, |adt| {
|
expand_simple_derive(span, tt, tm, quote! { #krate::fmt::Debug }, |adt| {
|
||||||
let for_variant = |name: String, v: &VariantShape| match v {
|
let for_variant = |name: String, v: &VariantShape| match v {
|
||||||
VariantShape::Struct(fields) => {
|
VariantShape::Struct(fields) => {
|
||||||
let for_fields = fields.iter().map(|it| {
|
let for_fields = fields.iter().map(|it| {
|
||||||
|
@ -609,11 +590,12 @@ fn debug_expand(
|
||||||
fn hash_expand(
|
fn hash_expand(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
id: MacroCallId,
|
id: MacroCallId,
|
||||||
|
span: SpanData,
|
||||||
tt: &ast::Adt,
|
tt: &ast::Adt,
|
||||||
tm: &SpanMap,
|
tm: SpanMapRef<'_>,
|
||||||
) -> ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
let krate = &find_builtin_crate(db, id);
|
let krate = &find_builtin_crate(db, id);
|
||||||
expand_simple_derive(tt, tm, quote! { #krate::hash::Hash }, |adt| {
|
expand_simple_derive(span, tt, tm, quote! { #krate::hash::Hash }, |adt| {
|
||||||
if matches!(adt.shape, AdtShape::Union) {
|
if matches!(adt.shape, AdtShape::Union) {
|
||||||
// FIXME: Return expand error here
|
// FIXME: Return expand error here
|
||||||
return quote! {};
|
return quote! {};
|
||||||
|
@ -660,21 +642,23 @@ fn hash_expand(
|
||||||
fn eq_expand(
|
fn eq_expand(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
id: MacroCallId,
|
id: MacroCallId,
|
||||||
|
span: SpanData,
|
||||||
tt: &ast::Adt,
|
tt: &ast::Adt,
|
||||||
tm: &SpanMap,
|
tm: SpanMapRef<'_>,
|
||||||
) -> ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
let krate = find_builtin_crate(db, id);
|
let krate = find_builtin_crate(db, id);
|
||||||
expand_simple_derive(tt, tm, quote! { #krate::cmp::Eq }, |_| quote! {})
|
expand_simple_derive(span, tt, tm, quote! { #krate::cmp::Eq }, |_| quote! {})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn partial_eq_expand(
|
fn partial_eq_expand(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
id: MacroCallId,
|
id: MacroCallId,
|
||||||
|
span: SpanData,
|
||||||
tt: &ast::Adt,
|
tt: &ast::Adt,
|
||||||
tm: &SpanMap,
|
tm: SpanMapRef<'_>,
|
||||||
) -> ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
let krate = find_builtin_crate(db, id);
|
let krate = find_builtin_crate(db, id);
|
||||||
expand_simple_derive(tt, tm, quote! { #krate::cmp::PartialEq }, |adt| {
|
expand_simple_derive(span, tt, tm, quote! { #krate::cmp::PartialEq }, |adt| {
|
||||||
if matches!(adt.shape, AdtShape::Union) {
|
if matches!(adt.shape, AdtShape::Union) {
|
||||||
// FIXME: Return expand error here
|
// FIXME: Return expand error here
|
||||||
return quote! {};
|
return quote! {};
|
||||||
|
@ -738,11 +722,12 @@ fn self_and_other_patterns(
|
||||||
fn ord_expand(
|
fn ord_expand(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
id: MacroCallId,
|
id: MacroCallId,
|
||||||
|
span: SpanData,
|
||||||
tt: &ast::Adt,
|
tt: &ast::Adt,
|
||||||
tm: &SpanMap,
|
tm: SpanMapRef<'_>,
|
||||||
) -> ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
let krate = &find_builtin_crate(db, id);
|
let krate = &find_builtin_crate(db, id);
|
||||||
expand_simple_derive(tt, tm, quote! { #krate::cmp::Ord }, |adt| {
|
expand_simple_derive(span, tt, tm, quote! { #krate::cmp::Ord }, |adt| {
|
||||||
fn compare(
|
fn compare(
|
||||||
krate: &tt::TokenTree,
|
krate: &tt::TokenTree,
|
||||||
left: tt::Subtree,
|
left: tt::Subtree,
|
||||||
|
@ -800,11 +785,12 @@ fn ord_expand(
|
||||||
fn partial_ord_expand(
|
fn partial_ord_expand(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
id: MacroCallId,
|
id: MacroCallId,
|
||||||
|
span: SpanData,
|
||||||
tt: &ast::Adt,
|
tt: &ast::Adt,
|
||||||
tm: &SpanMap,
|
tm: SpanMapRef<'_>,
|
||||||
) -> ExpandResult<tt::Subtree> {
|
) -> ExpandResult<tt::Subtree> {
|
||||||
let krate = &find_builtin_crate(db, id);
|
let krate = &find_builtin_crate(db, id);
|
||||||
expand_simple_derive(tt, tm, quote! { #krate::cmp::PartialOrd }, |adt| {
|
expand_simple_derive(span, tt, tm, quote! { #krate::cmp::PartialOrd }, |adt| {
|
||||||
fn compare(
|
fn compare(
|
||||||
krate: &tt::TokenTree,
|
krate: &tt::TokenTree,
|
||||||
left: tt::Subtree,
|
left: tt::Subtree,
|
||||||
|
|
|
@ -556,9 +556,10 @@ pub(crate) fn include_arg_to_tt(
|
||||||
let path = parse_string(&arg.0)?;
|
let path = parse_string(&arg.0)?;
|
||||||
let file_id = relative_file(db, *arg_id, &path, false)?;
|
let file_id = relative_file(db, *arg_id, &path, false)?;
|
||||||
|
|
||||||
|
// why are we not going through a SyntaxNode here?
|
||||||
let subtree = parse_to_token_tree(
|
let subtree = parse_to_token_tree(
|
||||||
|
SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
|
||||||
&db.file_text(file_id),
|
&db.file_text(file_id),
|
||||||
SpanAnchor { file_id: file_id.into(), ast_id: ROOT_ERASED_FILE_AST_ID },
|
|
||||||
)
|
)
|
||||||
.ok_or(mbe::ExpandError::ConversionError)?;
|
.ok_or(mbe::ExpandError::ConversionError)?;
|
||||||
Ok((triomphe::Arc::new(subtree), file_id))
|
Ok((triomphe::Arc::new(subtree), file_id))
|
||||||
|
|
|
@ -3,15 +3,15 @@
|
||||||
use ::tt::{SpanAnchor as _, SyntaxContext};
|
use ::tt::{SpanAnchor as _, SyntaxContext};
|
||||||
use base_db::{
|
use base_db::{
|
||||||
salsa,
|
salsa,
|
||||||
span::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID},
|
span::{SpanAnchor, SyntaxContextId},
|
||||||
CrateId, Edition, SourceDatabase,
|
CrateId, Edition, FileId, SourceDatabase,
|
||||||
};
|
};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use limit::Limit;
|
use limit::Limit;
|
||||||
use mbe::{map_from_syntax_node, syntax_node_to_token_tree, ValueResult};
|
use mbe::{syntax_node_to_token_tree, ValueResult};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, HasAttrs, HasDocComments},
|
ast::{self, HasAttrs, HasDocComments},
|
||||||
AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, TextSize, T,
|
AstNode, Parse, SyntaxError, SyntaxNode, SyntaxToken, T,
|
||||||
};
|
};
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
|
@ -21,9 +21,10 @@ use crate::{
|
||||||
builtin_attr_macro::pseudo_derive_attr_expansion,
|
builtin_attr_macro::pseudo_derive_attr_expansion,
|
||||||
builtin_fn_macro::EagerExpander,
|
builtin_fn_macro::EagerExpander,
|
||||||
hygiene::{self, SyntaxContextData, Transparency},
|
hygiene::{self, SyntaxContextData, Transparency},
|
||||||
|
span::{RealSpanMap, SpanMap, SpanMapRef},
|
||||||
tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo,
|
tt, AstId, BuiltinAttrExpander, BuiltinDeriveExpander, BuiltinFnLikeExpander, EagerCallInfo,
|
||||||
ExpandError, ExpandResult, ExpandTo, HirFileId, HirFileIdRepr, MacroCallId, MacroCallKind,
|
ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, HirFileId, HirFileIdRepr, MacroCallId,
|
||||||
MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander, SpanMap,
|
MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind, MacroFile, ProcMacroExpander,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Total limit on the number of tokens produced by any macro invocation.
|
/// Total limit on the number of tokens produced by any macro invocation.
|
||||||
|
@ -102,10 +103,11 @@ pub trait ExpandDatabase: SourceDatabase {
|
||||||
fn parse_macro_expansion(
|
fn parse_macro_expansion(
|
||||||
&self,
|
&self,
|
||||||
macro_file: MacroFile,
|
macro_file: MacroFile,
|
||||||
) -> ExpandResult<(Parse<SyntaxNode>, Arc<SpanMap>)>;
|
) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)>;
|
||||||
// FIXME: This always allocates one for non macro files which is wasteful.
|
|
||||||
#[salsa::transparent]
|
#[salsa::transparent]
|
||||||
fn span_map(&self, file_id: HirFileId) -> Arc<SpanMap>;
|
fn span_map(&self, file_id: HirFileId) -> SpanMap;
|
||||||
|
|
||||||
|
fn real_span_map(&self, file_id: FileId) -> Arc<RealSpanMap>;
|
||||||
|
|
||||||
/// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
|
/// Macro ids. That's probably the tricksiest bit in rust-analyzer, and the
|
||||||
/// reason why we use salsa at all.
|
/// reason why we use salsa at all.
|
||||||
|
@ -164,12 +166,19 @@ pub trait ExpandDatabase: SourceDatabase {
|
||||||
) -> ExpandResult<Box<[SyntaxError]>>;
|
) -> ExpandResult<Box<[SyntaxError]>>;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn span_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> Arc<SpanMap> {
|
#[inline]
|
||||||
|
pub fn span_map(db: &dyn ExpandDatabase, file_id: HirFileId) -> SpanMap {
|
||||||
match file_id.repr() {
|
match file_id.repr() {
|
||||||
HirFileIdRepr::FileId(_) => Arc::new(Default::default()),
|
HirFileIdRepr::FileId(file_id) => SpanMap::RealSpanMap(db.real_span_map(file_id)),
|
||||||
HirFileIdRepr::MacroFile(m) => db.parse_macro_expansion(m).value.1,
|
HirFileIdRepr::MacroFile(m) => {
|
||||||
|
SpanMap::ExpansionSpanMap(db.parse_macro_expansion(m).value.1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn real_span_map(db: &dyn ExpandDatabase, file_id: FileId) -> Arc<RealSpanMap> {
|
||||||
|
Arc::new(RealSpanMap::from_file(db, file_id))
|
||||||
|
}
|
||||||
|
|
||||||
/// This expands the given macro call, but with different arguments. This is
|
/// This expands the given macro call, but with different arguments. This is
|
||||||
/// used for completion, where we want to see what 'would happen' if we insert a
|
/// used for completion, where we want to see what 'would happen' if we insert a
|
||||||
|
@ -181,17 +190,15 @@ pub fn expand_speculative(
|
||||||
speculative_args: &SyntaxNode,
|
speculative_args: &SyntaxNode,
|
||||||
token_to_map: SyntaxToken,
|
token_to_map: SyntaxToken,
|
||||||
) -> Option<(SyntaxNode, SyntaxToken)> {
|
) -> Option<(SyntaxNode, SyntaxToken)> {
|
||||||
|
// FIXME spanmaps
|
||||||
let loc = db.lookup_intern_macro_call(actual_macro_call);
|
let loc = db.lookup_intern_macro_call(actual_macro_call);
|
||||||
let file_id = loc.kind.file_id();
|
|
||||||
|
|
||||||
// Build the subtree and token mapping for the speculative args
|
// Build the subtree and token mapping for the speculative args
|
||||||
let _censor = censor_for_macro_input(&loc, speculative_args);
|
let _censor = censor_for_macro_input(&loc, speculative_args);
|
||||||
let mut tt = mbe::syntax_node_to_token_tree(
|
let mut tt = mbe::syntax_node_to_token_tree(
|
||||||
speculative_args,
|
speculative_args,
|
||||||
// we don't leak these spans into any query so its fine to make them absolute
|
// we don't leak these spans into any query so its fine to make them absolute
|
||||||
SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
|
SpanMapRef::RealSpanMap(&RealSpanMap::empty(SpanAnchor::DUMMY.file_id)),
|
||||||
TextSize::new(0),
|
|
||||||
&Default::default(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
let attr_arg = match loc.kind {
|
let attr_arg = match loc.kind {
|
||||||
|
@ -211,9 +218,7 @@ pub fn expand_speculative(
|
||||||
Some(token_tree) => {
|
Some(token_tree) => {
|
||||||
let mut tree = syntax_node_to_token_tree(
|
let mut tree = syntax_node_to_token_tree(
|
||||||
token_tree.syntax(),
|
token_tree.syntax(),
|
||||||
SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
|
SpanMapRef::RealSpanMap(&RealSpanMap::empty(SpanAnchor::DUMMY.file_id)),
|
||||||
TextSize::new(0),
|
|
||||||
&Default::default(),
|
|
||||||
);
|
);
|
||||||
tree.delimiter = tt::Delimiter::UNSPECIFIED;
|
tree.delimiter = tt::Delimiter::UNSPECIFIED;
|
||||||
|
|
||||||
|
@ -242,12 +247,7 @@ pub fn expand_speculative(
|
||||||
db,
|
db,
|
||||||
actual_macro_call,
|
actual_macro_call,
|
||||||
&adt,
|
&adt,
|
||||||
&map_from_syntax_node(
|
SpanMapRef::RealSpanMap(&RealSpanMap::empty(SpanAnchor::DUMMY.file_id)),
|
||||||
speculative_args,
|
|
||||||
// we don't leak these spans into any query so its fine to make them absolute
|
|
||||||
SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
|
|
||||||
TextSize::new(0),
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
MacroDefKind::Declarative(it) => {
|
MacroDefKind::Declarative(it) => {
|
||||||
|
@ -261,15 +261,13 @@ pub fn expand_speculative(
|
||||||
};
|
};
|
||||||
|
|
||||||
let expand_to = macro_expand_to(db, actual_macro_call);
|
let expand_to = macro_expand_to(db, actual_macro_call);
|
||||||
let (node, mut rev_tmap) =
|
let (node, rev_tmap) = token_tree_to_syntax_node(db, &speculative_expansion.value, expand_to);
|
||||||
token_tree_to_syntax_node(db, &speculative_expansion.value, expand_to);
|
|
||||||
rev_tmap.real_file = false;
|
|
||||||
|
|
||||||
let syntax_node = node.syntax_node();
|
let syntax_node = node.syntax_node();
|
||||||
let token = rev_tmap
|
let token = rev_tmap
|
||||||
.ranges_with_span(tt::SpanData {
|
.ranges_with_span(tt::SpanData {
|
||||||
range: token_to_map.text_range(),
|
range: token_to_map.text_range(),
|
||||||
anchor: SpanAnchor { file_id, ast_id: ROOT_ERASED_FILE_AST_ID },
|
anchor: SpanAnchor::DUMMY,
|
||||||
ctx: SyntaxContextId::DUMMY,
|
ctx: SyntaxContextId::DUMMY,
|
||||||
})
|
})
|
||||||
.filter_map(|range| syntax_node.covering_element(range).into_token())
|
.filter_map(|range| syntax_node.covering_element(range).into_token())
|
||||||
|
@ -310,7 +308,7 @@ fn parse_or_expand_with_err(
|
||||||
fn parse_macro_expansion(
|
fn parse_macro_expansion(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
macro_file: MacroFile,
|
macro_file: MacroFile,
|
||||||
) -> ExpandResult<(Parse<SyntaxNode>, Arc<SpanMap>)> {
|
) -> ExpandResult<(Parse<SyntaxNode>, Arc<ExpansionSpanMap>)> {
|
||||||
let _p = profile::span("parse_macro_expansion");
|
let _p = profile::span("parse_macro_expansion");
|
||||||
let mbe::ValueResult { value: tt, err } = db.macro_expand(macro_file.macro_call_id);
|
let mbe::ValueResult { value: tt, err } = db.macro_expand(macro_file.macro_call_id);
|
||||||
|
|
||||||
|
@ -319,8 +317,7 @@ fn parse_macro_expansion(
|
||||||
tracing::debug!("expanded = {}", tt.as_debug_string());
|
tracing::debug!("expanded = {}", tt.as_debug_string());
|
||||||
tracing::debug!("kind = {:?}", expand_to);
|
tracing::debug!("kind = {:?}", expand_to);
|
||||||
|
|
||||||
let (parse, mut rev_token_map) = token_tree_to_syntax_node(db, &tt, expand_to);
|
let (parse, rev_token_map) = token_tree_to_syntax_node(db, &tt, expand_to);
|
||||||
rev_token_map.real_file = false;
|
|
||||||
|
|
||||||
ExpandResult { value: (parse, Arc::new(rev_token_map)), err }
|
ExpandResult { value: (parse, Arc::new(rev_token_map)), err }
|
||||||
}
|
}
|
||||||
|
@ -366,18 +363,21 @@ fn macro_arg(
|
||||||
{
|
{
|
||||||
ValueResult::ok(Some(Arc::new(arg.0.clone())))
|
ValueResult::ok(Some(Arc::new(arg.0.clone())))
|
||||||
} else {
|
} else {
|
||||||
|
//FIXME: clean this up, the ast id map lookup is done twice here
|
||||||
let (parse, map) = match loc.kind.file_id().repr() {
|
let (parse, map) = match loc.kind.file_id().repr() {
|
||||||
HirFileIdRepr::FileId(file_id) => {
|
HirFileIdRepr::FileId(file_id) => {
|
||||||
(db.parse(file_id).to_syntax(), Arc::new(Default::default()))
|
let syntax = db.parse(file_id).to_syntax();
|
||||||
|
|
||||||
|
(syntax, SpanMap::RealSpanMap(db.real_span_map(file_id)))
|
||||||
}
|
}
|
||||||
HirFileIdRepr::MacroFile(macro_file) => {
|
HirFileIdRepr::MacroFile(macro_file) => {
|
||||||
let (parse, map) = db.parse_macro_expansion(macro_file).value;
|
let (parse, map) = db.parse_macro_expansion(macro_file).value;
|
||||||
(parse, map)
|
(parse, SpanMap::ExpansionSpanMap(map))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let root = parse.syntax_node();
|
let root = parse.syntax_node();
|
||||||
|
|
||||||
let (syntax, offset, ast_id) = match loc.kind {
|
let syntax = match loc.kind {
|
||||||
MacroCallKind::FnLike { ast_id, .. } => {
|
MacroCallKind::FnLike { ast_id, .. } => {
|
||||||
let node = &ast_id.to_ptr(db).to_node(&root);
|
let node = &ast_id.to_ptr(db).to_node(&root);
|
||||||
let offset = node.syntax().text_range().start();
|
let offset = node.syntax().text_range().start();
|
||||||
|
@ -386,7 +386,7 @@ fn macro_arg(
|
||||||
if let Some(e) = mismatched_delimiters(&tt) {
|
if let Some(e) = mismatched_delimiters(&tt) {
|
||||||
return ValueResult::only_err(e);
|
return ValueResult::only_err(e);
|
||||||
}
|
}
|
||||||
(tt, offset, ast_id.value.erase())
|
tt
|
||||||
}
|
}
|
||||||
None => {
|
None => {
|
||||||
return ValueResult::only_err(Arc::new(Box::new([
|
return ValueResult::only_err(Arc::new(Box::new([
|
||||||
|
@ -396,15 +396,9 @@ fn macro_arg(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
MacroCallKind::Derive { ast_id, .. } => {
|
MacroCallKind::Derive { ast_id, .. } => {
|
||||||
let syntax_node = ast_id.to_ptr(db).to_node(&root).syntax().clone();
|
ast_id.to_ptr(db).to_node(&root).syntax().clone()
|
||||||
let offset = syntax_node.text_range().start();
|
|
||||||
(syntax_node, offset, ast_id.value.erase())
|
|
||||||
}
|
|
||||||
MacroCallKind::Attr { ast_id, .. } => {
|
|
||||||
let syntax_node = ast_id.to_ptr(db).to_node(&root).syntax().clone();
|
|
||||||
let offset = syntax_node.text_range().start();
|
|
||||||
(syntax_node, offset, ast_id.value.erase())
|
|
||||||
}
|
}
|
||||||
|
MacroCallKind::Attr { ast_id, .. } => ast_id.to_ptr(db).to_node(&root).syntax().clone(),
|
||||||
};
|
};
|
||||||
let censor = censor_for_macro_input(&loc, &syntax);
|
let censor = censor_for_macro_input(&loc, &syntax);
|
||||||
// let mut fixups = fixup::fixup_syntax(&node);
|
// let mut fixups = fixup::fixup_syntax(&node);
|
||||||
|
@ -416,13 +410,8 @@ fn macro_arg(
|
||||||
// fixups.replace,
|
// fixups.replace,
|
||||||
// fixups.append,
|
// fixups.append,
|
||||||
// );
|
// );
|
||||||
let mut tt = mbe::syntax_node_to_token_tree_censored(
|
|
||||||
&syntax,
|
let mut tt = mbe::syntax_node_to_token_tree_censored(&syntax, map.as_ref(), censor);
|
||||||
SpanAnchor { file_id: loc.kind.file_id(), ast_id },
|
|
||||||
offset,
|
|
||||||
&map,
|
|
||||||
censor,
|
|
||||||
);
|
|
||||||
|
|
||||||
if loc.def.is_proc_macro() {
|
if loc.def.is_proc_macro() {
|
||||||
// proc macros expect their inputs without parentheses, MBEs expect it with them included
|
// proc macros expect their inputs without parentheses, MBEs expect it with them included
|
||||||
|
@ -492,18 +481,19 @@ fn decl_macro_expander(
|
||||||
let is_2021 = db.crate_graph()[def_crate].edition >= Edition::Edition2021;
|
let is_2021 = db.crate_graph()[def_crate].edition >= Edition::Edition2021;
|
||||||
let (root, map) = match id.file_id.repr() {
|
let (root, map) = match id.file_id.repr() {
|
||||||
HirFileIdRepr::FileId(file_id) => {
|
HirFileIdRepr::FileId(file_id) => {
|
||||||
(db.parse(file_id).syntax_node(), Arc::new(Default::default()))
|
// FIXME: Arc
|
||||||
|
// FIXME: id.to_ptr duplicated, expensive
|
||||||
|
(db.parse(file_id).syntax_node(), SpanMap::RealSpanMap(db.real_span_map(file_id)))
|
||||||
}
|
}
|
||||||
HirFileIdRepr::MacroFile(macro_file) => {
|
HirFileIdRepr::MacroFile(macro_file) => {
|
||||||
let (parse, map) = db.parse_macro_expansion(macro_file).value;
|
let (parse, map) = db.parse_macro_expansion(macro_file).value;
|
||||||
(parse.syntax_node(), map)
|
(parse.syntax_node(), SpanMap::ExpansionSpanMap(map))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
let transparency = |node| {
|
let transparency = |node| {
|
||||||
// ... would be nice to have the item tree here
|
// ... would be nice to have the item tree here
|
||||||
let attrs =
|
let attrs = RawAttrs::new(db, node, map.as_ref()).filter(db, def_crate);
|
||||||
RawAttrs::new(db, SpanAnchor::DUMMY, node, &Default::default()).filter(db, def_crate);
|
|
||||||
match &*attrs
|
match &*attrs
|
||||||
.iter()
|
.iter()
|
||||||
.find(|it| {
|
.find(|it| {
|
||||||
|
@ -526,12 +516,7 @@ fn decl_macro_expander(
|
||||||
ast::Macro::MacroRules(macro_rules) => (
|
ast::Macro::MacroRules(macro_rules) => (
|
||||||
match macro_rules.token_tree() {
|
match macro_rules.token_tree() {
|
||||||
Some(arg) => {
|
Some(arg) => {
|
||||||
let tt = mbe::syntax_node_to_token_tree(
|
let tt = mbe::syntax_node_to_token_tree(arg.syntax(), map.as_ref());
|
||||||
arg.syntax(),
|
|
||||||
SpanAnchor { file_id: id.file_id, ast_id: id.value.erase() },
|
|
||||||
macro_rules.syntax().text_range().start(),
|
|
||||||
&map,
|
|
||||||
);
|
|
||||||
let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021);
|
let mac = mbe::DeclarativeMacro::parse_macro_rules(&tt, is_2021);
|
||||||
mac
|
mac
|
||||||
}
|
}
|
||||||
|
@ -545,12 +530,7 @@ fn decl_macro_expander(
|
||||||
ast::Macro::MacroDef(macro_def) => (
|
ast::Macro::MacroDef(macro_def) => (
|
||||||
match macro_def.body() {
|
match macro_def.body() {
|
||||||
Some(arg) => {
|
Some(arg) => {
|
||||||
let tt = mbe::syntax_node_to_token_tree(
|
let tt = mbe::syntax_node_to_token_tree(arg.syntax(), map.as_ref());
|
||||||
arg.syntax(),
|
|
||||||
SpanAnchor { file_id: id.file_id, ast_id: id.value.erase() },
|
|
||||||
macro_def.syntax().text_range().start(),
|
|
||||||
&map,
|
|
||||||
);
|
|
||||||
let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021);
|
let mac = mbe::DeclarativeMacro::parse_macro2(&tt, is_2021);
|
||||||
mac
|
mac
|
||||||
}
|
}
|
||||||
|
@ -591,10 +571,16 @@ fn macro_expand(
|
||||||
// FIXME: add firewall query for this?
|
// FIXME: add firewall query for this?
|
||||||
let hir_file_id = loc.kind.file_id();
|
let hir_file_id = loc.kind.file_id();
|
||||||
let (root, map) = match hir_file_id.repr() {
|
let (root, map) = match hir_file_id.repr() {
|
||||||
HirFileIdRepr::FileId(file_id) => (db.parse(file_id).syntax_node(), None),
|
HirFileIdRepr::FileId(file_id) => {
|
||||||
|
// FIXME: query for span map
|
||||||
|
(
|
||||||
|
db.parse(file_id).syntax_node(),
|
||||||
|
SpanMap::RealSpanMap(db.real_span_map(file_id)),
|
||||||
|
)
|
||||||
|
}
|
||||||
HirFileIdRepr::MacroFile(macro_file) => {
|
HirFileIdRepr::MacroFile(macro_file) => {
|
||||||
let (parse, map) = db.parse_macro_expansion(macro_file).value;
|
let (parse, map) = db.parse_macro_expansion(macro_file).value;
|
||||||
(parse.syntax_node(), Some(map))
|
(parse.syntax_node(), SpanMap::ExpansionSpanMap(map))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let MacroCallKind::Derive { ast_id, .. } = loc.kind else { unreachable!() };
|
let MacroCallKind::Derive { ast_id, .. } = loc.kind else { unreachable!() };
|
||||||
|
@ -602,23 +588,7 @@ fn macro_expand(
|
||||||
|
|
||||||
// FIXME: we might need to remove the spans from the input to the derive macro here
|
// FIXME: we might need to remove the spans from the input to the derive macro here
|
||||||
let _censor = censor_for_macro_input(&loc, node.syntax());
|
let _censor = censor_for_macro_input(&loc, node.syntax());
|
||||||
let _t;
|
expander.expand(db, macro_call_id, &node, map.as_ref())
|
||||||
expander.expand(
|
|
||||||
db,
|
|
||||||
macro_call_id,
|
|
||||||
&node,
|
|
||||||
match &map {
|
|
||||||
Some(map) => map,
|
|
||||||
None => {
|
|
||||||
_t = map_from_syntax_node(
|
|
||||||
node.syntax(),
|
|
||||||
SpanAnchor { file_id: hir_file_id, ast_id: ast_id.value.erase() },
|
|
||||||
node.syntax().text_range().start(),
|
|
||||||
);
|
|
||||||
&_t
|
|
||||||
}
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let ValueResult { value, err } = db.macro_arg(macro_call_id);
|
let ValueResult { value, err } = db.macro_arg(macro_call_id);
|
||||||
|
@ -732,7 +702,7 @@ fn token_tree_to_syntax_node(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
tt: &tt::Subtree,
|
tt: &tt::Subtree,
|
||||||
expand_to: ExpandTo,
|
expand_to: ExpandTo,
|
||||||
) -> (Parse<SyntaxNode>, SpanMap) {
|
) -> (Parse<SyntaxNode>, ExpansionSpanMap) {
|
||||||
let entry_point = match expand_to {
|
let entry_point = match expand_to {
|
||||||
ExpandTo::Statements => mbe::TopEntryPoint::MacroStmts,
|
ExpandTo::Statements => mbe::TopEntryPoint::MacroStmts,
|
||||||
ExpandTo::Items => mbe::TopEntryPoint::MacroItems,
|
ExpandTo::Items => mbe::TopEntryPoint::MacroItems,
|
||||||
|
@ -741,14 +711,14 @@ fn token_tree_to_syntax_node(
|
||||||
ExpandTo::Expr => mbe::TopEntryPoint::Expr,
|
ExpandTo::Expr => mbe::TopEntryPoint::Expr,
|
||||||
};
|
};
|
||||||
let mut tm = mbe::token_tree_to_syntax_node(tt, entry_point);
|
let mut tm = mbe::token_tree_to_syntax_node(tt, entry_point);
|
||||||
// now what the hell is going on here
|
// FIXME: now what the hell is going on here
|
||||||
tm.1.span_map.sort_by(|(_, a), (_, b)| {
|
tm.1.span_map.sort_by(|(_, a), (_, b)| {
|
||||||
a.anchor.file_id.cmp(&b.anchor.file_id).then_with(|| {
|
a.anchor.file_id.cmp(&b.anchor.file_id).then_with(|| {
|
||||||
let map = db.ast_id_map(a.anchor.file_id);
|
let map = db.ast_id_map(a.anchor.file_id.into());
|
||||||
map.get_raw(a.anchor.ast_id)
|
map.get_erased(a.anchor.ast_id)
|
||||||
.text_range()
|
.text_range()
|
||||||
.start()
|
.start()
|
||||||
.cmp(&map.get_raw(b.anchor.ast_id).text_range().start())
|
.cmp(&map.get_erased(b.anchor.ast_id).text_range().start())
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
tm
|
tm
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
//!
|
//!
|
||||||
//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
|
//! See the full discussion : <https://rust-lang.zulipchat.com/#narrow/stream/131828-t-compiler/topic/Eager.20expansion.20of.20built-in.20macros>
|
||||||
use base_db::{
|
use base_db::{
|
||||||
span::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID},
|
span::{SpanAnchor, SyntaxContextId},
|
||||||
CrateId,
|
CrateId,
|
||||||
};
|
};
|
||||||
use rustc_hash::FxHashMap;
|
use rustc_hash::FxHashMap;
|
||||||
|
@ -30,8 +30,9 @@ use crate::{
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
db::ExpandDatabase,
|
db::ExpandDatabase,
|
||||||
mod_path::ModPath,
|
mod_path::ModPath,
|
||||||
EagerCallInfo, ExpandError, ExpandResult, ExpandTo, InFile, MacroCallId, MacroCallKind,
|
span::{RealSpanMap, SpanMapRef},
|
||||||
MacroCallLoc, MacroDefId, MacroDefKind, SpanMap,
|
EagerCallInfo, ExpandError, ExpandResult, ExpandTo, ExpansionSpanMap, InFile, MacroCallId,
|
||||||
|
MacroCallKind, MacroCallLoc, MacroDefId, MacroDefKind,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn expand_eager_macro_input(
|
pub fn expand_eager_macro_input(
|
||||||
|
@ -39,6 +40,7 @@ pub fn expand_eager_macro_input(
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
macro_call: InFile<ast::MacroCall>,
|
macro_call: InFile<ast::MacroCall>,
|
||||||
def: MacroDefId,
|
def: MacroDefId,
|
||||||
|
call_site: SyntaxContextId,
|
||||||
resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
|
resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
|
||||||
) -> ExpandResult<Option<MacroCallId>> {
|
) -> ExpandResult<Option<MacroCallId>> {
|
||||||
let ast_map = db.ast_id_map(macro_call.file_id);
|
let ast_map = db.ast_id_map(macro_call.file_id);
|
||||||
|
@ -55,18 +57,10 @@ pub fn expand_eager_macro_input(
|
||||||
krate,
|
krate,
|
||||||
eager: None,
|
eager: None,
|
||||||
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr },
|
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to: ExpandTo::Expr },
|
||||||
// FIXME
|
call_site,
|
||||||
call_site: SyntaxContextId::ROOT,
|
|
||||||
});
|
});
|
||||||
let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } =
|
let ExpandResult { value: (arg_exp, arg_exp_map), err: parse_err } =
|
||||||
db.parse_macro_expansion(arg_id.as_macro_file());
|
db.parse_macro_expansion(arg_id.as_macro_file());
|
||||||
// we need this map here as the expansion of the eager input fake file loses whitespace ...
|
|
||||||
// let mut ws_mapping = FxHashMap::default();
|
|
||||||
// if let Some((tm)) = db.macro_arg(arg_id).value.as_deref() {
|
|
||||||
// ws_mapping.extend(tm.entries().filter_map(|(id, range)| {
|
|
||||||
// Some((arg_exp_map.first_range_by_token(id, syntax::SyntaxKind::TOMBSTONE)?, range))
|
|
||||||
// }));
|
|
||||||
// }
|
|
||||||
|
|
||||||
let ExpandResult { value: expanded_eager_input, err } = {
|
let ExpandResult { value: expanded_eager_input, err } = {
|
||||||
eager_macro_recur(
|
eager_macro_recur(
|
||||||
|
@ -74,6 +68,7 @@ pub fn expand_eager_macro_input(
|
||||||
&arg_exp_map,
|
&arg_exp_map,
|
||||||
InFile::new(arg_id.as_file(), arg_exp.syntax_node()),
|
InFile::new(arg_id.as_file(), arg_exp.syntax_node()),
|
||||||
krate,
|
krate,
|
||||||
|
call_site,
|
||||||
resolver,
|
resolver,
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
@ -83,44 +78,12 @@ pub fn expand_eager_macro_input(
|
||||||
return ExpandResult { value: None, err };
|
return ExpandResult { value: None, err };
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// FIXME: Spans!
|
||||||
let mut subtree = mbe::syntax_node_to_token_tree(
|
let mut subtree = mbe::syntax_node_to_token_tree(
|
||||||
&expanded_eager_input,
|
&expanded_eager_input,
|
||||||
// is this right?
|
RealSpanMap::empty(<SpanAnchor as tt::SpanAnchor>::DUMMY.file_id),
|
||||||
SpanAnchor { file_id: arg_id.as_file(), ast_id: ROOT_ERASED_FILE_AST_ID },
|
|
||||||
TextSize::new(0),
|
|
||||||
// FIXME: Spans! `eager_macro_recur` needs to fill out a span map for us
|
|
||||||
&Default::default(),
|
|
||||||
);
|
);
|
||||||
|
|
||||||
// let og_tmap = if let Some(tt) = macro_call.value.token_tree() {
|
|
||||||
// let mut ids_used = FxHashSet::default();
|
|
||||||
// let mut og_tmap = mbe::syntax_node_to_token_map(tt.syntax());
|
|
||||||
// // The tokenmap and ids of subtree point into the expanded syntax node, but that is inaccessible from the outside
|
|
||||||
// // so we need to remap them to the original input of the eager macro.
|
|
||||||
// subtree.visit_ids(&mut |id| {
|
|
||||||
// // Note: we discard all token ids of braces and the like here, but that's not too bad and only a temporary fix
|
|
||||||
|
|
||||||
// if let Some(range) = expanded_eager_input_token_map
|
|
||||||
// .first_range_by_token(id, syntax::SyntaxKind::TOMBSTONE)
|
|
||||||
// {
|
|
||||||
// // remap from expanded eager input to eager input expansion
|
|
||||||
// if let Some(og_range) = mapping.get(&range) {
|
|
||||||
// // remap from eager input expansion to original eager input
|
|
||||||
// if let Some(&og_range) = ws_mapping.get(og_range) {
|
|
||||||
// if let Some(og_token) = og_tmap.token_by_range(og_range) {
|
|
||||||
// ids_used.insert(og_token);
|
|
||||||
// return og_token;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// tt::TokenId::UNSPECIFIED
|
|
||||||
// });
|
|
||||||
// og_tmap.filter(|id| ids_used.contains(&id));
|
|
||||||
// og_tmap
|
|
||||||
// } else {
|
|
||||||
// Default::default()
|
|
||||||
// };
|
|
||||||
subtree.delimiter = crate::tt::Delimiter::UNSPECIFIED;
|
subtree.delimiter = crate::tt::Delimiter::UNSPECIFIED;
|
||||||
|
|
||||||
let loc = MacroCallLoc {
|
let loc = MacroCallLoc {
|
||||||
|
@ -132,8 +95,7 @@ pub fn expand_eager_macro_input(
|
||||||
error: err.clone(),
|
error: err.clone(),
|
||||||
})),
|
})),
|
||||||
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
|
kind: MacroCallKind::FnLike { ast_id: call_id, expand_to },
|
||||||
// FIXME
|
call_site,
|
||||||
call_site: SyntaxContextId::ROOT,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
ExpandResult { value: Some(db.intern_macro_call(loc)), err }
|
ExpandResult { value: Some(db.intern_macro_call(loc)), err }
|
||||||
|
@ -144,7 +106,8 @@ fn lazy_expand(
|
||||||
def: &MacroDefId,
|
def: &MacroDefId,
|
||||||
macro_call: InFile<ast::MacroCall>,
|
macro_call: InFile<ast::MacroCall>,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
) -> ExpandResult<(InFile<Parse<SyntaxNode>>, Arc<SpanMap>)> {
|
call_site: SyntaxContextId,
|
||||||
|
) -> ExpandResult<(InFile<Parse<SyntaxNode>>, Arc<ExpansionSpanMap>)> {
|
||||||
let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value);
|
let ast_id = db.ast_id_map(macro_call.file_id).ast_id(¯o_call.value);
|
||||||
|
|
||||||
let expand_to = ExpandTo::from_call_site(¯o_call.value);
|
let expand_to = ExpandTo::from_call_site(¯o_call.value);
|
||||||
|
@ -153,8 +116,8 @@ fn lazy_expand(
|
||||||
db,
|
db,
|
||||||
krate,
|
krate,
|
||||||
MacroCallKind::FnLike { ast_id, expand_to },
|
MacroCallKind::FnLike { ast_id, expand_to },
|
||||||
// FIXME
|
// FIXME: This is wrong
|
||||||
SyntaxContextId::ROOT,
|
call_site,
|
||||||
);
|
);
|
||||||
let macro_file = id.as_macro_file();
|
let macro_file = id.as_macro_file();
|
||||||
|
|
||||||
|
@ -164,9 +127,10 @@ fn lazy_expand(
|
||||||
|
|
||||||
fn eager_macro_recur(
|
fn eager_macro_recur(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
hygiene: &SpanMap,
|
hygiene: &ExpansionSpanMap,
|
||||||
curr: InFile<SyntaxNode>,
|
curr: InFile<SyntaxNode>,
|
||||||
krate: CrateId,
|
krate: CrateId,
|
||||||
|
call_site: SyntaxContextId,
|
||||||
macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
|
macro_resolver: &dyn Fn(ModPath) -> Option<MacroDefId>,
|
||||||
) -> ExpandResult<Option<(SyntaxNode, FxHashMap<TextRange, TextRange>)>> {
|
) -> ExpandResult<Option<(SyntaxNode, FxHashMap<TextRange, TextRange>)>> {
|
||||||
let original = curr.value.clone_for_update();
|
let original = curr.value.clone_for_update();
|
||||||
|
@ -204,7 +168,10 @@ fn eager_macro_recur(
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
let def = match call.path().and_then(|path| ModPath::from_src(db, path, hygiene)) {
|
let def = match call
|
||||||
|
.path()
|
||||||
|
.and_then(|path| ModPath::from_src(db, path, SpanMapRef::ExpansionSpanMap(hygiene)))
|
||||||
|
{
|
||||||
Some(path) => match macro_resolver(path.clone()) {
|
Some(path) => match macro_resolver(path.clone()) {
|
||||||
Some(def) => def,
|
Some(def) => def,
|
||||||
None => {
|
None => {
|
||||||
|
@ -225,6 +192,8 @@ fn eager_macro_recur(
|
||||||
krate,
|
krate,
|
||||||
curr.with_value(call.clone()),
|
curr.with_value(call.clone()),
|
||||||
def,
|
def,
|
||||||
|
// FIXME: This call site is not quite right I think? We probably need to mark it?
|
||||||
|
call_site,
|
||||||
macro_resolver,
|
macro_resolver,
|
||||||
);
|
);
|
||||||
match value {
|
match value {
|
||||||
|
@ -260,7 +229,7 @@ fn eager_macro_recur(
|
||||||
| MacroDefKind::BuiltInDerive(..)
|
| MacroDefKind::BuiltInDerive(..)
|
||||||
| MacroDefKind::ProcMacro(..) => {
|
| MacroDefKind::ProcMacro(..) => {
|
||||||
let ExpandResult { value: (parse, tm), err } =
|
let ExpandResult { value: (parse, tm), err } =
|
||||||
lazy_expand(db, &def, curr.with_value(call.clone()), krate);
|
lazy_expand(db, &def, curr.with_value(call.clone()), krate, call_site);
|
||||||
|
|
||||||
// replace macro inside
|
// replace macro inside
|
||||||
let ExpandResult { value, err: error } = eager_macro_recur(
|
let ExpandResult { value, err: error } = eager_macro_recur(
|
||||||
|
@ -269,6 +238,7 @@ fn eager_macro_recur(
|
||||||
// FIXME: We discard parse errors here
|
// FIXME: We discard parse errors here
|
||||||
parse.as_ref().map(|it| it.syntax_node()),
|
parse.as_ref().map(|it| it.syntax_node()),
|
||||||
krate,
|
krate,
|
||||||
|
call_site,
|
||||||
macro_resolver,
|
macro_resolver,
|
||||||
);
|
);
|
||||||
let err = err.or(error);
|
let err = err.or(error);
|
||||||
|
|
293
crates/hir-expand/src/files.rs
Normal file
293
crates/hir-expand/src/files.rs
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
use std::iter;
|
||||||
|
|
||||||
|
use base_db::{
|
||||||
|
span::{HirFileId, HirFileIdRepr, MacroFile, SyntaxContextId},
|
||||||
|
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:
|
||||||
|
///
|
||||||
|
/// * `InFile<SyntaxNode>` -- syntax node in a file
|
||||||
|
/// * `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 value: T,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> InFile<T> {
|
||||||
|
pub fn new(file_id: HirFileId, value: T) -> InFile<T> {
|
||||||
|
InFile { 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) -> InFile<U> {
|
||||||
|
InFile::new(self.file_id, f(self.value))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_ref(&self) -> InFile<&T> {
|
||||||
|
self.with_value(&self.value)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
|
||||||
|
db.parse_or_expand(self.file_id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T: Clone> InFile<&T> {
|
||||||
|
pub fn cloned(&self) -> InFile<T> {
|
||||||
|
self.with_value(self.value.clone())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T> InFile<Option<T>> {
|
||||||
|
pub fn transpose(self) -> Option<InFile<T>> {
|
||||||
|
let value = self.value?;
|
||||||
|
Some(InFile::new(self.file_id, value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<L, R> InFile<Either<L, R>> {
|
||||||
|
pub fn transpose(self) -> Either<InFile<L>, InFile<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)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
db: &dyn db::ExpandDatabase,
|
||||||
|
) -> impl Iterator<Item = 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) {
|
||||||
|
// 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()
|
||||||
|
} else {
|
||||||
|
Some(parent_node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
iter::successors(succ(&self.cloned()), succ)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Falls back to the macro call range if the node cannot be mapped up fully.
|
||||||
|
///
|
||||||
|
/// 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)) =
|
||||||
|
ExpansionInfo::new(db, mac_file).map_node_range_up(db, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Falls back to the macro call range if the node cannot be mapped up fully.
|
||||||
|
pub fn original_file_range_full(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)) =
|
||||||
|
ExpansionInfo::new(db, mac_file).map_node_range_up(db, 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to map the syntax node back up its macro calls.
|
||||||
|
pub fn original_file_range_opt(
|
||||||
|
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) => {
|
||||||
|
ExpansionInfo::new(db, mac_file).map_node_range_up(db, self.value.text_range())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn original_syntax_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<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));
|
||||||
|
};
|
||||||
|
if !self.file_id.is_attr_macro(db) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (FileRange { file_id, range }, ctx) =
|
||||||
|
ExpansionInfo::new(db, file_id).map_node_range_up(db, 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 !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)?;
|
||||||
|
Some(InFile::new(file_id.into(), value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InFile<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()))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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) => {
|
||||||
|
if let Some(res) = self.original_file_range_opt(db) {
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to map the syntax node back up its macro calls.
|
||||||
|
pub fn original_file_range_opt(self, db: &dyn db::ExpandDatabase) -> Option<FileRange> {
|
||||||
|
match self.file_id.repr() {
|
||||||
|
HirFileIdRepr::FileId(file_id) => {
|
||||||
|
Some(FileRange { file_id, range: self.value.text_range() })
|
||||||
|
}
|
||||||
|
HirFileIdRepr::MacroFile(_) => {
|
||||||
|
let (range, ctxt) = ascend_range_up_macros(db, self.map(|it| it.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() {
|
||||||
|
Some(range)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[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<N: AstNode> InFile<N> {
|
||||||
|
pub fn descendants<T: AstNode>(self) -> impl Iterator<Item = InFile<T>> {
|
||||||
|
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>> {
|
||||||
|
// 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);
|
||||||
|
};
|
||||||
|
if !self.file_id.is_attr_macro(db) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
let (FileRange { file_id, range }, ctx) = ExpansionInfo::new(db, file_id)
|
||||||
|
.map_node_range_up(db, 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)?;
|
||||||
|
return Some(InFile::new(file_id.into(), value));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn syntax(&self) -> InFile<&SyntaxNode> {
|
||||||
|
self.with_value(self.value.syntax())
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,7 +2,9 @@
|
||||||
//!
|
//!
|
||||||
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
|
//! Specifically, `ast` + `Hygiene` allows you to create a `Name`. Note that, at
|
||||||
//! this moment, this is horribly incomplete and handles only `$crate`.
|
//! this moment, this is horribly incomplete and handles only `$crate`.
|
||||||
use base_db::span::{MacroCallId, SyntaxContextId};
|
use std::iter;
|
||||||
|
|
||||||
|
use base_db::span::{MacroCallId, SpanData, SyntaxContextId};
|
||||||
|
|
||||||
use crate::db::ExpandDatabase;
|
use crate::db::ExpandDatabase;
|
||||||
|
|
||||||
|
@ -48,6 +50,39 @@ pub enum Transparency {
|
||||||
Opaque,
|
Opaque,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn span_with_def_site_ctxt(
|
||||||
|
db: &dyn ExpandDatabase,
|
||||||
|
span: SpanData,
|
||||||
|
expn_id: MacroCallId,
|
||||||
|
) -> SpanData {
|
||||||
|
span_with_ctxt_from_mark(db, span, expn_id, Transparency::Opaque)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn span_with_call_site_ctxt(
|
||||||
|
db: &dyn ExpandDatabase,
|
||||||
|
span: SpanData,
|
||||||
|
expn_id: MacroCallId,
|
||||||
|
) -> SpanData {
|
||||||
|
span_with_ctxt_from_mark(db, span, expn_id, Transparency::Transparent)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn span_with_mixed_site_ctxt(
|
||||||
|
db: &dyn ExpandDatabase,
|
||||||
|
span: SpanData,
|
||||||
|
expn_id: MacroCallId,
|
||||||
|
) -> SpanData {
|
||||||
|
span_with_ctxt_from_mark(db, span, expn_id, Transparency::SemiTransparent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn span_with_ctxt_from_mark(
|
||||||
|
db: &dyn ExpandDatabase,
|
||||||
|
span: SpanData,
|
||||||
|
expn_id: MacroCallId,
|
||||||
|
transparency: Transparency,
|
||||||
|
) -> SpanData {
|
||||||
|
SpanData { ctx: db.apply_mark(SyntaxContextId::ROOT, expn_id, transparency), ..span }
|
||||||
|
}
|
||||||
|
|
||||||
pub(super) fn apply_mark(
|
pub(super) fn apply_mark(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
ctxt: SyntaxContextId,
|
ctxt: SyntaxContextId,
|
||||||
|
@ -65,7 +100,7 @@ pub(super) fn apply_mark(
|
||||||
call_site_ctxt.normalize_to_macro_rules(db)
|
call_site_ctxt.normalize_to_macro_rules(db)
|
||||||
};
|
};
|
||||||
|
|
||||||
if call_site_ctxt.is_root(db) {
|
if call_site_ctxt.is_root() {
|
||||||
return apply_mark_internal(db, ctxt, Some(call_id), transparency);
|
return apply_mark_internal(db, ctxt, Some(call_id), transparency);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,7 +166,6 @@ fn apply_mark_internal(
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
pub trait SyntaxContextExt {
|
pub trait SyntaxContextExt {
|
||||||
fn is_root(self, db: &dyn ExpandDatabase) -> bool;
|
|
||||||
fn normalize_to_macro_rules(self, db: &dyn ExpandDatabase) -> Self;
|
fn normalize_to_macro_rules(self, db: &dyn ExpandDatabase) -> Self;
|
||||||
fn normalize_to_macros_2_0(self, db: &dyn ExpandDatabase) -> Self;
|
fn normalize_to_macros_2_0(self, db: &dyn ExpandDatabase) -> Self;
|
||||||
fn parent_ctxt(self, db: &dyn ExpandDatabase) -> Self;
|
fn parent_ctxt(self, db: &dyn ExpandDatabase) -> Self;
|
||||||
|
@ -148,9 +182,6 @@ fn handle_self_ref(p: SyntaxContextId, n: SyntaxContextId) -> SyntaxContextId {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SyntaxContextExt for SyntaxContextId {
|
impl SyntaxContextExt for SyntaxContextId {
|
||||||
fn is_root(self, db: &dyn ExpandDatabase) -> bool {
|
|
||||||
db.lookup_intern_syntax_context(self).outer_expn.is_none()
|
|
||||||
}
|
|
||||||
fn normalize_to_macro_rules(self, db: &dyn ExpandDatabase) -> Self {
|
fn normalize_to_macro_rules(self, db: &dyn ExpandDatabase) -> Self {
|
||||||
handle_self_ref(self, db.lookup_intern_syntax_context(self).opaque_and_semitransparent)
|
handle_self_ref(self, db.lookup_intern_syntax_context(self).opaque_and_semitransparent)
|
||||||
}
|
}
|
||||||
|
@ -164,20 +195,20 @@ impl SyntaxContextExt for SyntaxContextId {
|
||||||
let data = db.lookup_intern_syntax_context(self);
|
let data = db.lookup_intern_syntax_context(self);
|
||||||
(data.outer_expn, data.outer_transparency)
|
(data.outer_expn, data.outer_transparency)
|
||||||
}
|
}
|
||||||
fn marks(mut self, db: &dyn ExpandDatabase) -> Vec<(Option<MacroCallId>, Transparency)> {
|
fn marks(self, db: &dyn ExpandDatabase) -> Vec<(Option<MacroCallId>, Transparency)> {
|
||||||
let mut marks = Vec::new();
|
let mut marks = marks_rev(self, db).collect::<Vec<_>>();
|
||||||
while self != SyntaxContextId::ROOT {
|
|
||||||
marks.push(self.outer_mark(db));
|
|
||||||
self = self.parent_ctxt(db);
|
|
||||||
}
|
|
||||||
marks.reverse();
|
marks.reverse();
|
||||||
marks
|
marks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub(super) fn with_ctxt_from_mark(db: &ExpandDatabase, file_id: HirFileId) {
|
// FIXME: Make this a SyntaxContextExt method once we have RPIT
|
||||||
// self.with_ctxt_from_mark(expn_id, Transparency::Transparent)
|
pub fn marks_rev(
|
||||||
// }
|
ctxt: SyntaxContextId,
|
||||||
// pub(super) fn with_call_site_ctxt(db: &ExpandDatabase, file_id: HirFileId) {
|
db: &dyn ExpandDatabase,
|
||||||
// self.with_ctxt_from_mark(expn_id, Transparency::Transparent)
|
) -> impl Iterator<Item = (Option<MacroCallId>, Transparency)> + '_ {
|
||||||
// }
|
iter::successors(Some(ctxt), move |&mark| {
|
||||||
|
Some(mark.parent_ctxt(db)).filter(|&it| it != SyntaxContextId::ROOT)
|
||||||
|
})
|
||||||
|
.map(|ctx| ctx.outer_mark(db))
|
||||||
|
}
|
||||||
|
|
|
@ -18,25 +18,25 @@ pub mod quote;
|
||||||
pub mod eager;
|
pub mod eager;
|
||||||
pub mod mod_path;
|
pub mod mod_path;
|
||||||
pub mod attrs;
|
pub mod attrs;
|
||||||
|
pub mod span;
|
||||||
|
pub mod files;
|
||||||
// mod fixup;
|
// mod fixup;
|
||||||
|
|
||||||
use triomphe::Arc;
|
use triomphe::Arc;
|
||||||
|
|
||||||
use std::{fmt, hash::Hash, iter};
|
use std::{fmt, hash::Hash};
|
||||||
|
|
||||||
use base_db::{
|
use base_db::{
|
||||||
span::{HirFileIdRepr, SyntaxContextId},
|
span::{HirFileIdRepr, SpanData, SyntaxContextId},
|
||||||
CrateId, FileId, FileRange, ProcMacroKind,
|
CrateId, FileId, FileRange, ProcMacroKind,
|
||||||
};
|
};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
algo::{self, skip_trivia_token},
|
|
||||||
ast::{self, AstNode, HasDocComments},
|
ast::{self, AstNode, HasDocComments},
|
||||||
AstPtr, Direction, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextSize,
|
SyntaxNode, SyntaxToken, TextRange, TextSize,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
ast_id_map::{AstIdNode, ErasedFileAstId, FileAstId},
|
|
||||||
attrs::AttrId,
|
attrs::AttrId,
|
||||||
builtin_attr_macro::BuiltinAttrExpander,
|
builtin_attr_macro::BuiltinAttrExpander,
|
||||||
builtin_derive_macro::BuiltinDeriveExpander,
|
builtin_derive_macro::BuiltinDeriveExpander,
|
||||||
|
@ -44,12 +44,15 @@ use crate::{
|
||||||
db::TokenExpander,
|
db::TokenExpander,
|
||||||
mod_path::ModPath,
|
mod_path::ModPath,
|
||||||
proc_macro::ProcMacroExpander,
|
proc_macro::ProcMacroExpander,
|
||||||
|
span::ExpansionSpanMap,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
pub use crate::ast_id_map::{AstId, ErasedAstId, ErasedFileAstId};
|
||||||
|
pub use crate::files::{InFile, InMacroFile};
|
||||||
|
|
||||||
pub use base_db::span::{HirFileId, MacroCallId, MacroFile};
|
pub use base_db::span::{HirFileId, MacroCallId, MacroFile};
|
||||||
pub use mbe::ValueResult;
|
pub use mbe::ValueResult;
|
||||||
|
|
||||||
pub type SpanMap = ::mbe::TokenMap<tt::SpanData>;
|
|
||||||
pub type DeclarativeMacro = ::mbe::DeclarativeMacro<tt::SpanData>;
|
pub type DeclarativeMacro = ::mbe::DeclarativeMacro<tt::SpanData>;
|
||||||
|
|
||||||
pub mod tt {
|
pub mod tt {
|
||||||
|
@ -103,7 +106,7 @@ impl fmt::Display for ExpandError {
|
||||||
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct MacroCallLoc {
|
pub struct MacroCallLoc {
|
||||||
pub def: MacroDefId,
|
pub def: MacroDefId,
|
||||||
pub(crate) krate: CrateId,
|
pub krate: CrateId,
|
||||||
/// Some if this is a macro call for an eager macro. Note that this is `None`
|
/// Some if this is a macro call for an eager macro. Note that this is `None`
|
||||||
/// for the eager input macro file.
|
/// for the eager input macro file.
|
||||||
eager: Option<Box<EagerCallInfo>>,
|
eager: Option<Box<EagerCallInfo>>,
|
||||||
|
@ -247,8 +250,7 @@ impl HirFileIdExt for HirFileId {
|
||||||
|
|
||||||
/// Return expansion information if it is a macro-expansion file
|
/// Return expansion information if it is a macro-expansion file
|
||||||
fn expansion_info(self, db: &dyn db::ExpandDatabase) -> Option<ExpansionInfo> {
|
fn expansion_info(self, db: &dyn db::ExpandDatabase) -> Option<ExpansionInfo> {
|
||||||
let macro_file = self.macro_file()?;
|
Some(ExpansionInfo::new(db, self.macro_file()?))
|
||||||
ExpansionInfo::new(db, macro_file)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn as_builtin_derive_attr_node(
|
fn as_builtin_derive_attr_node(
|
||||||
|
@ -340,15 +342,14 @@ impl MacroDefId {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ast_id(&self) -> Either<AstId<ast::Macro>, AstId<ast::Fn>> {
|
pub fn ast_id(&self) -> Either<AstId<ast::Macro>, AstId<ast::Fn>> {
|
||||||
let id = match self.kind {
|
match self.kind {
|
||||||
MacroDefKind::ProcMacro(.., id) => return Either::Right(id),
|
MacroDefKind::ProcMacro(.., id) => return Either::Right(id),
|
||||||
MacroDefKind::Declarative(id)
|
MacroDefKind::Declarative(id)
|
||||||
| MacroDefKind::BuiltIn(_, id)
|
| MacroDefKind::BuiltIn(_, id)
|
||||||
| MacroDefKind::BuiltInAttr(_, id)
|
| MacroDefKind::BuiltInAttr(_, id)
|
||||||
| MacroDefKind::BuiltInDerive(_, id)
|
| MacroDefKind::BuiltInDerive(_, id)
|
||||||
| MacroDefKind::BuiltInEager(_, id) => id,
|
| MacroDefKind::BuiltInEager(_, id) => Either::Left(id),
|
||||||
};
|
}
|
||||||
Either::Left(id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_proc_macro(&self) -> bool {
|
pub fn is_proc_macro(&self) -> bool {
|
||||||
|
@ -390,6 +391,18 @@ impl MacroDefId {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MacroCallLoc {
|
impl MacroCallLoc {
|
||||||
|
pub fn span(&self, db: &dyn db::ExpandDatabase) -> SpanData {
|
||||||
|
let ast_id = self.kind.erased_ast_id();
|
||||||
|
let file_id = self.kind.file_id();
|
||||||
|
let range = db.ast_id_map(file_id).get_erased(ast_id).text_range();
|
||||||
|
match file_id.repr() {
|
||||||
|
HirFileIdRepr::FileId(file_id) => db.real_span_map(file_id).span_for_range(range),
|
||||||
|
HirFileIdRepr::MacroFile(m) => {
|
||||||
|
db.parse_macro_expansion(m).value.1.span_for_range(range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> InFile<SyntaxNode> {
|
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> InFile<SyntaxNode> {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
MacroCallKind::FnLike { ast_id, .. } => {
|
MacroCallKind::FnLike { ast_id, .. } => {
|
||||||
|
@ -430,17 +443,15 @@ impl MacroCallLoc {
|
||||||
match self.kind {
|
match self.kind {
|
||||||
MacroCallKind::FnLike { expand_to, .. } => expand_to,
|
MacroCallKind::FnLike { expand_to, .. } => expand_to,
|
||||||
MacroCallKind::Derive { .. } => ExpandTo::Items,
|
MacroCallKind::Derive { .. } => ExpandTo::Items,
|
||||||
MacroCallKind::Attr { .. } if self.def.is_attribute_derive() => ExpandTo::Statements,
|
MacroCallKind::Attr { .. } if self.def.is_attribute_derive() => ExpandTo::Items,
|
||||||
MacroCallKind::Attr { .. } => {
|
MacroCallKind::Attr { .. } => {
|
||||||
// is this always correct?
|
// FIXME(stmt_expr_attributes)
|
||||||
ExpandTo::Items
|
ExpandTo::Items
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: attribute indices do not account for nested `cfg_attr`
|
|
||||||
|
|
||||||
impl MacroCallKind {
|
impl MacroCallKind {
|
||||||
/// Returns the file containing the macro invocation.
|
/// Returns the file containing the macro invocation.
|
||||||
fn file_id(&self) -> HirFileId {
|
fn file_id(&self) -> HirFileId {
|
||||||
|
@ -451,6 +462,14 @@ impl MacroCallKind {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn erased_ast_id(&self) -> ErasedFileAstId {
|
||||||
|
match *self {
|
||||||
|
MacroCallKind::FnLike { ast_id: InFile { value, .. }, .. } => value.erase(),
|
||||||
|
MacroCallKind::Derive { ast_id: InFile { value, .. }, .. } => value.erase(),
|
||||||
|
MacroCallKind::Attr { ast_id: InFile { value, .. }, .. } => value.erase(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Returns the original file range that best describes the location of this macro call.
|
/// Returns the original file range that best describes the location of this macro call.
|
||||||
///
|
///
|
||||||
/// Unlike `MacroCallKind::original_call_range`, this also spans the item of attributes and derives.
|
/// Unlike `MacroCallKind::original_call_range`, this also spans the item of attributes and derives.
|
||||||
|
@ -518,34 +537,40 @@ impl MacroCallKind {
|
||||||
FileRange { range, file_id }
|
FileRange { range, file_id }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn arg(&self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxNode>> {
|
// FIXME: -> InFile<SyntaxNode> it should be impossible for the token tree to be missing at
|
||||||
|
// this point!
|
||||||
|
fn arg(&self, db: &dyn db::ExpandDatabase) -> InFile<Option<SyntaxNode>> {
|
||||||
match self {
|
match self {
|
||||||
MacroCallKind::FnLike { ast_id, .. } => ast_id
|
MacroCallKind::FnLike { ast_id, .. } => {
|
||||||
.to_in_file_node(db)
|
ast_id.to_in_file_node(db).map(|it| Some(it.token_tree()?.syntax().clone()))
|
||||||
.map(|it| Some(it.token_tree()?.syntax().clone()))
|
}
|
||||||
.transpose(),
|
|
||||||
MacroCallKind::Derive { ast_id, .. } => {
|
MacroCallKind::Derive { ast_id, .. } => {
|
||||||
Some(ast_id.to_in_file_node(db).syntax().cloned())
|
ast_id.to_in_file_node(db).syntax().cloned().map(Some)
|
||||||
}
|
}
|
||||||
MacroCallKind::Attr { ast_id, .. } => {
|
MacroCallKind::Attr { ast_id, .. } => {
|
||||||
Some(ast_id.to_in_file_node(db).syntax().cloned())
|
ast_id.to_in_file_node(db).syntax().cloned().map(Some)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// ExpansionInfo mainly describes how to map text range between src and expanded macro
|
/// ExpansionInfo mainly describes how to map text range between src and expanded macro
|
||||||
|
// FIXME: can be expensive to create, we should check the use sites and maybe replace them with
|
||||||
|
// simpler function calls if the map is only used once
|
||||||
#[derive(Debug, Clone, PartialEq, Eq)]
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub struct ExpansionInfo {
|
pub struct ExpansionInfo {
|
||||||
expanded: InMacroFile<SyntaxNode>,
|
pub expanded: InMacroFile<SyntaxNode>,
|
||||||
/// The argument TokenTree or item for attributes
|
/// The argument TokenTree or item for attributes
|
||||||
arg: InFile<SyntaxNode>,
|
// FIXME: Can this ever be `None`?
|
||||||
|
arg: InFile<Option<SyntaxNode>>,
|
||||||
/// The `macro_rules!` or attribute input.
|
/// The `macro_rules!` or attribute input.
|
||||||
attr_input_or_mac_def: Option<InFile<ast::TokenTree>>,
|
attr_input_or_mac_def: Option<InFile<ast::TokenTree>>,
|
||||||
|
|
||||||
macro_def: TokenExpander,
|
macro_def: TokenExpander,
|
||||||
macro_arg: Arc<tt::Subtree>,
|
macro_arg: Arc<tt::Subtree>,
|
||||||
exp_map: Arc<SpanMap>,
|
exp_map: Arc<ExpansionSpanMap>,
|
||||||
|
/// [`None`] if the call is in a real file
|
||||||
|
arg_map: Option<Arc<ExpansionSpanMap>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ExpansionInfo {
|
impl ExpansionInfo {
|
||||||
|
@ -554,81 +579,133 @@ impl ExpansionInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn call_node(&self) -> Option<InFile<SyntaxNode>> {
|
pub fn call_node(&self) -> Option<InFile<SyntaxNode>> {
|
||||||
Some(self.arg.with_value(self.arg.value.parent()?))
|
Some(self.arg.with_value(self.arg.value.as_ref()?.parent()?))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map a token down from macro input into the macro expansion.
|
/// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
|
||||||
///
|
pub fn map_range_down<'a>(
|
||||||
/// The inner workings of this function differ slightly depending on the type of macro we are dealing with:
|
&'a self,
|
||||||
/// - declarative:
|
db: &'a dyn db::ExpandDatabase,
|
||||||
/// For declarative macros, we need to accommodate for the macro definition site(which acts as a second unchanging input)
|
FileRange { file_id, range: absolute_range }: FileRange,
|
||||||
/// , as tokens can mapped in and out of it.
|
|
||||||
/// To do this we shift all ids in the expansion by the maximum id of the definition site giving us an easy
|
|
||||||
/// way to map all the tokens.
|
|
||||||
/// - attribute:
|
|
||||||
/// Attributes have two different inputs, the input tokentree in the attribute node and the item
|
|
||||||
/// the attribute is annotating. Similarly as for declarative macros we need to do a shift here
|
|
||||||
/// as well. Currently this is done by shifting the attribute input by the maximum id of the item.
|
|
||||||
/// - function-like and derives:
|
|
||||||
/// Both of these only have one simple call site input so no special handling is required here.
|
|
||||||
pub fn map_token_down(
|
|
||||||
&self,
|
|
||||||
db: &dyn db::ExpandDatabase,
|
|
||||||
token: InFile<&SyntaxToken>,
|
|
||||||
// FIXME: use this for range mapping, so that we can resolve inline format args
|
// FIXME: use this for range mapping, so that we can resolve inline format args
|
||||||
_relative_token_offset: Option<TextSize>,
|
_relative_token_offset: Option<TextSize>,
|
||||||
) -> Option<impl Iterator<Item = InFile<SyntaxToken>> + '_> {
|
// FIXME: ret ty should be wrapped in InMacroFile
|
||||||
assert_eq!(token.file_id, self.arg.file_id);
|
) -> Option<impl Iterator<Item = InFile<SyntaxToken>> + 'a> {
|
||||||
|
// search for all entries in the span map that have the given span and return the
|
||||||
|
// corresponding text ranges inside the expansion
|
||||||
|
// FIXME: Make this proper
|
||||||
let span_map = &self.exp_map.span_map;
|
let span_map = &self.exp_map.span_map;
|
||||||
let (start, end) = if span_map
|
let (start, end) = if span_map
|
||||||
.first()
|
.first()
|
||||||
.map_or(false, |(_, span)| span.anchor.file_id == token.file_id)
|
.map_or(false, |(_, span)| span.anchor.file_id == file_id)
|
||||||
{
|
{
|
||||||
(0, span_map.partition_point(|a| a.1.anchor.file_id == token.file_id))
|
(0, span_map.partition_point(|a| a.1.anchor.file_id == file_id))
|
||||||
} else {
|
} else {
|
||||||
let start = span_map.partition_point(|a| a.1.anchor.file_id != token.file_id);
|
let start = span_map.partition_point(|a| a.1.anchor.file_id != file_id);
|
||||||
(
|
(start, start + span_map[start..].partition_point(|a| a.1.anchor.file_id == file_id))
|
||||||
start,
|
|
||||||
start + span_map[start..].partition_point(|a| a.1.anchor.file_id == token.file_id),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
let token_text_range = token.value.text_range();
|
|
||||||
let ast_id_map = db.ast_id_map(token.file_id);
|
|
||||||
let tokens = span_map[start..end]
|
let tokens = span_map[start..end]
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(move |(range, span)| {
|
.filter_map(move |(range, span)| {
|
||||||
let offset = ast_id_map.get_raw(span.anchor.ast_id).text_range().start();
|
// we need to resolve the relative ranges here to make sure that we are in fact
|
||||||
|
// considering differently anchored spans (this might occur with proc-macros)
|
||||||
|
let offset = db
|
||||||
|
.ast_id_map(span.anchor.file_id.into())
|
||||||
|
.get_erased(span.anchor.ast_id)
|
||||||
|
.text_range()
|
||||||
|
.start();
|
||||||
let abs_range = span.range + offset;
|
let abs_range = span.range + offset;
|
||||||
token_text_range.eq(&abs_range).then_some(*range)
|
absolute_range.eq(&abs_range).then_some(*range)
|
||||||
})
|
})
|
||||||
.flat_map(move |range| self.expanded.value.covering_element(range).into_token());
|
.flat_map(move |range| self.expanded.value.covering_element(range).into_token());
|
||||||
|
|
||||||
Some(tokens.map(move |token| InFile::new(self.expanded.file_id.into(), token)))
|
Some(tokens.map(move |token| InFile::new(self.expanded.file_id.into(), token)))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Map a token up out of the expansion it resides in into the arguments of the macro call of the expansion.
|
/// Maps up the text range out of the expansion hierarchy back into the original file its from.
|
||||||
pub fn map_token_up(
|
pub fn map_token_range_up(
|
||||||
&self,
|
&self,
|
||||||
db: &dyn db::ExpandDatabase,
|
db: &dyn db::ExpandDatabase,
|
||||||
token: InFile<&SyntaxToken>,
|
range: TextRange,
|
||||||
) -> Option<InFile<SyntaxToken>> {
|
) -> (FileRange, SyntaxContextId) {
|
||||||
self.exp_map.span_for_range(token.value.text_range()).and_then(|span| {
|
debug_assert!(self.expanded.value.text_range().contains_range(range));
|
||||||
let anchor =
|
let span = self.exp_map.span_for_range(range);
|
||||||
db.ast_id_map(span.anchor.file_id).get_raw(span.anchor.ast_id).text_range().start();
|
let anchor_offset = db
|
||||||
InFile::new(
|
.ast_id_map(span.anchor.file_id.into())
|
||||||
span.anchor.file_id,
|
.get_erased(span.anchor.ast_id)
|
||||||
db.parse_or_expand(span.anchor.file_id)
|
.text_range()
|
||||||
.covering_element(span.range + anchor)
|
.start();
|
||||||
.into_token(),
|
(FileRange { file_id: span.anchor.file_id, range: span.range + anchor_offset }, span.ctx)
|
||||||
)
|
|
||||||
.transpose()
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new(db: &dyn db::ExpandDatabase, macro_file: MacroFile) -> Option<ExpansionInfo> {
|
/// Maps up the text range out of the expansion hierarchy back into the original file its from.
|
||||||
|
pub fn map_node_range_up(
|
||||||
|
&self,
|
||||||
|
db: &dyn db::ExpandDatabase,
|
||||||
|
range: TextRange,
|
||||||
|
) -> Option<(FileRange, SyntaxContextId)> {
|
||||||
|
debug_assert!(self.expanded.value.text_range().contains_range(range));
|
||||||
|
let mut spans = self.exp_map.spans_for_node_range(range);
|
||||||
|
let SpanData { range, anchor, ctx } = spans.next()?;
|
||||||
|
let mut start = range.start();
|
||||||
|
let mut end = range.end();
|
||||||
|
|
||||||
|
for span in spans {
|
||||||
|
if span.anchor != anchor || span.ctx != ctx {
|
||||||
|
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,
|
||||||
|
},
|
||||||
|
ctx,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Maps up the text range out of the expansion into is macro call.
|
||||||
|
pub fn map_range_up_once(
|
||||||
|
&self,
|
||||||
|
db: &dyn db::ExpandDatabase,
|
||||||
|
token: TextRange,
|
||||||
|
) -> InFile<smallvec::SmallVec<[TextRange; 1]>> {
|
||||||
|
debug_assert!(self.expanded.value.text_range().contains_range(token));
|
||||||
|
let span = self.exp_map.span_for_range(token);
|
||||||
|
match &self.arg_map {
|
||||||
|
None => {
|
||||||
|
let file_id = span.anchor.file_id.into();
|
||||||
|
let anchor_offset =
|
||||||
|
db.ast_id_map(file_id).get_erased(span.anchor.ast_id).text_range().start();
|
||||||
|
InFile { file_id, value: smallvec::smallvec![span.range + anchor_offset] }
|
||||||
|
}
|
||||||
|
Some(arg_map) => {
|
||||||
|
let arg_range = self
|
||||||
|
.arg
|
||||||
|
.value
|
||||||
|
.as_ref()
|
||||||
|
.map_or_else(|| TextRange::empty(TextSize::from(0)), |it| it.text_range());
|
||||||
|
InFile::new(
|
||||||
|
self.arg.file_id,
|
||||||
|
arg_map
|
||||||
|
.ranges_with_span(span)
|
||||||
|
.filter(|range| range.intersect(arg_range).is_some())
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(db: &dyn db::ExpandDatabase, macro_file: MacroFile) -> ExpansionInfo {
|
||||||
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
let loc: MacroCallLoc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||||
|
|
||||||
let arg_tt = loc.kind.arg(db)?;
|
let arg_tt = loc.kind.arg(db);
|
||||||
|
let arg_map =
|
||||||
|
arg_tt.file_id.macro_file().map(|file| db.parse_macro_expansion(file).value.1);
|
||||||
|
|
||||||
let macro_def = db.macro_expander(loc.def);
|
let macro_def = db.macro_expander(loc.def);
|
||||||
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
|
let (parse, exp_map) = db.parse_macro_expansion(macro_file).value;
|
||||||
|
@ -662,329 +739,16 @@ impl ExpansionInfo {
|
||||||
_ => None,
|
_ => None,
|
||||||
});
|
});
|
||||||
|
|
||||||
Some(ExpansionInfo {
|
ExpansionInfo {
|
||||||
expanded,
|
expanded,
|
||||||
arg: arg_tt,
|
arg: arg_tt,
|
||||||
attr_input_or_mac_def,
|
attr_input_or_mac_def,
|
||||||
macro_arg,
|
macro_arg,
|
||||||
macro_def,
|
macro_def,
|
||||||
exp_map,
|
exp_map,
|
||||||
})
|
arg_map,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `AstId` points to an AST node in any file.
|
|
||||||
///
|
|
||||||
/// It is stable across reparses, and can be used as salsa key/value.
|
|
||||||
pub type AstId<N> = InFile<FileAstId<N>>;
|
|
||||||
|
|
||||||
impl<N: AstIdNode> AstId<N> {
|
|
||||||
pub fn to_node(&self, db: &dyn db::ExpandDatabase) -> N {
|
|
||||||
self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))
|
|
||||||
}
|
|
||||||
pub fn to_in_file_node(&self, db: &dyn db::ExpandDatabase) -> InFile<N> {
|
|
||||||
InFile::new(self.file_id, self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id)))
|
|
||||||
}
|
|
||||||
pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> AstPtr<N> {
|
|
||||||
db.ast_id_map(self.file_id).get(self.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type ErasedAstId = InFile<ErasedFileAstId>;
|
|
||||||
|
|
||||||
impl ErasedAstId {
|
|
||||||
pub fn to_ptr(&self, db: &dyn db::ExpandDatabase) -> SyntaxNodePtr {
|
|
||||||
db.ast_id_map(self.file_id).get_raw(self.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// `InFile<T>` stores a value of `T` inside a particular file/syntax tree.
|
|
||||||
///
|
|
||||||
/// Typical usages are:
|
|
||||||
///
|
|
||||||
/// * `InFile<SyntaxNode>` -- syntax node in a file
|
|
||||||
/// * `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 value: T,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> InFile<T> {
|
|
||||||
pub fn new(file_id: HirFileId, value: T) -> InFile<T> {
|
|
||||||
InFile { 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) -> InFile<U> {
|
|
||||||
InFile::new(self.file_id, f(self.value))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn as_ref(&self) -> InFile<&T> {
|
|
||||||
self.with_value(&self.value)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn file_syntax(&self, db: &dyn db::ExpandDatabase) -> SyntaxNode {
|
|
||||||
db.parse_or_expand(self.file_id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T: Clone> InFile<&T> {
|
|
||||||
pub fn cloned(&self) -> InFile<T> {
|
|
||||||
self.with_value(self.value.clone())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<T> InFile<Option<T>> {
|
|
||||||
pub fn transpose(self) -> Option<InFile<T>> {
|
|
||||||
let value = self.value?;
|
|
||||||
Some(InFile::new(self.file_id, value))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<L, R> InFile<Either<L, R>> {
|
|
||||||
pub fn transpose(self) -> Either<InFile<L>, InFile<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)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
db: &dyn db::ExpandDatabase,
|
|
||||||
) -> impl Iterator<Item = 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) {
|
|
||||||
// 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()
|
|
||||||
} else {
|
|
||||||
Some(parent_node)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
iter::successors(succ(&self.cloned()), succ)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Falls back to the macro call range if the node cannot be mapped up fully.
|
|
||||||
///
|
|
||||||
/// 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) = self.original_file_range_opt(db) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Falls back to the macro call range if the node cannot be mapped up fully.
|
|
||||||
pub fn original_file_range_full(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) = self.original_file_range_opt(db) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to map the syntax node back up its macro calls.
|
|
||||||
pub fn original_file_range_opt(self, db: &dyn db::ExpandDatabase) -> Option<FileRange> {
|
|
||||||
match ascend_node_border_tokens(db, self) {
|
|
||||||
Some(InFile { file_id, value: (first, last) }) => {
|
|
||||||
let original_file = file_id.original_file(db);
|
|
||||||
let range = first.text_range().cover(last.text_range());
|
|
||||||
if file_id != original_file.into() {
|
|
||||||
tracing::error!("Failed mapping up more for {:?}", range);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(FileRange { file_id: original_file, range })
|
|
||||||
}
|
|
||||||
_ if !self.file_id.is_macro() => Some(FileRange {
|
|
||||||
file_id: self.file_id.original_file(db),
|
|
||||||
range: self.value.text_range(),
|
|
||||||
}),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn original_syntax_node(self, db: &dyn db::ExpandDatabase) -> Option<InFile<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
|
|
||||||
if !self.file_id.is_macro() {
|
|
||||||
return Some(self.map(Clone::clone));
|
|
||||||
} else if !self.file_id.is_attr_macro(db) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(InFile { file_id, value: (first, last) }) = ascend_node_border_tokens(db, self)
|
|
||||||
{
|
|
||||||
if file_id.is_macro() {
|
|
||||||
let range = first.text_range().cover(last.text_range());
|
|
||||||
tracing::error!("Failed mapping out of macro file for {:?}", range);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
// FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes
|
|
||||||
let anc = algo::least_common_ancestor(&first.parent()?, &last.parent()?)?;
|
|
||||||
let kind = self.value.kind();
|
|
||||||
let value = anc.ancestors().find(|it| it.kind() == kind)?;
|
|
||||||
return Some(InFile::new(file_id, value));
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl InFile<SyntaxToken> {
|
|
||||||
pub fn upmap(self, db: &dyn db::ExpandDatabase) -> Option<InFile<SyntaxToken>> {
|
|
||||||
let expansion = self.file_id.expansion_info(db)?;
|
|
||||||
expansion.map_token_up(db, self.as_ref())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// 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) => {
|
|
||||||
if let Some(res) = self.original_file_range_opt(db) {
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Attempts to map the syntax node back up its macro calls.
|
|
||||||
pub fn original_file_range_opt(self, db: &dyn db::ExpandDatabase) -> Option<FileRange> {
|
|
||||||
match self.file_id.repr() {
|
|
||||||
HirFileIdRepr::FileId(file_id) => {
|
|
||||||
Some(FileRange { file_id, range: self.value.text_range() })
|
|
||||||
}
|
|
||||||
HirFileIdRepr::MacroFile(_) => {
|
|
||||||
let expansion = self.file_id.expansion_info(db)?;
|
|
||||||
let InFile { file_id, value } = ascend_call_token(db, &expansion, self)?;
|
|
||||||
let original_file = file_id.original_file(db);
|
|
||||||
if file_id != original_file.into() {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(FileRange { file_id: original_file, range: value.text_range() })
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[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 }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Get rid of this
|
|
||||||
fn ascend_node_border_tokens(
|
|
||||||
db: &dyn db::ExpandDatabase,
|
|
||||||
InFile { file_id, value: node }: InFile<&SyntaxNode>,
|
|
||||||
) -> Option<InFile<(SyntaxToken, SyntaxToken)>> {
|
|
||||||
let expansion = file_id.expansion_info(db)?;
|
|
||||||
|
|
||||||
let first_token = |node: &SyntaxNode| skip_trivia_token(node.first_token()?, Direction::Next);
|
|
||||||
let last_token = |node: &SyntaxNode| skip_trivia_token(node.last_token()?, Direction::Prev);
|
|
||||||
|
|
||||||
// FIXME: Once the token map rewrite is done, this shouldnt need to rely on syntax nodes and tokens anymore
|
|
||||||
let first = first_token(node)?;
|
|
||||||
let last = last_token(node)?;
|
|
||||||
let first = ascend_call_token(db, &expansion, InFile::new(file_id, first))?;
|
|
||||||
let last = ascend_call_token(db, &expansion, InFile::new(file_id, last))?;
|
|
||||||
(first.file_id == last.file_id).then(|| InFile::new(first.file_id, (first.value, last.value)))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn ascend_call_token(
|
|
||||||
db: &dyn db::ExpandDatabase,
|
|
||||||
expansion: &ExpansionInfo,
|
|
||||||
token: InFile<SyntaxToken>,
|
|
||||||
) -> Option<InFile<SyntaxToken>> {
|
|
||||||
let mut mapping = expansion.map_token_up(db, token.as_ref())?;
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match mapping.file_id.expansion_info(db) {
|
|
||||||
Some(info) => mapping = info.map_token_up(db, mapping.as_ref())?,
|
|
||||||
None => return Some(mapping),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<N: AstNode> InFile<N> {
|
|
||||||
pub fn descendants<T: AstNode>(self) -> impl Iterator<Item = InFile<T>> {
|
|
||||||
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>> {
|
|
||||||
// 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
|
|
||||||
if !self.file_id.is_macro() {
|
|
||||||
return Some(self);
|
|
||||||
} else if !self.file_id.is_attr_macro(db) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(InFile { file_id, value: (first, last) }) =
|
|
||||||
ascend_node_border_tokens(db, self.syntax())
|
|
||||||
{
|
|
||||||
if file_id.is_macro() {
|
|
||||||
let range = first.text_range().cover(last.text_range());
|
|
||||||
tracing::error!("Failed mapping out of macro file for {:?}", range);
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
// FIXME: This heuristic is brittle and with the right macro may select completely unrelated nodes
|
|
||||||
let anc = algo::least_common_ancestor(&first.parent()?, &last.parent()?)?;
|
|
||||||
let value = anc.ancestors().find_map(N::cast)?;
|
|
||||||
return Some(InFile::new(file_id, value));
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn syntax(&self) -> InFile<&SyntaxNode> {
|
|
||||||
self.with_value(self.value.syntax())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// In Rust, macros expand token trees to token trees. When we want to turn a
|
/// In Rust, macros expand token trees to token trees. When we want to turn a
|
||||||
|
@ -1051,9 +815,4 @@ impl ExpandTo {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct UnresolvedMacro {
|
|
||||||
pub path: ModPath,
|
|
||||||
}
|
|
||||||
|
|
||||||
intern::impl_internable!(ModPath, attrs::AttrInput);
|
intern::impl_internable!(ModPath, attrs::AttrInput);
|
||||||
|
|
|
@ -7,9 +7,9 @@ use std::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
db::ExpandDatabase,
|
db::ExpandDatabase,
|
||||||
hygiene::{SyntaxContextExt, Transparency},
|
hygiene::{marks_rev, SyntaxContextExt, Transparency},
|
||||||
name::{known, AsName, Name},
|
name::{known, AsName, Name},
|
||||||
SpanMap,
|
span::SpanMapRef,
|
||||||
};
|
};
|
||||||
use base_db::{span::SyntaxContextId, CrateId};
|
use base_db::{span::SyntaxContextId, CrateId};
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
|
@ -47,7 +47,7 @@ impl ModPath {
|
||||||
pub fn from_src(
|
pub fn from_src(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
path: ast::Path,
|
path: ast::Path,
|
||||||
hygiene: &SpanMap,
|
hygiene: SpanMapRef<'_>,
|
||||||
) -> Option<ModPath> {
|
) -> Option<ModPath> {
|
||||||
convert_path(db, None, path, hygiene)
|
convert_path(db, None, path, hygiene)
|
||||||
}
|
}
|
||||||
|
@ -194,7 +194,7 @@ fn convert_path(
|
||||||
db: &dyn ExpandDatabase,
|
db: &dyn ExpandDatabase,
|
||||||
prefix: Option<ModPath>,
|
prefix: Option<ModPath>,
|
||||||
path: ast::Path,
|
path: ast::Path,
|
||||||
hygiene: &SpanMap,
|
hygiene: SpanMapRef<'_>,
|
||||||
) -> Option<ModPath> {
|
) -> Option<ModPath> {
|
||||||
let prefix = match path.qualifier() {
|
let prefix = match path.qualifier() {
|
||||||
Some(qual) => Some(convert_path(db, prefix, qual, hygiene)?),
|
Some(qual) => Some(convert_path(db, prefix, qual, hygiene)?),
|
||||||
|
@ -208,14 +208,14 @@ fn convert_path(
|
||||||
if prefix.is_some() {
|
if prefix.is_some() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
ModPath::from_kind(
|
||||||
resolve_crate_root(
|
resolve_crate_root(
|
||||||
db,
|
db,
|
||||||
hygiene
|
hygiene.span_for_range(name_ref.syntax().text_range()).ctx,
|
||||||
.span_for_range(name_ref.syntax().text_range())
|
|
||||||
.map_or(SyntaxContextId::ROOT, |s| s.ctx),
|
|
||||||
)
|
)
|
||||||
.map(PathKind::DollarCrate)
|
.map(PathKind::DollarCrate)
|
||||||
.map(ModPath::from_kind)?
|
.unwrap_or(PathKind::Crate),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
let mut res = prefix.unwrap_or_else(|| {
|
let mut res = prefix.unwrap_or_else(|| {
|
||||||
ModPath::from_kind(
|
ModPath::from_kind(
|
||||||
|
@ -265,13 +265,12 @@ fn convert_path(
|
||||||
// We follow what it did anyway :)
|
// We follow what it did anyway :)
|
||||||
if mod_path.segments.len() == 1 && mod_path.kind == PathKind::Plain {
|
if mod_path.segments.len() == 1 && mod_path.kind == PathKind::Plain {
|
||||||
if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
|
if let Some(_macro_call) = path.syntax().parent().and_then(ast::MacroCall::cast) {
|
||||||
let syn_ctx = hygiene
|
let syn_ctx = hygiene.span_for_range(segment.syntax().text_range()).ctx;
|
||||||
.span_for_range(segment.syntax().text_range())
|
|
||||||
.map_or(SyntaxContextId::ROOT, |s| s.ctx);
|
|
||||||
if let Some(macro_call_id) = db.lookup_intern_syntax_context(syn_ctx).outer_expn {
|
if let Some(macro_call_id) = db.lookup_intern_syntax_context(syn_ctx).outer_expn {
|
||||||
if db.lookup_intern_macro_call(macro_call_id).def.local_inner {
|
if db.lookup_intern_macro_call(macro_call_id).def.local_inner {
|
||||||
if let Some(crate_root) = resolve_crate_root(db, syn_ctx) {
|
mod_path.kind = match resolve_crate_root(db, syn_ctx) {
|
||||||
mod_path.kind = PathKind::DollarCrate(crate_root);
|
Some(crate_root) => PathKind::DollarCrate(crate_root),
|
||||||
|
None => PathKind::Crate,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -289,30 +288,19 @@ pub fn resolve_crate_root(db: &dyn ExpandDatabase, mut ctxt: SyntaxContextId) ->
|
||||||
// definitions actually produced by `macro` and `macro` definitions produced by
|
// definitions actually produced by `macro` and `macro` definitions produced by
|
||||||
// `macro_rules!`, but at least such configurations are not stable yet.
|
// `macro_rules!`, but at least such configurations are not stable yet.
|
||||||
ctxt = ctxt.normalize_to_macro_rules(db);
|
ctxt = ctxt.normalize_to_macro_rules(db);
|
||||||
let mut iter = ctxt.marks(db).into_iter().rev().peekable();
|
let mut iter = marks_rev(ctxt, db).peekable();
|
||||||
let mut result_mark = None;
|
let mut result_mark = None;
|
||||||
// Find the last opaque mark from the end if it exists.
|
// Find the last opaque mark from the end if it exists.
|
||||||
while let Some(&(mark, transparency)) = iter.peek() {
|
while let Some(&(mark, Transparency::Opaque)) = iter.peek() {
|
||||||
if transparency == Transparency::Opaque {
|
|
||||||
result_mark = Some(mark);
|
result_mark = Some(mark);
|
||||||
iter.next();
|
iter.next();
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Then find the last semi-transparent mark from the end if it exists.
|
// Then find the last semi-transparent mark from the end if it exists.
|
||||||
for (mark, transparency) in iter {
|
while let Some((mark, Transparency::SemiTransparent)) = iter.next() {
|
||||||
if transparency == Transparency::SemiTransparent {
|
|
||||||
result_mark = Some(mark);
|
result_mark = Some(mark);
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
match result_mark {
|
result_mark.flatten().map(|call| db.lookup_intern_macro_call(call.into()).def.krate)
|
||||||
Some(Some(call)) => Some(db.lookup_intern_macro_call(call.into()).def.krate),
|
|
||||||
Some(None) | None => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub use crate::name as __name;
|
pub use crate::name as __name;
|
||||||
|
|
109
crates/hir-expand/src/span.rs
Normal file
109
crates/hir-expand/src/span.rs
Normal file
|
@ -0,0 +1,109 @@
|
||||||
|
use base_db::{
|
||||||
|
span::{ErasedFileAstId, SpanAnchor, SpanData, SyntaxContextId, ROOT_ERASED_FILE_AST_ID},
|
||||||
|
FileId,
|
||||||
|
};
|
||||||
|
use mbe::TokenMap;
|
||||||
|
use syntax::{ast::HasModuleItem, AstNode, TextRange, TextSize};
|
||||||
|
use triomphe::Arc;
|
||||||
|
|
||||||
|
use crate::db::ExpandDatabase;
|
||||||
|
|
||||||
|
pub type ExpansionSpanMap = TokenMap<SpanData>;
|
||||||
|
|
||||||
|
/// Spanmap for a macro file or a real file
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub enum SpanMap {
|
||||||
|
/// Spanmap for a macro file
|
||||||
|
ExpansionSpanMap(Arc<ExpansionSpanMap>),
|
||||||
|
/// Spanmap for a real file
|
||||||
|
RealSpanMap(Arc<RealSpanMap>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
pub enum SpanMapRef<'a> {
|
||||||
|
/// Spanmap for a macro file
|
||||||
|
ExpansionSpanMap(&'a ExpansionSpanMap),
|
||||||
|
/// Spanmap for a real file
|
||||||
|
RealSpanMap(&'a RealSpanMap),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl mbe::SpanMapper<SpanData> for SpanMap {
|
||||||
|
fn span_for(&self, range: TextRange) -> SpanData {
|
||||||
|
self.span_for_range(range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl mbe::SpanMapper<SpanData> for SpanMapRef<'_> {
|
||||||
|
fn span_for(&self, range: TextRange) -> SpanData {
|
||||||
|
self.span_for_range(range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl mbe::SpanMapper<SpanData> for RealSpanMap {
|
||||||
|
fn span_for(&self, range: TextRange) -> SpanData {
|
||||||
|
self.span_for_range(range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpanMap {
|
||||||
|
pub fn span_for_range(&self, range: TextRange) -> SpanData {
|
||||||
|
match self {
|
||||||
|
Self::ExpansionSpanMap(span_map) => span_map.span_for_range(range),
|
||||||
|
Self::RealSpanMap(span_map) => span_map.span_for_range(range),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn as_ref(&self) -> SpanMapRef<'_> {
|
||||||
|
match self {
|
||||||
|
Self::ExpansionSpanMap(span_map) => SpanMapRef::ExpansionSpanMap(span_map),
|
||||||
|
Self::RealSpanMap(span_map) => SpanMapRef::RealSpanMap(span_map),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SpanMapRef<'_> {
|
||||||
|
pub fn span_for_range(self, range: TextRange) -> SpanData {
|
||||||
|
match self {
|
||||||
|
Self::ExpansionSpanMap(span_map) => span_map.span_for_range(range),
|
||||||
|
Self::RealSpanMap(span_map) => span_map.span_for_range(range),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(PartialEq, Eq, Hash, Debug)]
|
||||||
|
pub struct RealSpanMap {
|
||||||
|
file_id: FileId,
|
||||||
|
/// Invariant: Sorted vec over TextSize
|
||||||
|
// FIXME: SortedVec<(TextSize, ErasedFileAstId)>?
|
||||||
|
pairs: Box<[(TextSize, ErasedFileAstId)]>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl RealSpanMap {
|
||||||
|
pub fn empty(file_id: FileId) -> Self {
|
||||||
|
RealSpanMap { file_id, pairs: Box::from([(TextSize::new(0), ROOT_ERASED_FILE_AST_ID)]) }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn from_file(db: &dyn ExpandDatabase, file_id: FileId) -> Self {
|
||||||
|
let mut pairs = vec![(TextSize::new(0), ROOT_ERASED_FILE_AST_ID)];
|
||||||
|
let ast_id_map = db.ast_id_map(file_id.into());
|
||||||
|
pairs.extend(
|
||||||
|
db.parse(file_id)
|
||||||
|
.tree()
|
||||||
|
.items()
|
||||||
|
.map(|item| (item.syntax().text_range().start(), ast_id_map.ast_id(&item).erase())),
|
||||||
|
);
|
||||||
|
RealSpanMap { file_id, pairs: pairs.into_boxed_slice() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn span_for_range(&self, range: TextRange) -> SpanData {
|
||||||
|
let start = range.start();
|
||||||
|
let idx = self
|
||||||
|
.pairs
|
||||||
|
.binary_search_by(|&(it, _)| it.cmp(&start).then(std::cmp::Ordering::Less))
|
||||||
|
.unwrap_err();
|
||||||
|
let (offset, ast_id) = self.pairs[idx - 1];
|
||||||
|
SpanData {
|
||||||
|
range: range - offset,
|
||||||
|
anchor: SpanAnchor { file_id: self.file_id, ast_id },
|
||||||
|
ctx: SyntaxContextId::ROOT,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,6 @@
|
||||||
//! Attributes & documentation for hir types.
|
//! Attributes & documentation for hir types.
|
||||||
|
|
||||||
|
use base_db::FileId;
|
||||||
use hir_def::{
|
use hir_def::{
|
||||||
attr::AttrsWithOwner,
|
attr::AttrsWithOwner,
|
||||||
item_scope::ItemInNs,
|
item_scope::ItemInNs,
|
||||||
|
@ -8,7 +9,10 @@ use hir_def::{
|
||||||
resolver::{HasResolver, Resolver, TypeNs},
|
resolver::{HasResolver, Resolver, TypeNs},
|
||||||
AssocItemId, AttrDefId, ModuleDefId,
|
AssocItemId, AttrDefId, ModuleDefId,
|
||||||
};
|
};
|
||||||
use hir_expand::name::Name;
|
use hir_expand::{
|
||||||
|
name::Name,
|
||||||
|
span::{RealSpanMap, SpanMapRef},
|
||||||
|
};
|
||||||
use hir_ty::db::HirDatabase;
|
use hir_ty::db::HirDatabase;
|
||||||
use syntax::{ast, AstNode};
|
use syntax::{ast, AstNode};
|
||||||
|
|
||||||
|
@ -234,7 +238,11 @@ fn modpath_from_str(db: &dyn HirDatabase, link: &str) -> Option<ModPath> {
|
||||||
if ast_path.syntax().text() != link {
|
if ast_path.syntax().text() != link {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
ModPath::from_src(db.upcast(), ast_path, &Default::default())
|
ModPath::from_src(
|
||||||
|
db.upcast(),
|
||||||
|
ast_path,
|
||||||
|
SpanMapRef::RealSpanMap(&RealSpanMap::empty(FileId(0))),
|
||||||
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
let full = try_get_modpath(link);
|
let full = try_get_modpath(link);
|
||||||
|
|
|
@ -59,7 +59,7 @@ use hir_def::{
|
||||||
Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId,
|
Lookup, MacroExpander, MacroId, ModuleId, StaticId, StructId, TraitAliasId, TraitId,
|
||||||
TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
|
TypeAliasId, TypeOrConstParamId, TypeParamId, UnionId,
|
||||||
};
|
};
|
||||||
use hir_expand::{name::name, MacroCallKind};
|
use hir_expand::{name::name, InMacroFile, MacroCallKind};
|
||||||
use hir_ty::{
|
use hir_ty::{
|
||||||
all_super_traits, autoderef, check_orphan_rules,
|
all_super_traits, autoderef, check_orphan_rules,
|
||||||
consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
|
consteval::{try_const_usize, unknown_const_as_generic, ConstEvalError, ConstExt},
|
||||||
|
@ -3483,11 +3483,41 @@ impl Impl {
|
||||||
self.id.lookup(db.upcast()).container.into()
|
self.id.lookup(db.upcast()).container.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_builtin_derive(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> {
|
pub fn as_builtin_derive_attr(self, db: &dyn HirDatabase) -> Option<InFile<ast::Attr>> {
|
||||||
let src = self.source(db)?;
|
let src = self.source(db)?;
|
||||||
src.file_id.as_builtin_derive_attr_node(db.upcast())
|
src.file_id.as_builtin_derive_attr_node(db.upcast())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn as_builtin_derive_path(self, db: &dyn HirDatabase) -> Option<InMacroFile<ast::Path>> {
|
||||||
|
let src = self.source(db)?;
|
||||||
|
|
||||||
|
let macro_file = src.file_id.macro_file()?;
|
||||||
|
let loc = db.lookup_intern_macro_call(macro_file.macro_call_id);
|
||||||
|
let (derive_attr, derive_index) = match loc.kind {
|
||||||
|
MacroCallKind::Derive { ast_id, derive_attr_index, derive_index } => {
|
||||||
|
let module_id = self.id.lookup(db.upcast()).container;
|
||||||
|
(
|
||||||
|
db.crate_def_map(module_id.krate())[module_id.local_id]
|
||||||
|
.scope
|
||||||
|
.derive_macro_invoc(ast_id, derive_attr_index)?,
|
||||||
|
derive_index,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
let file_id = MacroFile { macro_call_id: derive_attr };
|
||||||
|
let path = db
|
||||||
|
.parse_macro_expansion(file_id)
|
||||||
|
.value
|
||||||
|
.0
|
||||||
|
.syntax_node()
|
||||||
|
.children()
|
||||||
|
.nth(derive_index as usize)
|
||||||
|
.and_then(<ast::Attr as AstNode>::cast)
|
||||||
|
.and_then(|it| it.path())?;
|
||||||
|
Some(InMacroFile { file_id, value: path })
|
||||||
|
}
|
||||||
|
|
||||||
pub fn check_orphan_rules(self, db: &dyn HirDatabase) -> bool {
|
pub fn check_orphan_rules(self, db: &dyn HirDatabase) -> bool {
|
||||||
check_orphan_rules(db, self.id)
|
check_orphan_rules(db, self.id)
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,6 +19,7 @@ use hir_expand::{db::ExpandDatabase, name::AsName, ExpansionInfo, HirFileIdExt,
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use rustc_hash::{FxHashMap, FxHashSet};
|
use rustc_hash::{FxHashMap, FxHashSet};
|
||||||
use smallvec::{smallvec, SmallVec};
|
use smallvec::{smallvec, SmallVec};
|
||||||
|
use stdx::TupleExt;
|
||||||
use syntax::{
|
use syntax::{
|
||||||
algo::skip_trivia_token,
|
algo::skip_trivia_token,
|
||||||
ast::{self, HasAttrs as _, HasGenericParams, HasLoopBody},
|
ast::{self, HasAttrs as _, HasGenericParams, HasLoopBody},
|
||||||
|
@ -529,11 +530,11 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
token: SyntaxToken,
|
token: SyntaxToken,
|
||||||
// FIXME: We might want this to be Option<TextSize> to be able to opt out of subrange
|
// FIXME: We might want this to be Option<TextSize> to be able to opt out of subrange
|
||||||
// mapping, specifically for node downmapping
|
// mapping, specifically for node downmapping
|
||||||
offset: TextSize,
|
_offset: TextSize,
|
||||||
f: &mut dyn FnMut(InFile<SyntaxToken>) -> bool,
|
f: &mut dyn FnMut(InFile<SyntaxToken>) -> bool,
|
||||||
) {
|
) {
|
||||||
|
// FIXME: Clean this up
|
||||||
let _p = profile::span("descend_into_macros");
|
let _p = profile::span("descend_into_macros");
|
||||||
let relative_token_offset = token.text_range().start().checked_sub(offset);
|
|
||||||
let parent = match token.parent() {
|
let parent = match token.parent() {
|
||||||
Some(it) => it,
|
Some(it) => it,
|
||||||
None => return,
|
None => return,
|
||||||
|
@ -543,13 +544,35 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
None => return,
|
None => return,
|
||||||
};
|
};
|
||||||
let def_map = sa.resolver.def_map();
|
let def_map = sa.resolver.def_map();
|
||||||
|
let absolute_range = match sa.file_id.repr() {
|
||||||
|
base_db::span::HirFileIdRepr::FileId(file_id) => {
|
||||||
|
FileRange { file_id, range: token.text_range() }
|
||||||
|
}
|
||||||
|
base_db::span::HirFileIdRepr::MacroFile(m) => {
|
||||||
|
let span =
|
||||||
|
self.db.parse_macro_expansion(m).value.1.span_for_range(token.text_range());
|
||||||
|
let range = span.range
|
||||||
|
+ self
|
||||||
|
.db
|
||||||
|
.ast_id_map(span.anchor.file_id.into())
|
||||||
|
.get_erased(span.anchor.ast_id)
|
||||||
|
.text_range()
|
||||||
|
.start();
|
||||||
|
FileRange { file_id: span.anchor.file_id, range }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// fetch span information of token in real file, then use that look through expansions of
|
||||||
|
// calls the token is in and afterwards recursively with the same span.
|
||||||
|
// what about things where spans change? Due to being joined etc, that is we don't find the
|
||||||
|
// exact span anymore?
|
||||||
|
|
||||||
let mut stack: SmallVec<[_; 4]> = smallvec![InFile::new(sa.file_id, token)];
|
let mut stack: SmallVec<[_; 4]> = smallvec![InFile::new(sa.file_id, token)];
|
||||||
let mut cache = self.expansion_info_cache.borrow_mut();
|
let mut cache = self.expansion_info_cache.borrow_mut();
|
||||||
let mut mcache = self.macro_call_cache.borrow_mut();
|
let mut mcache = self.macro_call_cache.borrow_mut();
|
||||||
|
|
||||||
let mut process_expansion_for_token =
|
let mut process_expansion_for_token =
|
||||||
|stack: &mut SmallVec<_>, macro_file, token: InFile<&_>| {
|
|stack: &mut SmallVec<_>, macro_file, _token: InFile<&_>| {
|
||||||
let expansion_info = cache
|
let expansion_info = cache
|
||||||
.entry(macro_file)
|
.entry(macro_file)
|
||||||
.or_insert_with(|| macro_file.expansion_info(self.db.upcast()))
|
.or_insert_with(|| macro_file.expansion_info(self.db.upcast()))
|
||||||
|
@ -560,11 +583,8 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
self.cache(value, file_id);
|
self.cache(value, file_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mapped_tokens = expansion_info.map_token_down(
|
let mapped_tokens =
|
||||||
self.db.upcast(),
|
expansion_info.map_range_down(self.db.upcast(), absolute_range, None)?;
|
||||||
token,
|
|
||||||
relative_token_offset,
|
|
||||||
)?;
|
|
||||||
let len = stack.len();
|
let len = stack.len();
|
||||||
|
|
||||||
// requeue the tokens we got from mapping our current token down
|
// requeue the tokens we got from mapping our current token down
|
||||||
|
@ -728,6 +748,8 @@ impl<'db> SemanticsImpl<'db> {
|
||||||
pub fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> {
|
pub fn original_range_opt(&self, node: &SyntaxNode) -> Option<FileRange> {
|
||||||
let node = self.find_file(node);
|
let node = self.find_file(node);
|
||||||
node.original_file_range_opt(self.db.upcast())
|
node.original_file_range_opt(self.db.upcast())
|
||||||
|
.filter(|(_, ctx)| ctx.is_root())
|
||||||
|
.map(TupleExt::head)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Attempts to map the node out of macro expanded files.
|
/// Attempts to map the node out of macro expanded files.
|
||||||
|
|
|
@ -7,7 +7,7 @@ use hir_def::{
|
||||||
AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, ModuleDefId, ModuleId,
|
AdtId, AssocItemId, DefWithBodyId, HasModule, ImplId, Lookup, MacroId, ModuleDefId, ModuleId,
|
||||||
TraitId,
|
TraitId,
|
||||||
};
|
};
|
||||||
use hir_expand::{HirFileId, InFile};
|
use hir_expand::{files::ascend_range_up_macros, HirFileId, InFile};
|
||||||
use hir_ty::db::HirDatabase;
|
use hir_ty::db::HirDatabase;
|
||||||
use syntax::{ast::HasName, AstNode, SmolStr, SyntaxNode, SyntaxNodePtr};
|
use syntax::{ast::HasName, AstNode, SmolStr, SyntaxNode, SyntaxNodePtr};
|
||||||
|
|
||||||
|
@ -50,13 +50,9 @@ impl DeclarationLocation {
|
||||||
node.as_ref().original_file_range(db.upcast())
|
node.as_ref().original_file_range(db.upcast())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn original_name_range(&self, db: &dyn HirDatabase) -> Option<FileRange> {
|
pub fn original_name_range(&self, db: &dyn HirDatabase) -> FileRange {
|
||||||
if let Some(file_id) = self.hir_file_id.file_id() {
|
let mapping = InFile::new(self.hir_file_id, self.name_ptr.text_range());
|
||||||
// fast path to prevent parsing
|
ascend_range_up_macros(db.upcast(), mapping).0
|
||||||
return Some(FileRange { file_id, range: self.name_ptr.text_range() });
|
|
||||||
}
|
|
||||||
let node = resolve_node(db, self.hir_file_id, &self.name_ptr);
|
|
||||||
node.as_ref().original_file_range_opt(db.upcast())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@ fn check(ra_fixture: &str, expect: Expect) {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore] // todo
|
||||||
fn complete_dot_in_attr() {
|
fn complete_dot_in_attr() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
@ -40,6 +41,7 @@ fn main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
#[ignore] // TODO
|
||||||
fn complete_dot_in_attr2() {
|
fn complete_dot_in_attr2() {
|
||||||
check(
|
check(
|
||||||
r#"
|
r#"
|
||||||
|
|
|
@ -22,10 +22,10 @@
|
||||||
//! Our current behavior is ¯\_(ツ)_/¯.
|
//! Our current behavior is ¯\_(ツ)_/¯.
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use base_db::{AnchoredPathBuf, FileId, FileRange};
|
use base_db::{span::SyntaxContextId, AnchoredPathBuf, FileId, FileRange};
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{FieldSource, HasSource, HirFileIdExt, InFile, ModuleSource, Semantics};
|
use hir::{FieldSource, HasSource, HirFileIdExt, InFile, ModuleSource, Semantics};
|
||||||
use stdx::never;
|
use stdx::{never, TupleExt};
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, HasName},
|
ast::{self, HasName},
|
||||||
AstNode, SyntaxKind, TextRange, T,
|
AstNode, SyntaxKind, TextRange, T,
|
||||||
|
@ -103,6 +103,7 @@ impl Definition {
|
||||||
/// renamed and extern crate names will report its range, though a rename will introduce
|
/// renamed and extern crate names will report its range, though a rename will introduce
|
||||||
/// an alias instead.
|
/// an alias instead.
|
||||||
pub fn range_for_rename(self, sema: &Semantics<'_, RootDatabase>) -> Option<FileRange> {
|
pub fn range_for_rename(self, sema: &Semantics<'_, RootDatabase>) -> Option<FileRange> {
|
||||||
|
let syn_ctx_is_root = |(range, ctx): (_, SyntaxContextId)| ctx.is_root().then(|| range);
|
||||||
let res = match self {
|
let res = match self {
|
||||||
Definition::Macro(mac) => {
|
Definition::Macro(mac) => {
|
||||||
let src = mac.source(sema.db)?;
|
let src = mac.source(sema.db)?;
|
||||||
|
@ -110,14 +111,18 @@ impl Definition {
|
||||||
Either::Left(it) => it.name()?,
|
Either::Left(it) => it.name()?,
|
||||||
Either::Right(it) => it.name()?,
|
Either::Right(it) => it.name()?,
|
||||||
};
|
};
|
||||||
src.with_value(name.syntax()).original_file_range_opt(sema.db)
|
src.with_value(name.syntax())
|
||||||
|
.original_file_range_opt(sema.db)
|
||||||
|
.and_then(syn_ctx_is_root)
|
||||||
}
|
}
|
||||||
Definition::Field(field) => {
|
Definition::Field(field) => {
|
||||||
let src = field.source(sema.db)?;
|
let src = field.source(sema.db)?;
|
||||||
match &src.value {
|
match &src.value {
|
||||||
FieldSource::Named(record_field) => {
|
FieldSource::Named(record_field) => {
|
||||||
let name = record_field.name()?;
|
let name = record_field.name()?;
|
||||||
src.with_value(name.syntax()).original_file_range_opt(sema.db)
|
src.with_value(name.syntax())
|
||||||
|
.original_file_range_opt(sema.db)
|
||||||
|
.and_then(syn_ctx_is_root)
|
||||||
}
|
}
|
||||||
FieldSource::Pos(_) => None,
|
FieldSource::Pos(_) => None,
|
||||||
}
|
}
|
||||||
|
@ -125,25 +130,31 @@ impl Definition {
|
||||||
Definition::Module(module) => {
|
Definition::Module(module) => {
|
||||||
let src = module.declaration_source(sema.db)?;
|
let src = module.declaration_source(sema.db)?;
|
||||||
let name = src.value.name()?;
|
let name = src.value.name()?;
|
||||||
src.with_value(name.syntax()).original_file_range_opt(sema.db)
|
src.with_value(name.syntax())
|
||||||
|
.original_file_range_opt(sema.db)
|
||||||
|
.and_then(syn_ctx_is_root)
|
||||||
}
|
}
|
||||||
Definition::Function(it) => name_range(it, sema),
|
Definition::Function(it) => name_range(it, sema).and_then(syn_ctx_is_root),
|
||||||
Definition::Adt(adt) => match adt {
|
Definition::Adt(adt) => match adt {
|
||||||
hir::Adt::Struct(it) => name_range(it, sema),
|
hir::Adt::Struct(it) => name_range(it, sema).and_then(syn_ctx_is_root),
|
||||||
hir::Adt::Union(it) => name_range(it, sema),
|
hir::Adt::Union(it) => name_range(it, sema).and_then(syn_ctx_is_root),
|
||||||
hir::Adt::Enum(it) => name_range(it, sema),
|
hir::Adt::Enum(it) => name_range(it, sema).and_then(syn_ctx_is_root),
|
||||||
},
|
},
|
||||||
Definition::Variant(it) => name_range(it, sema),
|
Definition::Variant(it) => name_range(it, sema).and_then(syn_ctx_is_root),
|
||||||
Definition::Const(it) => name_range(it, sema),
|
Definition::Const(it) => name_range(it, sema).and_then(syn_ctx_is_root),
|
||||||
Definition::Static(it) => name_range(it, sema),
|
Definition::Static(it) => name_range(it, sema).and_then(syn_ctx_is_root),
|
||||||
Definition::Trait(it) => name_range(it, sema),
|
Definition::Trait(it) => name_range(it, sema).and_then(syn_ctx_is_root),
|
||||||
Definition::TraitAlias(it) => name_range(it, sema),
|
Definition::TraitAlias(it) => name_range(it, sema).and_then(syn_ctx_is_root),
|
||||||
Definition::TypeAlias(it) => name_range(it, sema),
|
Definition::TypeAlias(it) => name_range(it, sema).and_then(syn_ctx_is_root),
|
||||||
Definition::Local(it) => name_range(it.primary_source(sema.db), sema),
|
Definition::Local(it) => {
|
||||||
|
name_range(it.primary_source(sema.db), sema).and_then(syn_ctx_is_root)
|
||||||
|
}
|
||||||
Definition::GenericParam(generic_param) => match generic_param {
|
Definition::GenericParam(generic_param) => match generic_param {
|
||||||
hir::GenericParam::LifetimeParam(lifetime_param) => {
|
hir::GenericParam::LifetimeParam(lifetime_param) => {
|
||||||
let src = lifetime_param.source(sema.db)?;
|
let src = lifetime_param.source(sema.db)?;
|
||||||
src.with_value(src.value.lifetime()?.syntax()).original_file_range_opt(sema.db)
|
src.with_value(src.value.lifetime()?.syntax())
|
||||||
|
.original_file_range_opt(sema.db)
|
||||||
|
.and_then(syn_ctx_is_root)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let x = match generic_param {
|
let x = match generic_param {
|
||||||
|
@ -156,22 +167,30 @@ impl Definition {
|
||||||
Either::Left(x) => x.name()?,
|
Either::Left(x) => x.name()?,
|
||||||
Either::Right(_) => return None,
|
Either::Right(_) => return None,
|
||||||
};
|
};
|
||||||
src.with_value(name.syntax()).original_file_range_opt(sema.db)
|
src.with_value(name.syntax())
|
||||||
|
.original_file_range_opt(sema.db)
|
||||||
|
.and_then(syn_ctx_is_root)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
Definition::Label(label) => {
|
Definition::Label(label) => {
|
||||||
let src = label.source(sema.db);
|
let src = label.source(sema.db);
|
||||||
let lifetime = src.value.lifetime()?;
|
let lifetime = src.value.lifetime()?;
|
||||||
src.with_value(lifetime.syntax()).original_file_range_opt(sema.db)
|
src.with_value(lifetime.syntax())
|
||||||
|
.original_file_range_opt(sema.db)
|
||||||
|
.and_then(syn_ctx_is_root)
|
||||||
}
|
}
|
||||||
Definition::ExternCrateDecl(it) => {
|
Definition::ExternCrateDecl(it) => {
|
||||||
let src = it.source(sema.db)?;
|
let src = it.source(sema.db)?;
|
||||||
if let Some(rename) = src.value.rename() {
|
if let Some(rename) = src.value.rename() {
|
||||||
let name = rename.name()?;
|
let name = rename.name()?;
|
||||||
src.with_value(name.syntax()).original_file_range_opt(sema.db)
|
src.with_value(name.syntax())
|
||||||
|
.original_file_range_opt(sema.db)
|
||||||
|
.and_then(syn_ctx_is_root)
|
||||||
} else {
|
} else {
|
||||||
let name = src.value.name_ref()?;
|
let name = src.value.name_ref()?;
|
||||||
src.with_value(name.syntax()).original_file_range_opt(sema.db)
|
src.with_value(name.syntax())
|
||||||
|
.original_file_range_opt(sema.db)
|
||||||
|
.and_then(syn_ctx_is_root)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Definition::BuiltinType(_) => return None,
|
Definition::BuiltinType(_) => return None,
|
||||||
|
@ -183,7 +202,10 @@ impl Definition {
|
||||||
};
|
};
|
||||||
return res;
|
return res;
|
||||||
|
|
||||||
fn name_range<D>(def: D, sema: &Semantics<'_, RootDatabase>) -> Option<FileRange>
|
fn name_range<D>(
|
||||||
|
def: D,
|
||||||
|
sema: &Semantics<'_, RootDatabase>,
|
||||||
|
) -> Option<(FileRange, SyntaxContextId)>
|
||||||
where
|
where
|
||||||
D: HasSource,
|
D: HasSource,
|
||||||
D::Ast: ast::HasName,
|
D::Ast: ast::HasName,
|
||||||
|
@ -256,8 +278,10 @@ fn rename_mod(
|
||||||
let file_id = src.file_id.original_file(sema.db);
|
let file_id = src.file_id.original_file(sema.db);
|
||||||
match src.value.name() {
|
match src.value.name() {
|
||||||
Some(name) => {
|
Some(name) => {
|
||||||
if let Some(file_range) =
|
if let Some(file_range) = src
|
||||||
src.with_value(name.syntax()).original_file_range_opt(sema.db)
|
.with_value(name.syntax())
|
||||||
|
.original_file_range_opt(sema.db)
|
||||||
|
.map(TupleExt::head)
|
||||||
{
|
{
|
||||||
source_change.insert_source_edit(
|
source_change.insert_source_edit(
|
||||||
file_id,
|
file_id,
|
||||||
|
@ -493,7 +517,12 @@ fn source_edit_from_def(
|
||||||
for source in local.sources(sema.db) {
|
for source in local.sources(sema.db) {
|
||||||
let source = match source.source.clone().original_ast_node(sema.db) {
|
let source = match source.source.clone().original_ast_node(sema.db) {
|
||||||
Some(source) => source,
|
Some(source) => source,
|
||||||
None => match source.source.syntax().original_file_range_opt(sema.db) {
|
None => match source
|
||||||
|
.source
|
||||||
|
.syntax()
|
||||||
|
.original_file_range_opt(sema.db)
|
||||||
|
.map(TupleExt::head)
|
||||||
|
{
|
||||||
Some(FileRange { file_id: file_id2, range }) => {
|
Some(FileRange { file_id: file_id2, range }) => {
|
||||||
file_id = Some(file_id2);
|
file_id = Some(file_id2);
|
||||||
edit.replace(range, new_name.to_owned());
|
edit.replace(range, new_name.to_owned());
|
||||||
|
|
|
@ -60,9 +60,6 @@ macro_rules! compile_error { () => {} }
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn eager_macro_concat() {
|
fn eager_macro_concat() {
|
||||||
// FIXME: this is incorrectly handling `$crate`, resulting in a wrong diagnostic.
|
|
||||||
// See: https://github.com/rust-lang/rust-analyzer/issues/10300
|
|
||||||
|
|
||||||
check_diagnostics(
|
check_diagnostics(
|
||||||
r#"
|
r#"
|
||||||
//- /lib.rs crate:lib deps:core
|
//- /lib.rs crate:lib deps:core
|
||||||
|
@ -80,7 +77,6 @@ macro_rules! m {
|
||||||
|
|
||||||
fn f() {
|
fn f() {
|
||||||
m!();
|
m!();
|
||||||
//^^^^ error: unresolved macro $crate::private::concat
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//- /core.rs crate:core
|
//- /core.rs crate:core
|
||||||
|
|
|
@ -33,7 +33,7 @@ pub(crate) fn typed_hole(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Di
|
||||||
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>> {
|
fn fixes(ctx: &DiagnosticsContext<'_>, d: &hir::TypedHole) -> Option<Vec<Assist>> {
|
||||||
let db = ctx.sema.db;
|
let db = ctx.sema.db;
|
||||||
let root = db.parse_or_expand(d.expr.file_id);
|
let root = db.parse_or_expand(d.expr.file_id);
|
||||||
let original_range =
|
let (original_range, _) =
|
||||||
d.expr.as_ref().map(|it| it.to_node(&root)).syntax().original_file_range_opt(db)?;
|
d.expr.as_ref().map(|it| it.to_node(&root)).syntax().original_file_range_opt(db)?;
|
||||||
let scope = ctx.sema.scope(d.expr.value.to_node(&root).syntax())?;
|
let scope = ctx.sema.scope(d.expr.value.to_node(&root).syntax())?;
|
||||||
let mut assists = vec![];
|
let mut assists = vec![];
|
||||||
|
|
|
@ -152,10 +152,8 @@ mod baz {}
|
||||||
main_node: Some(
|
main_node: Some(
|
||||||
InFile {
|
InFile {
|
||||||
file_id: FileId(
|
file_id: FileId(
|
||||||
FileId(
|
|
||||||
0,
|
0,
|
||||||
),
|
),
|
||||||
),
|
|
||||||
value: MODULE@0..8
|
value: MODULE@0..8
|
||||||
MOD_KW@0..3 "mod"
|
MOD_KW@0..3 "mod"
|
||||||
WHITESPACE@3..4 " "
|
WHITESPACE@3..4 " "
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use hir::{HirFileIdExt, Semantics};
|
use hir::{HirFileIdExt, InFile, Semantics};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::FileId, helpers::pick_best_token,
|
base_db::FileId, helpers::pick_best_token,
|
||||||
syntax_helpers::insert_whitespace_into_node::insert_ws_into, RootDatabase,
|
syntax_helpers::insert_whitespace_into_node::insert_ws_into, RootDatabase,
|
||||||
|
@ -49,7 +49,9 @@ pub(crate) fn expand_macro(db: &RootDatabase, position: FilePosition) -> Option<
|
||||||
|
|
||||||
let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string();
|
let name = descended.parent_ancestors().filter_map(ast::Path::cast).last()?.to_string();
|
||||||
// up map out of the #[derive] expansion
|
// up map out of the #[derive] expansion
|
||||||
let token = hir::InFile::new(hir_file, descended).upmap(db)?.value;
|
let InFile { file_id, value: tokens } =
|
||||||
|
hir::InFile::new(hir_file, descended).upmap_once(db)?;
|
||||||
|
let token = sema.parse_or_expand(file_id).covering_element(tokens[0]).into_token()?;
|
||||||
let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
|
let attr = token.parent_ancestors().find_map(ast::Attr::cast)?;
|
||||||
let expansions = sema.expand_derive_macro(&attr)?;
|
let expansions = sema.expand_derive_macro(&attr)?;
|
||||||
let idx = attr
|
let idx = attr
|
||||||
|
@ -338,8 +340,8 @@ fn main() {
|
||||||
expect![[r#"
|
expect![[r#"
|
||||||
match_ast!
|
match_ast!
|
||||||
{
|
{
|
||||||
if let Some(it) = ast::TraitDef::cast(container.clone()){}
|
if let Some(it) = ast::TraitDef::cast((container).clone()){}
|
||||||
else if let Some(it) = ast::ImplDef::cast(container.clone()){}
|
else if let Some(it) = ast::ImplDef::cast((container).clone()){}
|
||||||
else {
|
else {
|
||||||
{
|
{
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -401,11 +401,11 @@ fn bar() {
|
||||||
//- /lib.rs
|
//- /lib.rs
|
||||||
macro_rules! define_fn {
|
macro_rules! define_fn {
|
||||||
() => (fn foo() {})
|
() => (fn foo() {})
|
||||||
//^^^
|
|
||||||
}
|
}
|
||||||
|
|
||||||
define_fn!();
|
define_fn!();
|
||||||
|
//^^^^^^^^^^^^^
|
||||||
fn bar() {
|
fn bar() {
|
||||||
$0foo();
|
$0foo();
|
||||||
}
|
}
|
||||||
|
|
|
@ -249,7 +249,7 @@ impl T for &Foo {}
|
||||||
r#"
|
r#"
|
||||||
//- minicore: copy, derive
|
//- minicore: copy, derive
|
||||||
#[derive(Copy)]
|
#[derive(Copy)]
|
||||||
//^^^^^^^^^^^^^^^
|
//^^^^
|
||||||
struct Foo$0;
|
struct Foo$0;
|
||||||
"#,
|
"#,
|
||||||
);
|
);
|
||||||
|
|
|
@ -166,6 +166,7 @@ fn hover_simple(
|
||||||
} else {
|
} else {
|
||||||
sema.descend_into_macros_with_same_text(original_token.clone(), offset)
|
sema.descend_into_macros_with_same_text(original_token.clone(), offset)
|
||||||
};
|
};
|
||||||
|
dbg!(&descended);
|
||||||
let descended = || descended.iter();
|
let descended = || descended.iter();
|
||||||
|
|
||||||
let result = descended()
|
let result = descended()
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
//!
|
//!
|
||||||
//! Tests live in [`bind_pat`][super::bind_pat] module.
|
//! Tests live in [`bind_pat`][super::bind_pat] module.
|
||||||
use ide_db::{base_db::FileId, famous_defs::FamousDefs};
|
use ide_db::{base_db::FileId, famous_defs::FamousDefs};
|
||||||
|
use stdx::TupleExt;
|
||||||
use syntax::ast::{self, AstNode};
|
use syntax::ast::{self, AstNode};
|
||||||
use text_edit::{TextRange, TextSize};
|
use text_edit::{TextRange, TextSize};
|
||||||
|
|
||||||
|
@ -73,7 +74,9 @@ pub(super) fn hints(
|
||||||
capture.display_place(sema.db)
|
capture.display_place(sema.db)
|
||||||
),
|
),
|
||||||
None,
|
None,
|
||||||
source.name().and_then(|name| name.syntax().original_file_range_opt(sema.db)),
|
source.name().and_then(|name| {
|
||||||
|
name.syntax().original_file_range_opt(sema.db).map(TupleExt::head)
|
||||||
|
}),
|
||||||
);
|
);
|
||||||
acc.push(InlayHint {
|
acc.push(InlayHint {
|
||||||
needs_resolve: label.needs_resolve(),
|
needs_resolve: label.needs_resolve(),
|
||||||
|
|
|
@ -4,8 +4,8 @@ use std::fmt;
|
||||||
|
|
||||||
use either::Either;
|
use either::Either;
|
||||||
use hir::{
|
use hir::{
|
||||||
symbols::FileSymbol, AssocItem, FieldSource, HasContainer, HasSource, HirDisplay, HirFileId,
|
db::ExpandDatabase, symbols::FileSymbol, AssocItem, FieldSource, HasContainer, HasSource,
|
||||||
InFile, LocalSource, ModuleSource,
|
HirDisplay, HirFileId, InFile, LocalSource, ModuleSource,
|
||||||
};
|
};
|
||||||
use ide_db::{
|
use ide_db::{
|
||||||
base_db::{FileId, FileRange},
|
base_db::{FileId, FileRange},
|
||||||
|
@ -40,6 +40,8 @@ pub struct NavigationTarget {
|
||||||
/// comments, and `focus_range` is the range of the identifier.
|
/// comments, and `focus_range` is the range of the identifier.
|
||||||
///
|
///
|
||||||
/// Clients should place the cursor on this range when navigating to this target.
|
/// Clients should place the cursor on this range when navigating to this target.
|
||||||
|
///
|
||||||
|
/// This range must be contained within [`Self::full_range`].
|
||||||
pub focus_range: Option<TextRange>,
|
pub focus_range: Option<TextRange>,
|
||||||
pub name: SmolStr,
|
pub name: SmolStr,
|
||||||
pub kind: Option<SymbolKind>,
|
pub kind: Option<SymbolKind>,
|
||||||
|
@ -166,13 +168,9 @@ impl NavigationTarget {
|
||||||
impl TryToNav for FileSymbol {
|
impl TryToNav for FileSymbol {
|
||||||
fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
|
fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
|
||||||
let full_range = self.loc.original_range(db);
|
let full_range = self.loc.original_range(db);
|
||||||
let focus_range = self.loc.original_name_range(db).and_then(|it| {
|
let focus_range = self.loc.original_name_range(db);
|
||||||
if it.file_id == full_range.file_id {
|
let focus_range =
|
||||||
Some(it.range)
|
if focus_range.file_id == full_range.file_id { Some(focus_range.range) } else { None };
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Some(NavigationTarget {
|
Some(NavigationTarget {
|
||||||
file_id: full_range.file_id,
|
file_id: full_range.file_id,
|
||||||
|
@ -363,11 +361,11 @@ impl ToNav for hir::Module {
|
||||||
impl TryToNav for hir::Impl {
|
impl TryToNav for hir::Impl {
|
||||||
fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
|
fn try_to_nav(&self, db: &RootDatabase) -> Option<NavigationTarget> {
|
||||||
let InFile { file_id, value } = self.source(db)?;
|
let InFile { file_id, value } = self.source(db)?;
|
||||||
let derive_attr = self.as_builtin_derive(db);
|
let derive_path = self.as_builtin_derive_path(db);
|
||||||
|
|
||||||
let (focus, syntax) = match &derive_attr {
|
let (file_id, focus, syntax) = match &derive_path {
|
||||||
Some(attr) => (None, attr.value.syntax()),
|
Some(attr) => (attr.file_id.into(), None, attr.value.syntax()),
|
||||||
None => (value.self_ty(), value.syntax()),
|
None => (file_id, value.self_ty(), value.syntax()),
|
||||||
};
|
};
|
||||||
|
|
||||||
let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus);
|
let (file_id, full_range, focus_range) = orig_range_with_focus(db, file_id, syntax, focus);
|
||||||
|
@ -628,19 +626,30 @@ impl TryToNav for hir::ConstParam {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns the original range of the syntax node, and the range of the name mapped out of macro expansions
|
||||||
|
/// Additionally verifies that the name span is in bounds and related to the original range.
|
||||||
fn orig_range_with_focus(
|
fn orig_range_with_focus(
|
||||||
db: &RootDatabase,
|
db: &RootDatabase,
|
||||||
hir_file: HirFileId,
|
hir_file: HirFileId,
|
||||||
value: &SyntaxNode,
|
value: &SyntaxNode,
|
||||||
name: Option<impl AstNode>,
|
name: Option<impl AstNode>,
|
||||||
) -> (FileId, TextRange, Option<TextRange>) {
|
) -> (FileId, TextRange, Option<TextRange>) {
|
||||||
let FileRange { file_id, range: full_range } =
|
let FileRange { file_id, range } =
|
||||||
InFile::new(hir_file, value).original_file_range(db);
|
match InFile::new(hir_file, value).original_file_range_opt(db) {
|
||||||
|
Some((range, ctxt)) if ctxt.is_root() => range,
|
||||||
|
_ => db
|
||||||
|
.lookup_intern_macro_call(hir_file.macro_file().unwrap().macro_call_id)
|
||||||
|
.kind
|
||||||
|
.original_call_range(db),
|
||||||
|
};
|
||||||
let focus_range = name
|
let focus_range = name
|
||||||
.and_then(|it| InFile::new(hir_file, it.syntax()).original_file_range_opt(db))
|
.and_then(|it| InFile::new(hir_file, it.syntax()).original_file_range_opt(db))
|
||||||
.and_then(|range| if range.file_id == file_id { Some(range.range) } else { None });
|
.filter(|(frange, ctxt)| {
|
||||||
|
ctxt.is_root() && frange.file_id == file_id && frange.range.contains_range(frange.range)
|
||||||
|
})
|
||||||
|
.map(|(frange, _ctxt)| frange.range);
|
||||||
|
|
||||||
(file_id, full_range, focus_range)
|
(file_id, range, focus_range)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -242,6 +242,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[track_caller]
|
||||||
fn check_definitions(ra_fixture: &str) {
|
fn check_definitions(ra_fixture: &str) {
|
||||||
let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture);
|
let (analysis, ranges) = fixture::annotations_without_marker(ra_fixture);
|
||||||
let s = StaticIndex::compute(&analysis);
|
let s = StaticIndex::compute(&analysis);
|
||||||
|
|
|
@ -127,7 +127,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
|
||||||
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">+</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
||||||
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
||||||
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
||||||
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">-</span><span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="numeric_literal">0</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span>, <span class="numeric_literal macro">-</span><span class="numeric_literal macro">5</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
||||||
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"</span><span class="format_specifier">{</span><span class="format_specifier">:</span><span class="format_specifier">#</span><span class="numeric_literal">0</span><span class="numeric_literal">10</span><span class="variable">x</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="numeric_literal macro">27</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
||||||
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">0</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">5</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
||||||
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
<span class="macro">println</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello </span><span class="format_specifier">{</span><span class="numeric_literal">1</span><span class="format_specifier">}</span><span class="string_literal macro"> is </span><span class="format_specifier">{</span><span class="numeric_literal">2</span><span class="format_specifier">:</span><span class="format_specifier">.</span><span class="numeric_literal">0</span><span class="format_specifier">$</span><span class="format_specifier">}</span><span class="string_literal macro">"</span><span class="comma macro">,</span> <span class="numeric_literal macro">5</span><span class="comma macro">,</span> <span class="string_literal macro">"x"</span><span class="comma macro">,</span> <span class="numeric_literal macro">0.01</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
|
||||||
|
|
|
@ -10,12 +10,12 @@ use tt::{Span, SpanAnchor, SyntaxContext};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
parser::{MetaVarKind, Op, RepeatKind, Separator},
|
parser::{MetaVarKind, Op, RepeatKind, Separator},
|
||||||
syntax_node_to_token_tree, DeclarativeMacro,
|
syntax_node_to_token_tree, DeclarativeMacro, SpanMapper,
|
||||||
};
|
};
|
||||||
|
|
||||||
type SpanData = tt::SpanData<DummyFile, DummyCtx>;
|
type SpanData = tt::SpanData<DummyFile, DummyCtx>;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
|
||||||
struct DummyFile;
|
struct DummyFile;
|
||||||
impl SpanAnchor for DummyFile {
|
impl SpanAnchor for DummyFile {
|
||||||
const DUMMY: Self = DummyFile;
|
const DUMMY: Self = DummyFile;
|
||||||
|
@ -27,6 +27,14 @@ impl SyntaxContext for DummyCtx {
|
||||||
const DUMMY: Self = DummyCtx;
|
const DUMMY: Self = DummyCtx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct NoOpMap;
|
||||||
|
|
||||||
|
impl SpanMapper<SpanData> for NoOpMap {
|
||||||
|
fn span_for(&self, range: syntax::TextRange) -> SpanData {
|
||||||
|
SpanData { range, anchor: DummyFile, ctx: DummyCtx }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn benchmark_parse_macro_rules() {
|
fn benchmark_parse_macro_rules() {
|
||||||
if skip_slow_tests() {
|
if skip_slow_tests() {
|
||||||
|
@ -79,12 +87,7 @@ fn macro_rules_fixtures_tt() -> FxHashMap<String, tt::Subtree<SpanData>> {
|
||||||
.filter_map(ast::MacroRules::cast)
|
.filter_map(ast::MacroRules::cast)
|
||||||
.map(|rule| {
|
.map(|rule| {
|
||||||
let id = rule.name().unwrap().to_string();
|
let id = rule.name().unwrap().to_string();
|
||||||
let def_tt = syntax_node_to_token_tree(
|
let def_tt = syntax_node_to_token_tree(rule.token_tree().unwrap().syntax(), NoOpMap);
|
||||||
rule.token_tree().unwrap().syntax(),
|
|
||||||
DummyFile,
|
|
||||||
0.into(),
|
|
||||||
&Default::default(),
|
|
||||||
);
|
|
||||||
(id, def_tt)
|
(id, def_tt)
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
|
|
@ -30,12 +30,12 @@ use crate::{
|
||||||
|
|
||||||
// FIXME: we probably should re-think `token_tree_to_syntax_node` interfaces
|
// FIXME: we probably should re-think `token_tree_to_syntax_node` interfaces
|
||||||
pub use ::parser::TopEntryPoint;
|
pub use ::parser::TopEntryPoint;
|
||||||
pub use tt::{Delimiter, DelimiterKind, Punct};
|
pub use tt::{Delimiter, DelimiterKind, Punct, SyntaxContext};
|
||||||
|
|
||||||
pub use crate::{
|
pub use crate::{
|
||||||
syntax_bridge::{
|
syntax_bridge::{
|
||||||
map_from_syntax_node, parse_exprs_with_sep, parse_to_token_tree, syntax_node_to_token_tree,
|
map_from_syntax_node, parse_exprs_with_sep, parse_to_token_tree, syntax_node_to_token_tree,
|
||||||
syntax_node_to_token_tree_censored, token_tree_to_syntax_node,
|
syntax_node_to_token_tree_censored, token_tree_to_syntax_node, SpanMapper,
|
||||||
},
|
},
|
||||||
token_map::TokenMap,
|
token_map::TokenMap,
|
||||||
};
|
};
|
||||||
|
|
|
@ -17,43 +17,52 @@ use crate::{to_parser_input::to_parser_input, tt_iter::TtIter, TokenMap};
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests;
|
mod tests;
|
||||||
|
|
||||||
|
pub trait SpanMapper<S: Span> {
|
||||||
|
fn span_for(&self, range: TextRange) -> S;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Span> SpanMapper<S> for TokenMap<S> {
|
||||||
|
fn span_for(&self, range: TextRange) -> S {
|
||||||
|
self.span_for_range(range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Span, SM: SpanMapper<S>> SpanMapper<S> for &SM {
|
||||||
|
fn span_for(&self, range: TextRange) -> S {
|
||||||
|
SM::span_for(self, range)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Convert the syntax node to a `TokenTree` (what macro
|
/// Convert the syntax node to a `TokenTree` (what macro
|
||||||
/// will consume).
|
/// will consume).
|
||||||
/// `anchor` and `anchor_offset` are used to convert the node's spans
|
|
||||||
/// to relative spans, relative to the passed anchor.
|
|
||||||
/// `map` is used to resolve the converted spans accordingly.
|
|
||||||
/// TODO: Flesh out the doc comment more thoroughly
|
/// TODO: Flesh out the doc comment more thoroughly
|
||||||
pub fn syntax_node_to_token_tree<Anchor, Ctx>(
|
pub fn syntax_node_to_token_tree<Anchor, Ctx, SpanMap>(
|
||||||
node: &SyntaxNode,
|
node: &SyntaxNode,
|
||||||
anchor: Anchor,
|
map: SpanMap,
|
||||||
anchor_offset: TextSize,
|
|
||||||
map: &TokenMap<SpanData<Anchor, Ctx>>,
|
|
||||||
) -> tt::Subtree<SpanData<Anchor, Ctx>>
|
) -> tt::Subtree<SpanData<Anchor, Ctx>>
|
||||||
where
|
where
|
||||||
SpanData<Anchor, Ctx>: Span,
|
SpanData<Anchor, Ctx>: Span,
|
||||||
Anchor: Copy,
|
Anchor: Copy,
|
||||||
Ctx: SyntaxContext,
|
Ctx: SyntaxContext,
|
||||||
|
SpanMap: SpanMapper<SpanData<Anchor, Ctx>>,
|
||||||
{
|
{
|
||||||
assert!(anchor_offset <= node.text_range().start());
|
let mut c = Converter::new(node, vec![], map);
|
||||||
let mut c = Converter::new(node, anchor_offset, vec![], map);
|
convert_tokens(&mut c)
|
||||||
convert_tokens(&mut c, anchor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn syntax_node_to_token_tree_censored<Anchor, Ctx>(
|
pub fn syntax_node_to_token_tree_censored<Anchor, Ctx, SpanMap>(
|
||||||
node: &SyntaxNode,
|
node: &SyntaxNode,
|
||||||
anchor: Anchor,
|
map: SpanMap,
|
||||||
anchor_offset: TextSize,
|
|
||||||
map: &TokenMap<SpanData<Anchor, Ctx>>,
|
|
||||||
censored: Vec<SyntaxNode>,
|
censored: Vec<SyntaxNode>,
|
||||||
) -> tt::Subtree<SpanData<Anchor, Ctx>>
|
) -> tt::Subtree<SpanData<Anchor, Ctx>>
|
||||||
where
|
where
|
||||||
|
SpanMap: SpanMapper<SpanData<Anchor, Ctx>>,
|
||||||
SpanData<Anchor, Ctx>: Span,
|
SpanData<Anchor, Ctx>: Span,
|
||||||
Anchor: Copy,
|
Anchor: Copy,
|
||||||
Ctx: SyntaxContext,
|
Ctx: SyntaxContext,
|
||||||
{
|
{
|
||||||
assert!(anchor_offset <= node.text_range().start());
|
let mut c = Converter::new(node, censored, map);
|
||||||
let mut c = Converter::new(node, anchor_offset, censored, map);
|
convert_tokens(&mut c)
|
||||||
convert_tokens(&mut c, anchor)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The following items are what `rustc` macro can be parsed into :
|
// The following items are what `rustc` macro can be parsed into :
|
||||||
|
@ -113,20 +122,21 @@ where
|
||||||
SpanData<Anchor, Ctx>: Span,
|
SpanData<Anchor, Ctx>: Span,
|
||||||
Ctx: SyntaxContext,
|
Ctx: SyntaxContext,
|
||||||
{
|
{
|
||||||
let mut map = TokenMap::default();
|
let mut map = TokenMap::empty();
|
||||||
node.descendants_with_tokens().filter_map(NodeOrToken::into_token).for_each(|t| {
|
node.descendants_with_tokens().filter_map(NodeOrToken::into_token).for_each(|t| {
|
||||||
map.insert(
|
map.insert(
|
||||||
t.text_range(),
|
t.text_range(),
|
||||||
SpanData { range: t.text_range() - anchor_offset, anchor, ctx: Ctx::DUMMY },
|
SpanData { range: t.text_range() - anchor_offset, anchor, ctx: Ctx::DUMMY },
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
map.finish();
|
||||||
map
|
map
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a string to a `TokenTree`
|
/// Convert a string to a `TokenTree`
|
||||||
pub fn parse_to_token_tree<Anchor, Ctx>(
|
pub fn parse_to_token_tree<Anchor, Ctx>(
|
||||||
text: &str,
|
|
||||||
anchor: Anchor,
|
anchor: Anchor,
|
||||||
|
text: &str,
|
||||||
) -> Option<tt::Subtree<SpanData<Anchor, Ctx>>>
|
) -> Option<tt::Subtree<SpanData<Anchor, Ctx>>>
|
||||||
where
|
where
|
||||||
SpanData<Anchor, Ctx>: Span,
|
SpanData<Anchor, Ctx>: Span,
|
||||||
|
@ -137,8 +147,8 @@ where
|
||||||
if lexed.errors().next().is_some() {
|
if lexed.errors().next().is_some() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
let mut conv = RawConverter { lexed, pos: 0, _offset: TextSize::default() };
|
let mut conv = RawConverter { lexed, pos: 0, anchor };
|
||||||
Some(convert_tokens(&mut conv, anchor))
|
Some(convert_tokens(&mut conv))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Split token tree with separate expr: $($e:expr)SEP*
|
/// Split token tree with separate expr: $($e:expr)SEP*
|
||||||
|
@ -175,10 +185,7 @@ pub fn parse_exprs_with_sep<S: Span>(tt: &tt::Subtree<S>, sep: char) -> Vec<tt::
|
||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn convert_tokens<Anchor, Ctx, C>(
|
fn convert_tokens<Anchor, Ctx, C>(conv: &mut C) -> tt::Subtree<SpanData<Anchor, Ctx>>
|
||||||
conv: &mut C,
|
|
||||||
anchor: Anchor,
|
|
||||||
) -> tt::Subtree<SpanData<Anchor, Ctx>>
|
|
||||||
where
|
where
|
||||||
C: TokenConverter<Anchor, Ctx>,
|
C: TokenConverter<Anchor, Ctx>,
|
||||||
Ctx: SyntaxContext,
|
Ctx: SyntaxContext,
|
||||||
|
@ -188,16 +195,15 @@ where
|
||||||
let entry = tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: vec![] };
|
let entry = tt::Subtree { delimiter: tt::Delimiter::UNSPECIFIED, token_trees: vec![] };
|
||||||
let mut stack = NonEmptyVec::new(entry);
|
let mut stack = NonEmptyVec::new(entry);
|
||||||
|
|
||||||
while let Some((token, rel_range, abs_range)) = conv.bump() {
|
while let Some((token, abs_range)) = conv.bump() {
|
||||||
let tt::Subtree { delimiter, token_trees: result } = stack.last_mut();
|
let tt::Subtree { delimiter, token_trees: result } = stack.last_mut();
|
||||||
let mk_dummy_span = || SpanData { range: rel_range, anchor, ctx: Ctx::DUMMY };
|
|
||||||
|
|
||||||
let kind = token.kind(conv);
|
let kind = token.kind(conv);
|
||||||
|
|
||||||
let tt = match kind {
|
let tt = match kind {
|
||||||
// Desugar doc comments into doc attributes
|
// Desugar doc comments into doc attributes
|
||||||
COMMENT => {
|
COMMENT => {
|
||||||
let span = conv.span_for(abs_range).unwrap_or_else(mk_dummy_span);
|
let span = conv.span_for(abs_range);
|
||||||
if let Some(tokens) = conv.convert_doc_comment(&token, span) {
|
if let Some(tokens) = conv.convert_doc_comment(&token, span) {
|
||||||
result.extend(tokens);
|
result.extend(tokens);
|
||||||
}
|
}
|
||||||
|
@ -215,8 +221,7 @@ where
|
||||||
// and end the subtree here
|
// and end the subtree here
|
||||||
if matches!(expected, Some(expected) if expected == kind) {
|
if matches!(expected, Some(expected) if expected == kind) {
|
||||||
if let Some(mut subtree) = stack.pop() {
|
if let Some(mut subtree) = stack.pop() {
|
||||||
subtree.delimiter.close =
|
subtree.delimiter.close = conv.span_for(abs_range);
|
||||||
conv.span_for(abs_range).unwrap_or_else(mk_dummy_span);
|
|
||||||
stack.last_mut().token_trees.push(subtree.into());
|
stack.last_mut().token_trees.push(subtree.into());
|
||||||
}
|
}
|
||||||
continue;
|
continue;
|
||||||
|
@ -231,11 +236,12 @@ where
|
||||||
|
|
||||||
// Start a new subtree
|
// Start a new subtree
|
||||||
if let Some(kind) = delim {
|
if let Some(kind) = delim {
|
||||||
|
let open = conv.span_for(abs_range);
|
||||||
stack.push(tt::Subtree {
|
stack.push(tt::Subtree {
|
||||||
delimiter: tt::Delimiter {
|
delimiter: tt::Delimiter {
|
||||||
open: conv.span_for(abs_range).unwrap_or_else(mk_dummy_span),
|
open,
|
||||||
// will be overwritten on subtree close above
|
// will be overwritten on subtree close above
|
||||||
close: mk_dummy_span(),
|
close: open,
|
||||||
kind,
|
kind,
|
||||||
},
|
},
|
||||||
token_trees: vec![],
|
token_trees: vec![],
|
||||||
|
@ -250,21 +256,12 @@ where
|
||||||
let Some(char) = token.to_char(conv) else {
|
let Some(char) = token.to_char(conv) else {
|
||||||
panic!("Token from lexer must be single char: token = {token:#?}")
|
panic!("Token from lexer must be single char: token = {token:#?}")
|
||||||
};
|
};
|
||||||
tt::Leaf::from(tt::Punct {
|
tt::Leaf::from(tt::Punct { char, spacing, span: conv.span_for(abs_range) }).into()
|
||||||
char,
|
|
||||||
spacing,
|
|
||||||
span: conv.span_for(abs_range).unwrap_or_else(mk_dummy_span),
|
|
||||||
})
|
|
||||||
.into()
|
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
macro_rules! make_leaf {
|
macro_rules! make_leaf {
|
||||||
($i:ident) => {
|
($i:ident) => {
|
||||||
tt::$i {
|
tt::$i { span: conv.span_for(abs_range), text: token.to_text(conv) }.into()
|
||||||
span: conv.span_for(abs_range).unwrap_or_else(mk_dummy_span),
|
|
||||||
text: token.to_text(conv),
|
|
||||||
}
|
|
||||||
.into()
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
let leaf: tt::Leaf<_> = match kind {
|
let leaf: tt::Leaf<_> = match kind {
|
||||||
|
@ -273,32 +270,21 @@ where
|
||||||
UNDERSCORE => make_leaf!(Ident),
|
UNDERSCORE => make_leaf!(Ident),
|
||||||
k if k.is_keyword() => make_leaf!(Ident),
|
k if k.is_keyword() => make_leaf!(Ident),
|
||||||
k if k.is_literal() => make_leaf!(Literal),
|
k if k.is_literal() => make_leaf!(Literal),
|
||||||
// FIXME: Check whether span splitting works as intended
|
|
||||||
LIFETIME_IDENT => {
|
LIFETIME_IDENT => {
|
||||||
let char_unit = TextSize::of('\'');
|
|
||||||
let r = TextRange::at(rel_range.start(), char_unit);
|
|
||||||
let apostrophe = tt::Leaf::from(tt::Punct {
|
let apostrophe = tt::Leaf::from(tt::Punct {
|
||||||
char: '\'',
|
char: '\'',
|
||||||
spacing: tt::Spacing::Joint,
|
spacing: tt::Spacing::Joint,
|
||||||
span: conv.span_for(abs_range).unwrap_or(SpanData {
|
span: conv
|
||||||
range: r,
|
.span_for(TextRange::at(abs_range.start(), TextSize::of('\''))),
|
||||||
anchor,
|
|
||||||
ctx: Ctx::DUMMY,
|
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
result.push(apostrophe.into());
|
result.push(apostrophe.into());
|
||||||
|
|
||||||
let r = TextRange::at(
|
|
||||||
rel_range.start() + char_unit,
|
|
||||||
rel_range.len() - char_unit,
|
|
||||||
);
|
|
||||||
let ident = tt::Leaf::from(tt::Ident {
|
let ident = tt::Leaf::from(tt::Ident {
|
||||||
text: SmolStr::new(&token.to_text(conv)[1..]),
|
text: SmolStr::new(&token.to_text(conv)[1..]),
|
||||||
span: conv.span_for(abs_range).unwrap_or(SpanData {
|
span: conv.span_for(TextRange::at(
|
||||||
range: r,
|
abs_range.start() + TextSize::of('\''),
|
||||||
anchor,
|
abs_range.end(),
|
||||||
ctx: Ctx::DUMMY,
|
)),
|
||||||
}),
|
|
||||||
});
|
});
|
||||||
result.push(ident.into());
|
result.push(ident.into());
|
||||||
continue;
|
continue;
|
||||||
|
@ -433,10 +419,10 @@ fn convert_doc_comment<S: Copy>(
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A raw token (straight from lexer) converter
|
/// A raw token (straight from lexer) converter
|
||||||
struct RawConverter<'a> {
|
struct RawConverter<'a, Anchor> {
|
||||||
lexed: parser::LexedStr<'a>,
|
lexed: parser::LexedStr<'a>,
|
||||||
pos: usize,
|
pos: usize,
|
||||||
_offset: TextSize,
|
anchor: Anchor,
|
||||||
}
|
}
|
||||||
|
|
||||||
trait SrcToken<Ctx>: std::fmt::Debug {
|
trait SrcToken<Ctx>: std::fmt::Debug {
|
||||||
|
@ -456,28 +442,28 @@ trait TokenConverter<Anchor, Ctx>: Sized {
|
||||||
span: SpanData<Anchor, Ctx>,
|
span: SpanData<Anchor, Ctx>,
|
||||||
) -> Option<Vec<tt::TokenTree<SpanData<Anchor, Ctx>>>>;
|
) -> Option<Vec<tt::TokenTree<SpanData<Anchor, Ctx>>>>;
|
||||||
|
|
||||||
fn bump(&mut self) -> Option<(Self::Token, TextRange, TextRange)>;
|
fn bump(&mut self) -> Option<(Self::Token, TextRange)>;
|
||||||
|
|
||||||
fn peek(&self) -> Option<Self::Token>;
|
fn peek(&self) -> Option<Self::Token>;
|
||||||
|
|
||||||
fn span_for(&self, range: TextRange) -> Option<SpanData<Anchor, Ctx>>;
|
fn span_for(&self, range: TextRange) -> SpanData<Anchor, Ctx>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SrcToken<RawConverter<'_>> for usize {
|
impl<Anchor> SrcToken<RawConverter<'_, Anchor>> for usize {
|
||||||
fn kind(&self, ctx: &RawConverter<'_>) -> SyntaxKind {
|
fn kind(&self, ctx: &RawConverter<'_, Anchor>) -> SyntaxKind {
|
||||||
ctx.lexed.kind(*self)
|
ctx.lexed.kind(*self)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_char(&self, ctx: &RawConverter<'_>) -> Option<char> {
|
fn to_char(&self, ctx: &RawConverter<'_, Anchor>) -> Option<char> {
|
||||||
ctx.lexed.text(*self).chars().next()
|
ctx.lexed.text(*self).chars().next()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn to_text(&self, ctx: &RawConverter<'_>) -> SmolStr {
|
fn to_text(&self, ctx: &RawConverter<'_, Anchor>) -> SmolStr {
|
||||||
ctx.lexed.text(*self).into()
|
ctx.lexed.text(*self).into()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Anchor: Copy, Ctx> TokenConverter<Anchor, Ctx> for RawConverter<'_>
|
impl<Anchor: Copy, Ctx: SyntaxContext> TokenConverter<Anchor, Ctx> for RawConverter<'_, Anchor>
|
||||||
where
|
where
|
||||||
SpanData<Anchor, Ctx>: Span,
|
SpanData<Anchor, Ctx>: Span,
|
||||||
{
|
{
|
||||||
|
@ -492,7 +478,7 @@ where
|
||||||
convert_doc_comment(&doc_comment(text), span)
|
convert_doc_comment(&doc_comment(text), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bump(&mut self) -> Option<(Self::Token, TextRange, TextRange)> {
|
fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
|
||||||
if self.pos == self.lexed.len() {
|
if self.pos == self.lexed.len() {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
@ -501,7 +487,7 @@ where
|
||||||
let range = self.lexed.text_range(token);
|
let range = self.lexed.text_range(token);
|
||||||
let range = TextRange::new(range.start.try_into().ok()?, range.end.try_into().ok()?);
|
let range = TextRange::new(range.start.try_into().ok()?, range.end.try_into().ok()?);
|
||||||
|
|
||||||
Some((token, range, range))
|
Some((token, range))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn peek(&self) -> Option<Self::Token> {
|
fn peek(&self) -> Option<Self::Token> {
|
||||||
|
@ -511,41 +497,27 @@ where
|
||||||
Some(self.pos)
|
Some(self.pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn span_for(&self, _: TextRange) -> Option<SpanData<Anchor, Ctx>> {
|
fn span_for(&self, range: TextRange) -> SpanData<Anchor, Ctx> {
|
||||||
None
|
SpanData { range, anchor: self.anchor, ctx: Ctx::DUMMY }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Converter<'a, Anchor, Ctx> {
|
struct Converter<SpanMap> {
|
||||||
current: Option<SyntaxToken>,
|
current: Option<SyntaxToken>,
|
||||||
preorder: PreorderWithTokens,
|
preorder: PreorderWithTokens,
|
||||||
range: TextRange,
|
range: TextRange,
|
||||||
punct_offset: Option<(SyntaxToken, TextSize)>,
|
punct_offset: Option<(SyntaxToken, TextSize)>,
|
||||||
/// Used to make the emitted text ranges in the spans relative to the span anchor.
|
/// Used to make the emitted text ranges in the spans relative to the span anchor.
|
||||||
offset: TextSize,
|
map: SpanMap,
|
||||||
map: &'a TokenMap<SpanData<Anchor, Ctx>>,
|
|
||||||
censored: Vec<SyntaxNode>,
|
censored: Vec<SyntaxNode>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, Anchor, Ctx> Converter<'a, Anchor, Ctx> {
|
impl<SpanMap> Converter<SpanMap> {
|
||||||
fn new(
|
fn new(node: &SyntaxNode, censored: Vec<SyntaxNode>, map: SpanMap) -> Self {
|
||||||
node: &SyntaxNode,
|
|
||||||
anchor_offset: TextSize,
|
|
||||||
censored: Vec<SyntaxNode>,
|
|
||||||
map: &'a TokenMap<SpanData<Anchor, Ctx>>,
|
|
||||||
) -> Self {
|
|
||||||
let range = node.text_range();
|
let range = node.text_range();
|
||||||
let mut preorder = node.preorder_with_tokens();
|
let mut preorder = node.preorder_with_tokens();
|
||||||
let first = Self::next_token(&mut preorder, &censored);
|
let first = Self::next_token(&mut preorder, &censored);
|
||||||
Converter {
|
Converter { current: first, preorder, range, punct_offset: None, censored, map }
|
||||||
current: first,
|
|
||||||
preorder,
|
|
||||||
range,
|
|
||||||
punct_offset: None,
|
|
||||||
offset: anchor_offset,
|
|
||||||
censored,
|
|
||||||
map,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn next_token(preorder: &mut PreorderWithTokens, censor: &[SyntaxNode]) -> Option<SyntaxToken> {
|
fn next_token(preorder: &mut PreorderWithTokens, censor: &[SyntaxNode]) -> Option<SyntaxToken> {
|
||||||
|
@ -577,29 +549,30 @@ impl SynToken {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Anchor, Ctx> SrcToken<Converter<'_, Anchor, Ctx>> for SynToken {
|
impl<SpanMap> SrcToken<Converter<SpanMap>> for SynToken {
|
||||||
fn kind(&self, ctx: &Converter<'_, Anchor, Ctx>) -> SyntaxKind {
|
fn kind(&self, ctx: &Converter<SpanMap>) -> SyntaxKind {
|
||||||
match self {
|
match self {
|
||||||
SynToken::Ordinary(token) => token.kind(),
|
SynToken::Ordinary(token) => token.kind(),
|
||||||
SynToken::Punct(..) => SyntaxKind::from_char(self.to_char(ctx).unwrap()).unwrap(),
|
SynToken::Punct(..) => SyntaxKind::from_char(self.to_char(ctx).unwrap()).unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn to_char(&self, _ctx: &Converter<'_, Anchor, Ctx>) -> Option<char> {
|
fn to_char(&self, _ctx: &Converter<SpanMap>) -> Option<char> {
|
||||||
match self {
|
match self {
|
||||||
SynToken::Ordinary(_) => None,
|
SynToken::Ordinary(_) => None,
|
||||||
SynToken::Punct(it, i) => it.text().chars().nth(*i),
|
SynToken::Punct(it, i) => it.text().chars().nth(*i),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fn to_text(&self, _ctx: &Converter<'_, Anchor, Ctx>) -> SmolStr {
|
fn to_text(&self, _ctx: &Converter<SpanMap>) -> SmolStr {
|
||||||
match self {
|
match self {
|
||||||
SynToken::Ordinary(token) | SynToken::Punct(token, _) => token.text().into(),
|
SynToken::Ordinary(token) | SynToken::Punct(token, _) => token.text().into(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Anchor: Copy, Ctx> TokenConverter<Anchor, Ctx> for Converter<'_, Anchor, Ctx>
|
impl<Anchor: Copy, Ctx, SpanMap> TokenConverter<Anchor, Ctx> for Converter<SpanMap>
|
||||||
where
|
where
|
||||||
SpanData<Anchor, Ctx>: Span,
|
SpanData<Anchor, Ctx>: Span,
|
||||||
|
SpanMap: SpanMapper<SpanData<Anchor, Ctx>>,
|
||||||
{
|
{
|
||||||
type Token = SynToken;
|
type Token = SynToken;
|
||||||
fn convert_doc_comment(
|
fn convert_doc_comment(
|
||||||
|
@ -610,18 +583,14 @@ where
|
||||||
convert_doc_comment(token.token(), span)
|
convert_doc_comment(token.token(), span)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn bump(&mut self) -> Option<(Self::Token, TextRange, TextRange)> {
|
fn bump(&mut self) -> Option<(Self::Token, TextRange)> {
|
||||||
if let Some((punct, offset)) = self.punct_offset.clone() {
|
if let Some((punct, offset)) = self.punct_offset.clone() {
|
||||||
if usize::from(offset) + 1 < punct.text().len() {
|
if usize::from(offset) + 1 < punct.text().len() {
|
||||||
let offset = offset + TextSize::of('.');
|
let offset = offset + TextSize::of('.');
|
||||||
let range = punct.text_range();
|
let range = punct.text_range();
|
||||||
self.punct_offset = Some((punct.clone(), offset));
|
self.punct_offset = Some((punct.clone(), offset));
|
||||||
let range = TextRange::at(range.start() + offset, TextSize::of('.'));
|
let range = TextRange::at(range.start() + offset, TextSize::of('.'));
|
||||||
return Some((
|
return Some((SynToken::Punct(punct, u32::from(offset) as usize), range));
|
||||||
SynToken::Punct(punct, u32::from(offset) as usize),
|
|
||||||
range - self.offset,
|
|
||||||
range,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -634,11 +603,11 @@ where
|
||||||
self.punct_offset = Some((curr.clone(), 0.into()));
|
self.punct_offset = Some((curr.clone(), 0.into()));
|
||||||
let range = curr.text_range();
|
let range = curr.text_range();
|
||||||
let range = TextRange::at(range.start(), TextSize::of('.'));
|
let range = TextRange::at(range.start(), TextSize::of('.'));
|
||||||
(SynToken::Punct(curr, 0 as usize), range - self.offset, range)
|
(SynToken::Punct(curr, 0 as usize), range)
|
||||||
} else {
|
} else {
|
||||||
self.punct_offset = None;
|
self.punct_offset = None;
|
||||||
let range = curr.text_range();
|
let range = curr.text_range();
|
||||||
(SynToken::Ordinary(curr), range - self.offset, range)
|
(SynToken::Ordinary(curr), range)
|
||||||
};
|
};
|
||||||
|
|
||||||
Some(token)
|
Some(token)
|
||||||
|
@ -665,12 +634,15 @@ where
|
||||||
Some(token)
|
Some(token)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn span_for(&self, range: TextRange) -> Option<SpanData<Anchor, Ctx>> {
|
fn span_for(&self, range: TextRange) -> SpanData<Anchor, Ctx> {
|
||||||
self.map.span_for_range(range)
|
self.map.span_for(range)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct TtTreeSink<'a, Anchor, Ctx> {
|
struct TtTreeSink<'a, Anchor, Ctx>
|
||||||
|
where
|
||||||
|
SpanData<Anchor, Ctx>: Span,
|
||||||
|
{
|
||||||
buf: String,
|
buf: String,
|
||||||
cursor: Cursor<'a, SpanData<Anchor, Ctx>>,
|
cursor: Cursor<'a, SpanData<Anchor, Ctx>>,
|
||||||
text_pos: TextSize,
|
text_pos: TextSize,
|
||||||
|
@ -688,12 +660,12 @@ where
|
||||||
cursor,
|
cursor,
|
||||||
text_pos: 0.into(),
|
text_pos: 0.into(),
|
||||||
inner: SyntaxTreeBuilder::default(),
|
inner: SyntaxTreeBuilder::default(),
|
||||||
token_map: TokenMap::default(),
|
token_map: TokenMap::empty(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn finish(mut self) -> (Parse<SyntaxNode>, TokenMap<SpanData<Anchor, Ctx>>) {
|
fn finish(mut self) -> (Parse<SyntaxNode>, TokenMap<SpanData<Anchor, Ctx>>) {
|
||||||
self.token_map.shrink_to_fit();
|
self.token_map.finish();
|
||||||
(self.inner.finish(), self.token_map)
|
(self.inner.finish(), self.token_map)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -825,6 +797,7 @@ where
|
||||||
|
|
||||||
self.inner.token(kind, self.buf.as_str());
|
self.inner.token(kind, self.buf.as_str());
|
||||||
self.buf.clear();
|
self.buf.clear();
|
||||||
|
// FIXME: Emitting whitespace for this is really just a hack, we should get rid of it.
|
||||||
// Add whitespace between adjoint puncts
|
// Add whitespace between adjoint puncts
|
||||||
let next = last.bump();
|
let next = last.bump();
|
||||||
if let (
|
if let (
|
||||||
|
@ -839,6 +812,7 @@ where
|
||||||
// need to add whitespace either.
|
// need to add whitespace either.
|
||||||
if curr.spacing == tt::Spacing::Alone && curr.char != ';' && next.char != '\'' {
|
if curr.spacing == tt::Spacing::Alone && curr.char != ';' && next.char != '\'' {
|
||||||
self.inner.token(WHITESPACE, " ");
|
self.inner.token(WHITESPACE, " ");
|
||||||
|
self.token_map.insert(TextRange::at(self.text_pos, TextSize::of(' ')), curr.span);
|
||||||
self.text_pos += TextSize::of(' ');
|
self.text_pos += TextSize::of(' ');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,12 +7,14 @@ use tt::{
|
||||||
Leaf, Punct, Spacing, SpanAnchor, SyntaxContext,
|
Leaf, Punct, Spacing, SpanAnchor, SyntaxContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::SpanMapper;
|
||||||
|
|
||||||
use super::syntax_node_to_token_tree;
|
use super::syntax_node_to_token_tree;
|
||||||
|
|
||||||
fn check_punct_spacing(fixture: &str) {
|
fn check_punct_spacing(fixture: &str) {
|
||||||
type SpanData = tt::SpanData<DummyFile, DummyCtx>;
|
type SpanData = tt::SpanData<DummyFile, DummyCtx>;
|
||||||
|
|
||||||
#[derive(PartialEq, Eq, Clone, Copy, Debug)]
|
#[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)]
|
||||||
struct DummyFile;
|
struct DummyFile;
|
||||||
impl SpanAnchor for DummyFile {
|
impl SpanAnchor for DummyFile {
|
||||||
const DUMMY: Self = DummyFile;
|
const DUMMY: Self = DummyFile;
|
||||||
|
@ -24,9 +26,16 @@ fn check_punct_spacing(fixture: &str) {
|
||||||
const DUMMY: Self = DummyCtx;
|
const DUMMY: Self = DummyCtx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct NoOpMap;
|
||||||
|
|
||||||
|
impl SpanMapper<SpanData> for NoOpMap {
|
||||||
|
fn span_for(&self, range: syntax::TextRange) -> SpanData {
|
||||||
|
SpanData { range, anchor: DummyFile, ctx: DummyCtx }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let source_file = ast::SourceFile::parse(fixture).ok().unwrap();
|
let source_file = ast::SourceFile::parse(fixture).ok().unwrap();
|
||||||
let subtree =
|
let subtree = syntax_node_to_token_tree(source_file.syntax(), NoOpMap);
|
||||||
syntax_node_to_token_tree(source_file.syntax(), DummyFile, 0.into(), &Default::default());
|
|
||||||
let mut annotations: HashMap<_, _> = extract_annotations(fixture)
|
let mut annotations: HashMap<_, _> = extract_annotations(fixture)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(range, annotation)| {
|
.map(|(range, annotation)| {
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
use std::hash::Hash;
|
use std::hash::Hash;
|
||||||
|
|
||||||
use stdx::never;
|
use stdx::itertools::Itertools;
|
||||||
use syntax::TextRange;
|
use syntax::TextRange;
|
||||||
use tt::Span;
|
use tt::Span;
|
||||||
|
|
||||||
|
@ -15,23 +15,29 @@ use tt::Span;
|
||||||
/// Maps absolute text ranges for the corresponding file to the relevant span data.
|
/// Maps absolute text ranges for the corresponding file to the relevant span data.
|
||||||
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
#[derive(Debug, PartialEq, Eq, Clone, Hash)]
|
||||||
// FIXME: Rename to SpanMap
|
// FIXME: Rename to SpanMap
|
||||||
pub struct TokenMap<S> {
|
pub struct TokenMap<S: Span> {
|
||||||
// FIXME: This needs to be sorted by (FileId, AstId)
|
// FIXME: This needs to be sorted by (FileId, AstId)
|
||||||
// Then we can do a binary search on the file id,
|
// Then we can do a binary search on the file id,
|
||||||
// then a bin search on the ast id
|
// then a bin search on the ast id
|
||||||
pub span_map: Vec<(TextRange, S)>,
|
pub span_map: Vec<(TextRange, S)>,
|
||||||
// span_map2: rustc_hash::FxHashMap<TextRange, usize>,
|
// span_map2: rustc_hash::FxHashMap<TextRange, usize>,
|
||||||
pub real_file: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<S> Default for TokenMap<S> {
|
|
||||||
fn default() -> Self {
|
|
||||||
Self { span_map: Vec::new(), real_file: true }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<S: Span> TokenMap<S> {
|
impl<S: Span> TokenMap<S> {
|
||||||
pub(crate) fn shrink_to_fit(&mut self) {
|
pub fn empty() -> Self {
|
||||||
|
Self { span_map: Vec::new() }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finish(&mut self) {
|
||||||
|
debug_assert_eq!(
|
||||||
|
self.span_map
|
||||||
|
.iter()
|
||||||
|
.sorted_by_key(|it| (it.0.start(), it.0.end()))
|
||||||
|
.tuple_windows()
|
||||||
|
.find(|(range, next)| range.0.end() != next.0.start()),
|
||||||
|
None,
|
||||||
|
"span map has holes!"
|
||||||
|
);
|
||||||
self.span_map.shrink_to_fit();
|
self.span_map.shrink_to_fit();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -40,6 +46,8 @@ impl<S: Span> TokenMap<S> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn ranges_with_span(&self, span: S) -> impl Iterator<Item = TextRange> + '_ {
|
pub fn ranges_with_span(&self, span: S) -> impl Iterator<Item = TextRange> + '_ {
|
||||||
|
// FIXME: linear search
|
||||||
|
// FIXME: Disregards resolving spans to get more matches! See ExpansionInfo::map_token_down
|
||||||
self.span_map.iter().filter_map(
|
self.span_map.iter().filter_map(
|
||||||
move |(range, s)| {
|
move |(range, s)| {
|
||||||
if s == &span {
|
if s == &span {
|
||||||
|
@ -51,20 +59,31 @@ impl<S: Span> TokenMap<S> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Should be infallible
|
// FIXME: We need APIs for fetching the span of a token as well as for a whole node. The node
|
||||||
pub fn span_for_range(&self, range: TextRange) -> Option<S> {
|
// one *is* fallible though.
|
||||||
|
// Token span fetching technically only needs an offset really, as the entire file span is
|
||||||
|
// populated, where node fetching is more like fetching the spans at all source positions, and
|
||||||
|
// then we need to verify that all those positions have the same context, if not we fail! But
|
||||||
|
// how do we handle them having different span ranges?
|
||||||
|
|
||||||
|
pub fn span_for_range(&self, range: TextRange) -> S {
|
||||||
// TODO FIXME: make this proper
|
// TODO FIXME: make this proper
|
||||||
self.span_map
|
self.span_map
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|(r, s)| Some((r, s, r.intersect(range)?)))
|
.filter_map(|(r, s)| Some((r, s, r.intersect(range).filter(|it| !it.is_empty())?)))
|
||||||
.max_by_key(|(_, _, intersection)| intersection.len())
|
.max_by_key(|(_, _, intersection)| intersection.len())
|
||||||
.map(|(_, &s, _)| s)
|
.map_or_else(
|
||||||
.or_else(|| {
|
|| panic!("no span for range {:?} in {:#?}", range, self.span_map),
|
||||||
if !self.real_file {
|
|(_, &s, _)| s,
|
||||||
never!("no span for range {:?} in {:#?}", range, self.span_map);
|
)
|
||||||
}
|
}
|
||||||
None
|
|
||||||
})
|
pub fn spans_for_node_range(&self, range: TextRange) -> impl Iterator<Item = S> + '_ {
|
||||||
|
// TODO FIXME: make this proper
|
||||||
|
self.span_map
|
||||||
|
.iter()
|
||||||
|
.filter(move |(r, _)| r.intersect(range).filter(|it| !it.is_empty()).is_some())
|
||||||
|
.map(|&(_, s)| s)
|
||||||
}
|
}
|
||||||
|
|
||||||
// pub fn ranges_by_token(
|
// pub fn ranges_by_token(
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
//! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable`
|
//! * By **copying** the whole rustc `lib_proc_macro` code, we are able to build this with `stable`
|
||||||
//! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)…
|
//! rustc rather than `unstable`. (Although in general ABI compatibility is still an issue)…
|
||||||
|
|
||||||
#![cfg(feature = "sysroot-abi")]
|
#![cfg(any(feature = "sysroot-abi", rust_analyzer))]
|
||||||
#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)]
|
#![feature(proc_macro_internals, proc_macro_diagnostic, proc_macro_span)]
|
||||||
#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
|
#![warn(rust_2018_idioms, unused_lifetimes, semicolon_in_expressions_from_macros)]
|
||||||
#![allow(unreachable_pub)]
|
#![allow(unreachable_pub)]
|
||||||
|
|
|
@ -209,24 +209,26 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
use cfg::CfgExpr;
|
use cfg::CfgExpr;
|
||||||
use hir::HirFileId;
|
use hir_def::tt::{self, Span};
|
||||||
use ide_db::base_db::span::{SpanAnchor, SyntaxContextId, ROOT_ERASED_FILE_AST_ID};
|
use mbe::{syntax_node_to_token_tree, SpanMapper};
|
||||||
use mbe::syntax_node_to_token_tree;
|
|
||||||
use syntax::{
|
use syntax::{
|
||||||
ast::{self, AstNode},
|
ast::{self, AstNode},
|
||||||
SmolStr, TextSize,
|
SmolStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct NoOpMap;
|
||||||
|
|
||||||
|
impl SpanMapper<tt::SpanData> for NoOpMap {
|
||||||
|
fn span_for(&self, _: syntax::TextRange) -> tt::SpanData {
|
||||||
|
tt::SpanData::DUMMY
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn check(cfg: &str, expected_features: &[&str]) {
|
fn check(cfg: &str, expected_features: &[&str]) {
|
||||||
let cfg_expr = {
|
let cfg_expr = {
|
||||||
let source_file = ast::SourceFile::parse(cfg).ok().unwrap();
|
let source_file = ast::SourceFile::parse(cfg).ok().unwrap();
|
||||||
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
let tt = source_file.syntax().descendants().find_map(ast::TokenTree::cast).unwrap();
|
||||||
let tt = syntax_node_to_token_tree::<_, SyntaxContextId>(
|
let tt = syntax_node_to_token_tree(tt.syntax(), &NoOpMap);
|
||||||
tt.syntax(),
|
|
||||||
SpanAnchor { file_id: HirFileId::from(0), ast_id: ROOT_ERASED_FILE_AST_ID },
|
|
||||||
TextSize::new(0),
|
|
||||||
&Default::default(),
|
|
||||||
);
|
|
||||||
CfgExpr::parse(&tt)
|
CfgExpr::parse(&tt)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ always-assert = { version = "0.1.2", features = ["log"] }
|
||||||
jod-thread = "0.1.2"
|
jod-thread = "0.1.2"
|
||||||
libc.workspace = true
|
libc.workspace = true
|
||||||
crossbeam-channel = "0.5.5"
|
crossbeam-channel = "0.5.5"
|
||||||
|
itertools.workspace = true
|
||||||
# Think twice before adding anything here
|
# Think twice before adding anything here
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
|
|
@ -15,6 +15,7 @@ pub mod thread;
|
||||||
pub mod anymap;
|
pub mod anymap;
|
||||||
|
|
||||||
pub use always_assert::{always, never};
|
pub use always_assert::{always, never};
|
||||||
|
pub use itertools;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_ci() -> bool {
|
pub fn is_ci() -> bool {
|
||||||
|
@ -40,6 +41,24 @@ Uncomment `default = [ "backtrace" ]` in `crates/stdx/Cargo.toml`.
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait TupleExt {
|
||||||
|
type Head;
|
||||||
|
type Tail;
|
||||||
|
fn head(self) -> Self::Head;
|
||||||
|
fn tail(self) -> Self::Tail;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<T, U> TupleExt for (T, U) {
|
||||||
|
type Head = T;
|
||||||
|
type Tail = U;
|
||||||
|
fn head(self) -> Self::Head {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
fn tail(self) -> Self::Tail {
|
||||||
|
self.1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn to_lower_snake_case(s: &str) -> String {
|
pub fn to_lower_snake_case(s: &str) -> String {
|
||||||
to_snake_case(s, char::to_lowercase)
|
to_snake_case(s, char::to_lowercase)
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,20 +45,32 @@ pub struct SpanData<Anchor, Ctx> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<Anchor: SpanAnchor, Ctx: SyntaxContext> Span for SpanData<Anchor, Ctx> {
|
impl<Anchor: SpanAnchor, Ctx: SyntaxContext> Span for SpanData<Anchor, Ctx> {
|
||||||
|
type Anchor = Anchor;
|
||||||
const DUMMY: Self = SpanData {
|
const DUMMY: Self = SpanData {
|
||||||
range: TextRange::empty(TextSize::new(0)),
|
range: TextRange::empty(TextSize::new(0)),
|
||||||
anchor: Anchor::DUMMY,
|
anchor: Anchor::DUMMY,
|
||||||
ctx: Ctx::DUMMY,
|
ctx: Ctx::DUMMY,
|
||||||
};
|
};
|
||||||
|
fn anchor(self) -> Self::Anchor {
|
||||||
|
self.anchor
|
||||||
|
}
|
||||||
|
fn mk(anchor: Self::Anchor, range: TextRange) -> Self {
|
||||||
|
SpanData { anchor, range, ctx: Ctx::DUMMY }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SpanAnchor: std::fmt::Debug + Copy + Sized + Eq {
|
pub trait SpanAnchor:
|
||||||
|
std::fmt::Debug + Copy + Sized + Eq + Copy + fmt::Debug + std::hash::Hash
|
||||||
|
{
|
||||||
const DUMMY: Self;
|
const DUMMY: Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Get rid of this trait?
|
// FIXME: Get rid of this trait?
|
||||||
pub trait Span: std::fmt::Debug + Copy + Sized + Eq {
|
pub trait Span: std::fmt::Debug + Copy + Sized + Eq {
|
||||||
const DUMMY: Self;
|
const DUMMY: Self;
|
||||||
|
type Anchor: Copy + fmt::Debug + Eq + std::hash::Hash;
|
||||||
|
fn anchor(self) -> Self::Anchor;
|
||||||
|
fn mk(anchor: Self::Anchor, range: TextRange) -> Self;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait SyntaxContext: std::fmt::Debug + Copy + Sized + Eq {
|
pub trait SyntaxContext: std::fmt::Debug + Copy + Sized + Eq {
|
||||||
|
|
Loading…
Reference in a new issue