Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-def/src/expr_store/lower.rs22
-rw-r--r--crates/hir-def/src/item_tree/pretty.rs6
-rw-r--r--crates/hir-def/src/item_tree/tests.rs64
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe.rs22
-rw-r--r--crates/hir-def/src/macro_expansion_tests/proc_macros.rs6
-rw-r--r--crates/hir-expand/src/builtin/quote.rs4
-rw-r--r--crates/hir-expand/src/files.rs4
-rw-r--r--crates/mbe/src/tests.rs232
-rw-r--r--crates/proc-macro-api/src/legacy_protocol/msg.rs13
-rw-r--r--crates/proc-macro-api/src/process.rs14
-rw-r--r--crates/proc-macro-srv-cli/src/main_loop.rs7
-rw-r--r--crates/proc-macro-srv/src/dylib.rs15
-rw-r--r--crates/proc-macro-srv/src/lib.rs32
-rw-r--r--crates/proc-macro-srv/src/proc_macros.rs8
-rw-r--r--crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs17
-rw-r--r--crates/proc-macro-srv/src/tests/mod.rs344
-rw-r--r--crates/proc-macro-srv/src/tests/utils.rs34
-rw-r--r--crates/span/Cargo.toml3
-rw-r--r--crates/span/src/ast_id.rs903
-rw-r--r--crates/span/src/lib.rs24
-rw-r--r--crates/span/src/map.rs2
-rw-r--r--crates/syntax/src/ast/node_ext.rs10
22 files changed, 1230 insertions, 556 deletions
diff --git a/crates/hir-def/src/expr_store/lower.rs b/crates/hir-def/src/expr_store/lower.rs
index 29871f5e04..b7a482a85d 100644
--- a/crates/hir-def/src/expr_store/lower.rs
+++ b/crates/hir-def/src/expr_store/lower.rs
@@ -2141,26 +2141,10 @@ impl ExprCollector<'_> {
block: ast::BlockExpr,
mk_block: impl FnOnce(Option<BlockId>, Box<[Statement]>, Option<ExprId>) -> Expr,
) -> ExprId {
- let block_has_items = {
- let statement_has_item = block.statements().any(|stmt| match stmt {
- ast::Stmt::Item(_) => true,
- // Macro calls can be both items and expressions. The syntax library always treats
- // them as expressions here, so we undo that.
- ast::Stmt::ExprStmt(es) => matches!(es.expr(), Some(ast::Expr::MacroExpr(_))),
- _ => false,
- });
- statement_has_item
- || matches!(block.tail_expr(), Some(ast::Expr::MacroExpr(_)))
- || (block.may_carry_attributes() && block.attrs().next().is_some())
- };
-
- let block_id = if block_has_items {
- let file_local_id = self.expander.ast_id_map().ast_id(&block);
+ let block_id = self.expander.ast_id_map().ast_id_for_block(&block).map(|file_local_id| {
let ast_id = self.expander.in_file(file_local_id);
- Some(self.db.intern_block(BlockLoc { ast_id, module: self.module }))
- } else {
- None
- };
+ self.db.intern_block(BlockLoc { ast_id, module: self.module })
+ });
let (module, def_map) =
match block_id.map(|block_id| (block_def_map(self.db, block_id), block_id)) {
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs
index 47c6eb1329..51172c0a1e 100644
--- a/crates/hir-def/src/item_tree/pretty.rs
+++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -353,8 +353,8 @@ impl Printer<'_> {
let MacroCall { path, ast_id, expand_to, ctxt } = &self.tree[it];
let _ = writeln!(
self,
- "// AstId: {:?}, SyntaxContextId: {}, ExpandTo: {:?}",
- ast_id.erase().into_raw(),
+ "// AstId: {:#?}, SyntaxContextId: {}, ExpandTo: {:?}",
+ ast_id.erase(),
ctxt,
expand_to
);
@@ -377,7 +377,7 @@ impl Printer<'_> {
}
fn print_ast_id(&mut self, ast_id: ErasedFileAstId) {
- wln!(self, "// AstId: {:?}", ast_id.into_raw());
+ wln!(self, "// AstId: {ast_id:#?}");
}
}
diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs
index 824fbfa592..abfff283ef 100644
--- a/crates/hir-def/src/item_tree/tests.rs
+++ b/crates/hir-def/src/item_tree/tests.rs
@@ -35,23 +35,23 @@ use a::{c, d::{e}};
#![no_std]
#![doc = " another file comment"]
- // AstId: 1
+ // AstId: ExternCrate[5A82, 0]
pub(self) extern crate self as renamed;
- // AstId: 2
+ // AstId: ExternCrate[7E1C, 0]
pub(super) extern crate bli;
- // AstId: 3
+ // AstId: Use[0000, 0]
pub use crate::path::{nested, items as renamed, Trait as _};
- // AstId: 4
+ // AstId: Use[0000, 1]
pub(self) use globs::*;
#[doc = " docs on import"]
- // AstId: 5
+ // AstId: Use[0000, 2]
pub(self) use crate::{A, B};
- // AstId: 6
+ // AstId: Use[0000, 3]
pub(self) use a::{c, d::{e}};
"##]],
);
@@ -75,18 +75,18 @@ extern "C" {
"#,
expect![[r##"
#[on_extern_block]
- // AstId: 1
+ // AstId: ExternBlock[0000, 0]
extern "C" {
#[on_extern_type]
- // AstId: 2
+ // AstId: TypeAlias[9FDF, 0]
pub(self) type ExType;
#[on_extern_static]
- // AstId: 3
+ // AstId: Static[43C1, 0]
pub(self) static EX_STATIC = _;
#[on_extern_fn]
- // AstId: 4
+ // AstId: Fn[452D, 0]
pub(self) fn ex_fn;
}
"##]],
@@ -124,39 +124,39 @@ enum E {
}
"#,
expect![[r#"
- // AstId: 1
+ // AstId: Struct[DFF3, 0]
pub(self) struct Unit;
#[derive(Debug)]
- // AstId: 2
+ // AstId: Struct[C7A1, 0]
pub(self) struct Struct {
#[doc = " fld docs"]
pub(self) fld,
}
- // AstId: 3
+ // AstId: Struct[DAC2, 0]
pub(self) struct Tuple(
#[attr]
pub(self) 0,
);
- // AstId: 4
+ // AstId: Union[2DBB, 0]
pub(self) union Ize {
pub(self) a,
pub(self) b,
}
- // AstId: 5
+ // AstId: Enum[7FF8, 0]
pub(self) enum E
- // AstId: 6
+ // AstId: Variant[C717, 0]
#[doc = " comment on Unit"]
Unit,
- // AstId: 7
+ // AstId: Variant[AEAB, 0]
#[doc = " comment on Tuple"]
Tuple(
pub(self) 0,
),
- // AstId: 8
+ // AstId: Variant[4B1B, 0]
Struct {
#[doc = " comment on a: u8"]
pub(self) a,
@@ -185,23 +185,23 @@ trait Tr: SuperTrait + 'lifetime {
}
"#,
expect![[r#"
- // AstId: 1
+ // AstId: Static[B393, 0]
pub static ST = _;
- // AstId: 2
+ // AstId: Const[B309, 0]
pub(self) const _ = _;
#[attr]
#[inner_attr_in_fn]
- // AstId: 3
+ // AstId: Fn[75E3, 0]
pub(self) fn f;
- // AstId: 4
+ // AstId: Trait[2998, 0]
pub(self) trait Tr {
- // AstId: 6
+ // AstId: TypeAlias[9F08, 0]
pub(self) type Assoc;
- // AstId: 7
+ // AstId: Fn[6C0C, 0]
pub(self) fn method;
}
"#]],
@@ -226,16 +226,16 @@ mod outline;
expect![[r##"
#[doc = " outer"]
#[doc = " inner"]
- // AstId: 1
+ // AstId: Module[CF93, 0]
pub(self) mod inline {
- // AstId: 3
+ // AstId: Use[0000, 0]
pub(self) use super::*;
- // AstId: 4
+ // AstId: Fn[1B26, 0]
pub(self) fn fn_in_module;
}
- // AstId: 2
+ // AstId: Module[8994, 0]
pub(self) mod outline;
"##]],
);
@@ -254,13 +254,13 @@ pub macro m2() {}
m!();
"#,
expect![[r#"
- // AstId: 1
+ // AstId: MacroRules[88CE, 0]
macro_rules! m { ... }
- // AstId: 2
+ // AstId: MacroDef[DC34, 0]
pub macro m2 { ... }
- // AstId: 3, SyntaxContextId: ROOT2024, ExpandTo: Items
+ // AstId: MacroCall[612F, 0], SyntaxContextId: ROOT2024, ExpandTo: Items
m!(...);
"#]],
);
@@ -273,7 +273,7 @@ fn pub_self() {
pub(self) struct S;
"#,
expect![[r#"
- // AstId: 1
+ // AstId: Struct[42E2, 0]
pub(self) struct S;
"#]],
)
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index 38fc4b3d11..eea50d16f5 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -35,9 +35,9 @@ macro_rules! f {
};
}
-struct#0:[email protected]#14336# MyTraitMap2#0:[email protected]#ROOT2024# {#0:[email protected]#14336#
- map#0:[email protected]#14336#:#0:[email protected]#14336# #0:[email protected]#14336#::#0:[email protected]#14336#std#0:[email protected]#14336#::#0:[email protected]#14336#collections#0:[email protected]#14336#::#0:[email protected]#14336#HashSet#0:[email protected]#14336#<#0:[email protected]#14336#(#0:[email protected]#14336#)#0:[email protected]#14336#>#0:[email protected]#14336#,#0:[email protected]#14336#
-}#0:[email protected]#14336#
+struct#0:MacroRules[8C8E, 0]@58..64#14336# MyTraitMap2#0:MacroCall[D499, 0]@31..42#ROOT2024# {#0:MacroRules[8C8E, 0]@72..73#14336#
+ map#0:MacroRules[8C8E, 0]@86..89#14336#:#0:MacroRules[8C8E, 0]@89..90#14336# #0:MacroRules[8C8E, 0]@89..90#14336#::#0:MacroRules[8C8E, 0]@91..93#14336#std#0:MacroRules[8C8E, 0]@93..96#14336#::#0:MacroRules[8C8E, 0]@96..98#14336#collections#0:MacroRules[8C8E, 0]@98..109#14336#::#0:MacroRules[8C8E, 0]@109..111#14336#HashSet#0:MacroRules[8C8E, 0]@111..118#14336#<#0:MacroRules[8C8E, 0]@118..119#14336#(#0:MacroRules[8C8E, 0]@119..120#14336#)#0:MacroRules[8C8E, 0]@120..121#14336#>#0:MacroRules[8C8E, 0]@121..122#14336#,#0:MacroRules[8C8E, 0]@122..123#14336#
+}#0:MacroRules[8C8E, 0]@132..133#14336#
"#]],
);
}
@@ -75,12 +75,12 @@ macro_rules! f {
};
}
-fn#0:[email protected]#ROOT2024# main#0:[email protected]#ROOT2024#(#0:[email protected]#ROOT2024#)#0:[email protected]#ROOT2024# {#0:[email protected]#ROOT2024#
- 1#0:[email protected]#ROOT2024#;#0:[email protected]#ROOT2024#
- 1.0#0:[email protected]#ROOT2024#;#0:[email protected]#ROOT2024#
- (#0:[email protected]#ROOT2024#(#0:[email protected]#ROOT2024#1#0:[email protected]#ROOT2024#,#0:[email protected]#ROOT2024# )#0:[email protected]#ROOT2024#,#0:[email protected]#ROOT2024# )#0:[email protected]#ROOT2024#.#0:[email protected]#ROOT2024#0#0:[email protected]#ROOT2024#.#0:[email protected]#ROOT2024#0#0:[email protected]#ROOT2024#;#0:[email protected]#ROOT2024#
- let#0:[email protected]#ROOT2024# x#0:[email protected]#ROOT2024# =#0:[email protected]#ROOT2024# 1#0:[email protected]#ROOT2024#;#0:[email protected]#ROOT2024#
-}#0:[email protected]#ROOT2024#
+fn#0:MacroCall[D499, 0]@30..32#ROOT2024# main#0:MacroCall[D499, 0]@33..37#ROOT2024#(#0:MacroCall[D499, 0]@37..38#ROOT2024#)#0:MacroCall[D499, 0]@38..39#ROOT2024# {#0:MacroCall[D499, 0]@40..41#ROOT2024#
+ 1#0:MacroCall[D499, 0]@50..51#ROOT2024#;#0:MacroCall[D499, 0]@51..52#ROOT2024#
+ 1.0#0:MacroCall[D499, 0]@61..64#ROOT2024#;#0:MacroCall[D499, 0]@64..65#ROOT2024#
+ (#0:MacroCall[D499, 0]@74..75#ROOT2024#(#0:MacroCall[D499, 0]@75..76#ROOT2024#1#0:MacroCall[D499, 0]@76..77#ROOT2024#,#0:MacroCall[D499, 0]@77..78#ROOT2024# )#0:MacroCall[D499, 0]@78..79#ROOT2024#,#0:MacroCall[D499, 0]@79..80#ROOT2024# )#0:MacroCall[D499, 0]@80..81#ROOT2024#.#0:MacroCall[D499, 0]@81..82#ROOT2024#0#0:MacroCall[D499, 0]@82..85#ROOT2024#.#0:MacroCall[D499, 0]@82..85#ROOT2024#0#0:MacroCall[D499, 0]@82..85#ROOT2024#;#0:MacroCall[D499, 0]@85..86#ROOT2024#
+ let#0:MacroCall[D499, 0]@95..98#ROOT2024# x#0:MacroCall[D499, 0]@99..100#ROOT2024# =#0:MacroCall[D499, 0]@101..102#ROOT2024# 1#0:MacroCall[D499, 0]@103..104#ROOT2024#;#0:MacroCall[D499, 0]@104..105#ROOT2024#
+}#0:MacroCall[D499, 0]@110..111#ROOT2024#
"#]],
@@ -171,7 +171,7 @@ fn main(foo: ()) {
}
fn main(foo: ()) {
- /* error: unresolved macro unresolved */"helloworld!"#0:[email protected]#ROOT2024#;
+ /* error: unresolved macro unresolved */"helloworld!"#0:Fn[B9C7, 0]@236..321#ROOT2024#;
}
}
@@ -197,7 +197,7 @@ macro_rules! mk_struct {
#[macro_use]
mod foo;
-struct#1:[email protected]#14336# Foo#0:[email protected]#ROOT2024#(#1:[email protected]#14336#u32#0:[email protected]#ROOT2024#)#1:[email protected]#14336#;#1:[email protected]#14336#
+struct#1:MacroRules[E572, 0]@59..65#14336# Foo#0:MacroCall[BDD3, 0]@32..35#ROOT2024#(#1:MacroRules[E572, 0]@70..71#14336#u32#0:MacroCall[BDD3, 0]@41..44#ROOT2024#)#1:MacroRules[E572, 0]@74..75#14336#;#1:MacroRules[E572, 0]@75..76#14336#
"#]],
);
}
diff --git a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
index b2e1adc365..d5ae6f8d88 100644
--- a/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
+++ b/crates/hir-def/src/macro_expansion_tests/proc_macros.rs
@@ -181,9 +181,9 @@ fn foo(&self) {
self.0. 1;
}
-fn#0:[email protected]#ROOT2024# foo#0:[email protected]#ROOT2024#(#0:[email protected]#ROOT2024#&#0:[email protected]#ROOT2024#self#0:[email protected]#ROOT2024# )#0:[email protected]#ROOT2024# {#0:[email protected]#ROOT2024#
- self#0:[email protected]#ROOT2024# .#0:[email protected]#ROOT2024#0#0:[email protected]#ROOT2024#.#0:[email protected]#ROOT2024#1#0:[email protected]#ROOT2024#;#0:[email protected]#ROOT2024#
-}#0:[email protected]#ROOT2024#"#]],
+fn#0:Fn[4D85, 0]@45..47#ROOT2024# foo#0:Fn[4D85, 0]@48..51#ROOT2024#(#0:Fn[4D85, 0]@51..52#ROOT2024#&#0:Fn[4D85, 0]@52..53#ROOT2024#self#0:Fn[4D85, 0]@53..57#ROOT2024# )#0:Fn[4D85, 0]@57..58#ROOT2024# {#0:Fn[4D85, 0]@59..60#ROOT2024#
+ self#0:Fn[4D85, 0]@65..69#ROOT2024# .#0:Fn[4D85, 0]@69..70#ROOT2024#0#0:Fn[4D85, 0]@70..71#ROOT2024#.#0:Fn[4D85, 0]@71..72#ROOT2024#1#0:Fn[4D85, 0]@73..74#ROOT2024#;#0:Fn[4D85, 0]@74..75#ROOT2024#
+}#0:Fn[4D85, 0]@76..77#ROOT2024#"#]],
);
}
diff --git a/crates/hir-expand/src/builtin/quote.rs b/crates/hir-expand/src/builtin/quote.rs
index 62b7b638e7..d5874f829b 100644
--- a/crates/hir-expand/src/builtin/quote.rs
+++ b/crates/hir-expand/src/builtin/quote.rs
@@ -277,8 +277,8 @@ mod tests {
assert_eq!(quoted.to_string(), "hello");
let t = format!("{quoted:#?}");
expect![[r#"
- SUBTREE $$ 937550:[email protected]#ROOT2024 937550:[email protected]#ROOT2024
- IDENT hello 937550:[email protected]#ROOT2024"#]]
+ SUBTREE $$ 937550:Root[0000, 0]@0..0#ROOT2024 937550:Root[0000, 0]@0..0#ROOT2024
+ IDENT hello 937550:Root[0000, 0]@0..0#ROOT2024"#]]
.assert_eq(&t);
}
diff --git a/crates/hir-expand/src/files.rs b/crates/hir-expand/src/files.rs
index 8024823cbc..e8f6b82434 100644
--- a/crates/hir-expand/src/files.rs
+++ b/crates/hir-expand/src/files.rs
@@ -2,7 +2,7 @@
use std::borrow::Borrow;
use either::Either;
-use span::{AstIdNode, ErasedFileAstId, FileAstId, FileId, SyntaxContext};
+use span::{ErasedFileAstId, FileAstId, FileId, SyntaxContext};
use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, SyntaxToken, TextRange, TextSize};
use crate::{
@@ -106,7 +106,7 @@ impl FileRange {
/// 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> {
+impl<N: AstNode> AstId<N> {
pub fn to_node(&self, db: &dyn ExpandDatabase) -> N {
self.to_ptr(db).to_node(&db.parse_or_expand(self.file_id))
}
diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs
index 769455faac..56034516ef 100644
--- a/crates/mbe/src/tests.rs
+++ b/crates/mbe/src/tests.rs
@@ -3,7 +3,9 @@
// FIXME: Move more of the nameres independent tests from
// crates\hir-def\src\macro_expansion_tests\mod.rs to this
use expect_test::expect;
-use span::{Edition, EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContext};
+use span::{
+ Edition, EditionedFileId, FileId, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext,
+};
use stdx::format_to;
use tt::{TextRange, TextSize};
@@ -24,7 +26,7 @@ fn check_(
def_edition,
SpanAnchor {
file_id: EditionedFileId::new(FileId::from_raw(0), def_edition),
- ast_id: ErasedFileAstId::from_raw(0),
+ ast_id: ROOT_ERASED_FILE_AST_ID,
},
SyntaxContext::root(Edition::CURRENT),
decl,
@@ -37,7 +39,7 @@ fn check_(
};
let call_anchor = SpanAnchor {
file_id: EditionedFileId::new(FileId::from_raw(1), call_edition),
- ast_id: ErasedFileAstId::from_raw(0),
+ ast_id: ROOT_ERASED_FILE_AST_ID,
};
let arg_tt = syntax_bridge::parse_to_token_tree(
call_edition,
@@ -110,8 +112,8 @@ fn unbalanced_brace() {
"#,
r#""#,
expect![[r#"
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- SUBTREE {} 0:[email protected]#ROOT2024 0:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..0#ROOT2024 1:Root[0000, 0]@0..0#ROOT2024
+ SUBTREE {} 0:Root[0000, 0]@9..10#ROOT2024 0:Root[0000, 0]@11..12#ROOT2024
{}"#]],
);
@@ -133,25 +135,25 @@ fn token_mapping_smoke_test() {
struct MyTraitMap2
"#,
expect![[r#"
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- IDENT struct 0:[email protected]#ROOT2024
- IDENT MyTraitMap2 1:[email protected]#ROOT2024
- SUBTREE {} 0:[email protected]#ROOT2024 0:[email protected]#ROOT2024
- IDENT map 0:[email protected]#ROOT2024
- PUNCH : [alone] 0:[email protected]#ROOT2024
- PUNCH : [joint] 0:[email protected]#ROOT2024
- PUNCH : [alone] 0:[email protected]#ROOT2024
- IDENT std 0:[email protected]#ROOT2024
- PUNCH : [joint] 0:[email protected]#ROOT2024
- PUNCH : [alone] 0:[email protected]#ROOT2024
- IDENT collections 0:[email protected]#ROOT2024
- PUNCH : [joint] 0:[email protected]#ROOT2024
- PUNCH : [alone] 0:[email protected]#ROOT2024
- IDENT HashSet 0:[email protected]#ROOT2024
- PUNCH < [alone] 0:[email protected]#ROOT2024
- SUBTREE () 0:[email protected]#ROOT2024 0:[email protected]#ROOT2024
- PUNCH > [joint] 0:[email protected]#ROOT2024
- PUNCH , [alone] 0:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..20#ROOT2024 1:Root[0000, 0]@0..20#ROOT2024
+ IDENT struct 0:Root[0000, 0]@34..40#ROOT2024
+ IDENT MyTraitMap2 1:Root[0000, 0]@8..19#ROOT2024
+ SUBTREE {} 0:Root[0000, 0]@48..49#ROOT2024 0:Root[0000, 0]@100..101#ROOT2024
+ IDENT map 0:Root[0000, 0]@58..61#ROOT2024
+ PUNCH : [alone] 0:Root[0000, 0]@61..62#ROOT2024
+ PUNCH : [joint] 0:Root[0000, 0]@63..64#ROOT2024
+ PUNCH : [alone] 0:Root[0000, 0]@64..65#ROOT2024
+ IDENT std 0:Root[0000, 0]@65..68#ROOT2024
+ PUNCH : [joint] 0:Root[0000, 0]@68..69#ROOT2024
+ PUNCH : [alone] 0:Root[0000, 0]@69..70#ROOT2024
+ IDENT collections 0:Root[0000, 0]@70..81#ROOT2024
+ PUNCH : [joint] 0:Root[0000, 0]@81..82#ROOT2024
+ PUNCH : [alone] 0:Root[0000, 0]@82..83#ROOT2024
+ IDENT HashSet 0:Root[0000, 0]@83..90#ROOT2024
+ PUNCH < [alone] 0:Root[0000, 0]@90..91#ROOT2024
+ SUBTREE () 0:Root[0000, 0]@91..92#ROOT2024 0:Root[0000, 0]@92..93#ROOT2024
+ PUNCH > [joint] 0:Root[0000, 0]@93..94#ROOT2024
+ PUNCH , [alone] 0:Root[0000, 0]@94..95#ROOT2024
struct MyTraitMap2 {
map: ::std::collections::HashSet<()>,
@@ -180,28 +182,28 @@ fn main() {
}
"#,
expect![[r#"
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- IDENT fn 1:[email protected]#ROOT2024
- IDENT main 1:[email protected]#ROOT2024
- SUBTREE () 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- SUBTREE {} 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- LITERAL Integer 1 1:[email protected]#ROOT2024
- PUNCH ; [alone] 1:[email protected]#ROOT2024
- LITERAL Float 1.0 1:[email protected]#ROOT2024
- PUNCH ; [alone] 1:[email protected]#ROOT2024
- SUBTREE () 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- SUBTREE () 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- LITERAL Integer 1 1:[email protected]#ROOT2024
- PUNCH , [alone] 1:[email protected]#ROOT2024
- PUNCH , [alone] 1:[email protected]#ROOT2024
- PUNCH . [alone] 1:[email protected]#ROOT2024
- LITERAL Float 0.0 1:[email protected]#ROOT2024
- PUNCH ; [alone] 1:[email protected]#ROOT2024
- IDENT let 1:[email protected]#ROOT2024
- IDENT x 1:[email protected]#ROOT2024
- PUNCH = [alone] 1:[email protected]#ROOT2024
- LITERAL Integer 1 1:[email protected]#ROOT2024
- PUNCH ; [alone] 1:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..63#ROOT2024 1:Root[0000, 0]@0..63#ROOT2024
+ IDENT fn 1:Root[0000, 0]@1..3#ROOT2024
+ IDENT main 1:Root[0000, 0]@4..8#ROOT2024
+ SUBTREE () 1:Root[0000, 0]@8..9#ROOT2024 1:Root[0000, 0]@9..10#ROOT2024
+ SUBTREE {} 1:Root[0000, 0]@11..12#ROOT2024 1:Root[0000, 0]@61..62#ROOT2024
+ LITERAL Integer 1 1:Root[0000, 0]@17..18#ROOT2024
+ PUNCH ; [alone] 1:Root[0000, 0]@18..19#ROOT2024
+ LITERAL Float 1.0 1:Root[0000, 0]@24..27#ROOT2024
+ PUNCH ; [alone] 1:Root[0000, 0]@27..28#ROOT2024
+ SUBTREE () 1:Root[0000, 0]@33..34#ROOT2024 1:Root[0000, 0]@39..40#ROOT2024
+ SUBTREE () 1:Root[0000, 0]@34..35#ROOT2024 1:Root[0000, 0]@37..38#ROOT2024
+ LITERAL Integer 1 1:Root[0000, 0]@35..36#ROOT2024
+ PUNCH , [alone] 1:Root[0000, 0]@36..37#ROOT2024
+ PUNCH , [alone] 1:Root[0000, 0]@38..39#ROOT2024
+ PUNCH . [alone] 1:Root[0000, 0]@40..41#ROOT2024
+ LITERAL Float 0.0 1:Root[0000, 0]@41..44#ROOT2024
+ PUNCH ; [alone] 1:Root[0000, 0]@44..45#ROOT2024
+ IDENT let 1:Root[0000, 0]@50..53#ROOT2024
+ IDENT x 1:Root[0000, 0]@54..55#ROOT2024
+ PUNCH = [alone] 1:Root[0000, 0]@56..57#ROOT2024
+ LITERAL Integer 1 1:Root[0000, 0]@58..59#ROOT2024
+ PUNCH ; [alone] 1:Root[0000, 0]@59..60#ROOT2024
fn main(){
1;
@@ -227,14 +229,14 @@ fn expr_2021() {
const { 1 },
"#,
expect![[r#"
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- IDENT _ 1:[email protected]#ROOT2024
- PUNCH ; [joint] 0:[email protected]#ROOT2024
- SUBTREE () 0:[email protected]#ROOT2024 0:[email protected]#ROOT2024
- IDENT const 1:[email protected]#ROOT2024
- SUBTREE {} 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- LITERAL Integer 1 1:[email protected]#ROOT2024
- PUNCH ; [alone] 0:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..25#ROOT2024 1:Root[0000, 0]@0..25#ROOT2024
+ IDENT _ 1:Root[0000, 0]@5..6#ROOT2024
+ PUNCH ; [joint] 0:Root[0000, 0]@36..37#ROOT2024
+ SUBTREE () 0:Root[0000, 0]@34..35#ROOT2024 0:Root[0000, 0]@34..35#ROOT2024
+ IDENT const 1:Root[0000, 0]@12..17#ROOT2024
+ SUBTREE {} 1:Root[0000, 0]@18..19#ROOT2024 1:Root[0000, 0]@22..23#ROOT2024
+ LITERAL Integer 1 1:Root[0000, 0]@20..21#ROOT2024
+ PUNCH ; [alone] 0:Root[0000, 0]@39..40#ROOT2024
_;
(const {
@@ -255,13 +257,13 @@ fn expr_2021() {
expect![[r#"
ExpandError {
inner: (
- 1:[email protected]#ROOT2024,
+ 1:Root[0000, 0]@5..6#ROOT2024,
NoMatchingRule,
),
}
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- PUNCH ; [alone] 0:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..8#ROOT2024 1:Root[0000, 0]@0..8#ROOT2024
+ PUNCH ; [alone] 0:Root[0000, 0]@39..40#ROOT2024
;"#]],
);
@@ -279,13 +281,13 @@ fn expr_2021() {
expect![[r#"
ExpandError {
inner: (
- 1:[email protected]#ROOT2024,
+ 1:Root[0000, 0]@5..10#ROOT2024,
NoMatchingRule,
),
}
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- PUNCH ; [alone] 0:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..18#ROOT2024 1:Root[0000, 0]@0..18#ROOT2024
+ PUNCH ; [alone] 0:Root[0000, 0]@39..40#ROOT2024
;"#]],
);
@@ -305,26 +307,26 @@ fn expr_2021() {
break 'foo bar,
"#,
expect![[r#"
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- LITERAL Integer 4 1:[email protected]#ROOT2024
- PUNCH ; [joint] 0:[email protected]#ROOT2024
- LITERAL Str literal 1:[email protected]#ROOT2024
- PUNCH ; [joint] 0:[email protected]#ROOT2024
- SUBTREE () 0:[email protected]#ROOT2024 0:[email protected]#ROOT2024
- IDENT funcall 1:[email protected]#ROOT2024
- SUBTREE () 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- PUNCH ; [joint] 0:[email protected]#ROOT2024
- SUBTREE () 0:[email protected]#ROOT2024 0:[email protected]#ROOT2024
- IDENT future 1:[email protected]#ROOT2024
- PUNCH . [alone] 1:[email protected]#ROOT2024
- IDENT await 1:[email protected]#ROOT2024
- PUNCH ; [joint] 0:[email protected]#ROOT2024
- SUBTREE () 0:[email protected]#ROOT2024 0:[email protected]#ROOT2024
- IDENT break 1:[email protected]#ROOT2024
- PUNCH ' [joint] 1:[email protected]#ROOT2024
- IDENT foo 1:[email protected]#ROOT2024
- IDENT bar 1:[email protected]#ROOT2024
- PUNCH ; [alone] 0:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..76#ROOT2024 1:Root[0000, 0]@0..76#ROOT2024
+ LITERAL Integer 4 1:Root[0000, 0]@5..6#ROOT2024
+ PUNCH ; [joint] 0:Root[0000, 0]@41..42#ROOT2024
+ LITERAL Str literal 1:Root[0000, 0]@12..21#ROOT2024
+ PUNCH ; [joint] 0:Root[0000, 0]@41..42#ROOT2024
+ SUBTREE () 0:Root[0000, 0]@39..40#ROOT2024 0:Root[0000, 0]@39..40#ROOT2024
+ IDENT funcall 1:Root[0000, 0]@27..34#ROOT2024
+ SUBTREE () 1:Root[0000, 0]@34..35#ROOT2024 1:Root[0000, 0]@35..36#ROOT2024
+ PUNCH ; [joint] 0:Root[0000, 0]@41..42#ROOT2024
+ SUBTREE () 0:Root[0000, 0]@39..40#ROOT2024 0:Root[0000, 0]@39..40#ROOT2024
+ IDENT future 1:Root[0000, 0]@42..48#ROOT2024
+ PUNCH . [alone] 1:Root[0000, 0]@48..49#ROOT2024
+ IDENT await 1:Root[0000, 0]@49..54#ROOT2024
+ PUNCH ; [joint] 0:Root[0000, 0]@41..42#ROOT2024
+ SUBTREE () 0:Root[0000, 0]@39..40#ROOT2024 0:Root[0000, 0]@39..40#ROOT2024
+ IDENT break 1:Root[0000, 0]@60..65#ROOT2024
+ PUNCH ' [joint] 1:Root[0000, 0]@66..67#ROOT2024
+ IDENT foo 1:Root[0000, 0]@67..70#ROOT2024
+ IDENT bar 1:Root[0000, 0]@71..74#ROOT2024
+ PUNCH ; [alone] 0:Root[0000, 0]@44..45#ROOT2024
4;
"literal";
@@ -346,13 +348,13 @@ fn expr_2021() {
expect![[r#"
ExpandError {
inner: (
- 1:[email protected]#ROOT2024,
+ 1:Root[0000, 0]@5..6#ROOT2024,
NoMatchingRule,
),
}
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- PUNCH ; [alone] 0:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..8#ROOT2024 1:Root[0000, 0]@0..8#ROOT2024
+ PUNCH ; [alone] 0:Root[0000, 0]@44..45#ROOT2024
;"#]],
);
@@ -370,88 +372,88 @@ fn minus_belongs_to_literal() {
check(
"-1",
expect![[r#"
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- PUNCH - [alone] 0:[email protected]#ROOT2024
- LITERAL Integer 1 0:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..2#ROOT2024 1:Root[0000, 0]@0..2#ROOT2024
+ PUNCH - [alone] 0:Root[0000, 0]@10..11#ROOT2024
+ LITERAL Integer 1 0:Root[0000, 0]@11..12#ROOT2024
-1"#]],
);
check(
"- 1",
expect![[r#"
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- PUNCH - [alone] 0:[email protected]#ROOT2024
- LITERAL Integer 1 0:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..3#ROOT2024 1:Root[0000, 0]@0..3#ROOT2024
+ PUNCH - [alone] 0:Root[0000, 0]@10..11#ROOT2024
+ LITERAL Integer 1 0:Root[0000, 0]@11..12#ROOT2024
-1"#]],
);
check(
"-2",
expect![[r#"
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- PUNCH - [alone] 0:[email protected]#ROOT2024
- LITERAL Integer 2 0:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..2#ROOT2024 1:Root[0000, 0]@0..2#ROOT2024
+ PUNCH - [alone] 0:Root[0000, 0]@25..26#ROOT2024
+ LITERAL Integer 2 0:Root[0000, 0]@27..28#ROOT2024
-2"#]],
);
check(
"- 2",
expect![[r#"
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- PUNCH - [alone] 0:[email protected]#ROOT2024
- LITERAL Integer 2 0:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..3#ROOT2024 1:Root[0000, 0]@0..3#ROOT2024
+ PUNCH - [alone] 0:Root[0000, 0]@25..26#ROOT2024
+ LITERAL Integer 2 0:Root[0000, 0]@27..28#ROOT2024
-2"#]],
);
check(
"-3.0",
expect![[r#"
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- PUNCH - [alone] 0:[email protected]#ROOT2024
- LITERAL Float 3.0 0:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..4#ROOT2024 1:Root[0000, 0]@0..4#ROOT2024
+ PUNCH - [alone] 0:Root[0000, 0]@43..44#ROOT2024
+ LITERAL Float 3.0 0:Root[0000, 0]@45..48#ROOT2024
-3.0"#]],
);
check(
"- 3.0",
expect![[r#"
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- PUNCH - [alone] 0:[email protected]#ROOT2024
- LITERAL Float 3.0 0:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..5#ROOT2024 1:Root[0000, 0]@0..5#ROOT2024
+ PUNCH - [alone] 0:Root[0000, 0]@43..44#ROOT2024
+ LITERAL Float 3.0 0:Root[0000, 0]@45..48#ROOT2024
-3.0"#]],
);
check(
"@1",
expect![[r#"
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- LITERAL Integer 1 1:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..2#ROOT2024 1:Root[0000, 0]@0..2#ROOT2024
+ LITERAL Integer 1 1:Root[0000, 0]@1..2#ROOT2024
1"#]],
);
check(
"@-1",
expect![[r#"
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- PUNCH - [alone] 1:[email protected]#ROOT2024
- LITERAL Integer 1 1:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..3#ROOT2024 1:Root[0000, 0]@0..3#ROOT2024
+ PUNCH - [alone] 1:Root[0000, 0]@1..2#ROOT2024
+ LITERAL Integer 1 1:Root[0000, 0]@2..3#ROOT2024
-1"#]],
);
check(
"@1.0",
expect![[r#"
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- LITERAL Float 1.0 1:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..4#ROOT2024 1:Root[0000, 0]@0..4#ROOT2024
+ LITERAL Float 1.0 1:Root[0000, 0]@1..4#ROOT2024
1.0"#]],
);
check(
"@-1.0",
expect![[r#"
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- PUNCH - [alone] 1:[email protected]#ROOT2024
- LITERAL Float 1.0 1:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..5#ROOT2024 1:Root[0000, 0]@0..5#ROOT2024
+ PUNCH - [alone] 1:Root[0000, 0]@1..2#ROOT2024
+ LITERAL Float 1.0 1:Root[0000, 0]@2..5#ROOT2024
-1.0"#]],
);
@@ -460,16 +462,16 @@ fn minus_belongs_to_literal() {
expect![[r#"
ExpandError {
inner: (
- 1:[email protected]#ROOT2024,
+ 1:Root[0000, 0]@1..2#ROOT2024,
BindingError(
"expected literal",
),
),
}
- SUBTREE $$ 1:[email protected]#ROOT2024 1:[email protected]#ROOT2024
- PUNCH - [joint] 1:[email protected]#ROOT2024
- PUNCH - [alone] 1:[email protected]#ROOT2024
+ SUBTREE $$ 1:Root[0000, 0]@0..6#ROOT2024 1:Root[0000, 0]@0..6#ROOT2024
+ PUNCH - [joint] 1:Root[0000, 0]@1..2#ROOT2024
+ PUNCH - [alone] 1:Root[0000, 0]@2..3#ROOT2024
--"#]],
);
diff --git a/crates/proc-macro-api/src/legacy_protocol/msg.rs b/crates/proc-macro-api/src/legacy_protocol/msg.rs
index 55185aa492..4e1526fe48 100644
--- a/crates/proc-macro-api/src/legacy_protocol/msg.rs
+++ b/crates/proc-macro-api/src/legacy_protocol/msg.rs
@@ -22,9 +22,10 @@ pub const HAS_GLOBAL_SPANS: u32 = 3;
pub const RUST_ANALYZER_SPAN_SUPPORT: u32 = 4;
/// Whether literals encode their kind as an additional u32 field and idents their rawness as a u32 field.
pub const EXTENDED_LEAF_DATA: u32 = 5;
+pub const HASHED_AST_ID: u32 = 6;
/// Current API version of the proc-macro protocol.
-pub const CURRENT_API_VERSION: u32 = EXTENDED_LEAF_DATA;
+pub const CURRENT_API_VERSION: u32 = HASHED_AST_ID;
/// Represents requests sent from the client to the proc-macro-srv.
#[derive(Debug, Serialize, Deserialize)]
@@ -54,7 +55,7 @@ pub enum SpanMode {
Id,
/// Rust Analyzer-specific span handling mode.
- RustAnalyzer,
+ RustAnalyzer { fixup_ast_id: u32 },
}
/// Represents responses sent from the proc-macro-srv to the client.
@@ -201,7 +202,9 @@ type ProtocolWrite<W: Write> = for<'o, 'msg> fn(out: &'o mut W, msg: &'msg str)
#[cfg(test)]
mod tests {
use intern::{Symbol, sym};
- use span::{Edition, ErasedFileAstId, Span, SpanAnchor, SyntaxContext, TextRange, TextSize};
+ use span::{
+ Edition, ROOT_ERASED_FILE_AST_ID, Span, SpanAnchor, SyntaxContext, TextRange, TextSize,
+ };
use tt::{
Delimiter, DelimiterKind, Ident, Leaf, Literal, Punct, Spacing, TopSubtree,
TopSubtreeBuilder,
@@ -215,7 +218,7 @@ mod tests {
span::FileId::from_raw(0xe4e4e),
span::Edition::CURRENT,
),
- ast_id: ErasedFileAstId::from_raw(0),
+ ast_id: ROOT_ERASED_FILE_AST_ID,
};
let mut builder = TopSubtreeBuilder::new(Delimiter {
@@ -305,7 +308,7 @@ mod tests {
#[test]
fn test_proc_macro_rpc_works() {
let tt = fixture_token_tree();
- for v in RUST_ANALYZER_SPAN_SUPPORT..=CURRENT_API_VERSION {
+ for v in HASHED_AST_ID..=CURRENT_API_VERSION {
let mut span_data_table = Default::default();
let task = ExpandMacro {
data: ExpandMacroData {
diff --git a/crates/proc-macro-api/src/process.rs b/crates/proc-macro-api/src/process.rs
index fcea75ef67..e3b0614546 100644
--- a/crates/proc-macro-api/src/process.rs
+++ b/crates/proc-macro-api/src/process.rs
@@ -8,6 +8,7 @@ use std::{
};
use paths::AbsPath;
+use span::FIXUP_ERASED_FILE_AST_ID_MARKER;
use stdx::JodChild;
use crate::{
@@ -15,8 +16,7 @@ use crate::{
legacy_protocol::{
json::{read_json, write_json},
msg::{
- CURRENT_API_VERSION, Message, RUST_ANALYZER_SPAN_SUPPORT, Request, Response,
- ServerConfig, SpanMode,
+ CURRENT_API_VERSION, HASHED_AST_ID, Message, Request, Response, ServerConfig, SpanMode,
},
},
};
@@ -71,7 +71,9 @@ impl ProcMacroServerProcess {
Ok(v) => {
tracing::info!("Proc-macro server version: {v}");
srv.version = v;
- if srv.version >= RUST_ANALYZER_SPAN_SUPPORT {
+ if srv.version >= HASHED_AST_ID {
+ // We don't enable spans on versions prior to `HASHED_AST_ID`, because their ast id layout
+ // is different.
if let Ok(mode) = srv.enable_rust_analyzer_spans() {
srv.mode = mode;
}
@@ -111,7 +113,11 @@ impl ProcMacroServerProcess {
/// Enable support for rust-analyzer span mode if the server supports it.
fn enable_rust_analyzer_spans(&self) -> Result<SpanMode, ServerError> {
- let request = Request::SetConfig(ServerConfig { span_mode: SpanMode::RustAnalyzer });
+ let request = Request::SetConfig(ServerConfig {
+ span_mode: SpanMode::RustAnalyzer {
+ fixup_ast_id: FIXUP_ERASED_FILE_AST_ID_MARKER.into_raw(),
+ },
+ });
let response = self.send_task(request)?;
match response {
diff --git a/crates/proc-macro-srv-cli/src/main_loop.rs b/crates/proc-macro-srv-cli/src/main_loop.rs
index f54dff1f2d..25a49fbff9 100644
--- a/crates/proc-macro-srv-cli/src/main_loop.rs
+++ b/crates/proc-macro-srv-cli/src/main_loop.rs
@@ -26,7 +26,7 @@ pub(crate) fn run() -> io::Result<()> {
let write_response = |msg: msg::Response| msg.write(write_json, &mut io::stdout().lock());
let env = EnvSnapshot::default();
- let srv = proc_macro_srv::ProcMacroSrv::new(&env);
+ let mut srv = proc_macro_srv::ProcMacroSrv::new(&env);
let mut span_mode = SpanMode::Id;
@@ -78,7 +78,7 @@ pub(crate) fn run() -> io::Result<()> {
})
.map_err(msg::PanicMessage)
}),
- SpanMode::RustAnalyzer => msg::Response::ExpandMacroExtended({
+ SpanMode::RustAnalyzer { .. } => msg::Response::ExpandMacroExtended({
let mut span_data_table = deserialize_span_data_index_map(&span_data_table);
let def_site = span_data_table[def_site];
@@ -122,6 +122,9 @@ pub(crate) fn run() -> io::Result<()> {
msg::Request::ApiVersionCheck {} => msg::Response::ApiVersionCheck(CURRENT_API_VERSION),
msg::Request::SetConfig(config) => {
span_mode = config.span_mode;
+ if let SpanMode::RustAnalyzer { fixup_ast_id } = span_mode {
+ srv.set_fixup_ast_id(fixup_ast_id);
+ }
msg::Response::SetConfig(config)
}
};
diff --git a/crates/proc-macro-srv/src/dylib.rs b/crates/proc-macro-srv/src/dylib.rs
index c49159df99..c64a9833e3 100644
--- a/crates/proc-macro-srv/src/dylib.rs
+++ b/crates/proc-macro-srv/src/dylib.rs
@@ -3,6 +3,7 @@
mod version;
use proc_macro::bridge;
+use span::ErasedFileAstId;
use std::{fmt, fs, io, time::SystemTime};
use libloading::Library;
@@ -161,14 +162,20 @@ impl Expander {
def_site: S,
call_site: S,
mixed_site: S,
+ fixup_ast_id: ErasedFileAstId,
) -> Result<TopSubtree<S>, String>
where
<S::Server as bridge::server::Types>::TokenStream: Default,
{
- let result = self
- .inner
- .proc_macros
- .expand(macro_name, macro_body, attributes, def_site, call_site, mixed_site);
+ let result = self.inner.proc_macros.expand(
+ macro_name,
+ macro_body,
+ attributes,
+ def_site,
+ call_site,
+ mixed_site,
+ fixup_ast_id,
+ );
result.map_err(|e| e.into_string().unwrap_or_default())
}
diff --git a/crates/proc-macro-srv/src/lib.rs b/crates/proc-macro-srv/src/lib.rs
index 223c5a54b7..22afa018de 100644
--- a/crates/proc-macro-srv/src/lib.rs
+++ b/crates/proc-macro-srv/src/lib.rs
@@ -41,7 +41,7 @@ use std::{
};
use paths::{Utf8Path, Utf8PathBuf};
-use span::{Span, TokenId};
+use span::{ErasedFileAstId, FIXUP_ERASED_FILE_AST_ID_MARKER, Span, TokenId};
use crate::server_impl::TokenStream;
@@ -57,11 +57,16 @@ pub const RUSTC_VERSION_STRING: &str = env!("RUSTC_VERSION");
pub struct ProcMacroSrv<'env> {
expanders: Mutex<HashMap<Utf8PathBuf, Arc<dylib::Expander>>>,
env: &'env EnvSnapshot,
+ fixup_ast_id: ErasedFileAstId,
}
impl<'env> ProcMacroSrv<'env> {
pub fn new(env: &'env EnvSnapshot) -> Self {
- Self { expanders: Default::default(), env }
+ Self { expanders: Default::default(), env, fixup_ast_id: FIXUP_ERASED_FILE_AST_ID_MARKER }
+ }
+
+ pub fn set_fixup_ast_id(&mut self, fixup_ast_id: u32) {
+ self.fixup_ast_id = ErasedFileAstId::from_raw(fixup_ast_id);
}
}
@@ -101,6 +106,7 @@ impl ProcMacroSrv<'_> {
def_site,
call_site,
mixed_site,
+ self.fixup_ast_id,
)
.map(|tt| tt.0)
});
@@ -156,25 +162,41 @@ impl ProcMacroSrv<'_> {
pub trait ProcMacroSrvSpan: Copy + Send {
type Server: proc_macro::bridge::server::Server<TokenStream = TokenStream<Self>>;
- fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server;
+ fn make_server(
+ call_site: Self,
+ def_site: Self,
+ mixed_site: Self,
+ fixup_ast_id: ErasedFileAstId,
+ ) -> Self::Server;
}
impl ProcMacroSrvSpan for TokenId {
type Server = server_impl::token_id::TokenIdServer;
- fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server {
+ fn make_server(
+ call_site: Self,
+ def_site: Self,
+ mixed_site: Self,
+ _fixup_ast_id: ErasedFileAstId,
+ ) -> Self::Server {
Self::Server { call_site, def_site, mixed_site }
}
}
impl ProcMacroSrvSpan for Span {
type Server = server_impl::rust_analyzer_span::RaSpanServer;
- fn make_server(call_site: Self, def_site: Self, mixed_site: Self) -> Self::Server {
+ fn make_server(
+ call_site: Self,
+ def_site: Self,
+ mixed_site: Self,
+ fixup_ast_id: ErasedFileAstId,
+ ) -> Self::Server {
Self::Server {
call_site,
def_site,
mixed_site,
tracked_env_vars: Default::default(),
tracked_paths: Default::default(),
+ fixup_ast_id,
}
}
}
diff --git a/crates/proc-macro-srv/src/proc_macros.rs b/crates/proc-macro-srv/src/proc_macros.rs
index 18532706c4..3b9c45894c 100644
--- a/crates/proc-macro-srv/src/proc_macros.rs
+++ b/crates/proc-macro-srv/src/proc_macros.rs
@@ -1,6 +1,7 @@
//! Proc macro ABI
use proc_macro::bridge;
+use span::ErasedFileAstId;
use crate::{ProcMacroKind, ProcMacroSrvSpan, server_impl::TopSubtree};
@@ -22,6 +23,7 @@ impl ProcMacros {
def_site: S,
call_site: S,
mixed_site: S,
+ fixup_ast_id: ErasedFileAstId,
) -> Result<TopSubtree<S>, crate::PanicMessage> {
let parsed_body = crate::server_impl::TokenStream::with_subtree(macro_body);
@@ -37,7 +39,7 @@ impl ProcMacros {
{
let res = client.run(
&bridge::server::SameThread,
- S::make_server(call_site, def_site, mixed_site),
+ S::make_server(call_site, def_site, mixed_site, fixup_ast_id),
parsed_body,
cfg!(debug_assertions),
);
@@ -48,7 +50,7 @@ impl ProcMacros {
bridge::client::ProcMacro::Bang { name, client } if *name == macro_name => {
let res = client.run(
&bridge::server::SameThread,
- S::make_server(call_site, def_site, mixed_site),
+ S::make_server(call_site, def_site, mixed_site, fixup_ast_id),
parsed_body,
cfg!(debug_assertions),
);
@@ -59,7 +61,7 @@ impl ProcMacros {
bridge::client::ProcMacro::Attr { name, client } if *name == macro_name => {
let res = client.run(
&bridge::server::SameThread,
- S::make_server(call_site, def_site, mixed_site),
+ S::make_server(call_site, def_site, mixed_site, fixup_ast_id),
parsed_attributes,
parsed_body,
cfg!(debug_assertions),
diff --git a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
index 5d1271ba81..b071ad80f5 100644
--- a/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
+++ b/crates/proc-macro-srv/src/server_impl/rust_analyzer_span.rs
@@ -11,7 +11,7 @@ use std::{
use intern::Symbol;
use proc_macro::bridge::{self, server};
-use span::{FIXUP_ERASED_FILE_AST_ID_MARKER, Span};
+use span::{ErasedFileAstId, Span};
use tt::{TextRange, TextSize};
use crate::server_impl::{from_token_tree, literal_from_str, token_stream::TokenStreamBuilder};
@@ -28,6 +28,7 @@ pub struct RaSpanServer {
pub call_site: Span,
pub def_site: Span,
pub mixed_site: Span,
+ pub fixup_ast_id: ErasedFileAstId,
}
impl server::Types for RaSpanServer {
@@ -181,10 +182,10 @@ impl server::Span for RaSpanServer {
fn join(&mut self, first: Self::Span, second: Self::Span) -> Option<Self::Span> {
// We can't modify the span range for fixup spans, those are meaningful to fixup, so just
// prefer the non-fixup span.
- if first.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER {
+ if first.anchor.ast_id == self.fixup_ast_id {
return Some(second);
}
- if second.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER {
+ if second.anchor.ast_id == self.fixup_ast_id {
return Some(first);
}
// FIXME: Once we can talk back to the client, implement a "long join" request for anchors
@@ -213,7 +214,7 @@ impl server::Span for RaSpanServer {
end: Bound<usize>,
) -> Option<Self::Span> {
// We can't modify the span range for fixup spans, those are meaningful to fixup.
- if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER {
+ if span.anchor.ast_id == self.fixup_ast_id {
return Some(span);
}
let length = span.range.len().into();
@@ -256,7 +257,7 @@ impl server::Span for RaSpanServer {
fn end(&mut self, span: Self::Span) -> Self::Span {
// We can't modify the span range for fixup spans, those are meaningful to fixup.
- if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER {
+ if span.anchor.ast_id == self.fixup_ast_id {
return span;
}
Span { range: TextRange::empty(span.range.end()), ..span }
@@ -264,7 +265,7 @@ impl server::Span for RaSpanServer {
fn start(&mut self, span: Self::Span) -> Self::Span {
// We can't modify the span range for fixup spans, those are meaningful to fixup.
- if span.anchor.ast_id == FIXUP_ERASED_FILE_AST_ID_MARKER {
+ if span.anchor.ast_id == self.fixup_ast_id {
return span;
}
Span { range: TextRange::empty(span.range.start()), ..span }
@@ -318,7 +319,7 @@ mod tests {
range: TextRange::empty(TextSize::new(0)),
anchor: span::SpanAnchor {
file_id: EditionedFileId::current_edition(FileId::from_raw(0)),
- ast_id: span::ErasedFileAstId::from_raw(0),
+ ast_id: span::ROOT_ERASED_FILE_AST_ID,
},
ctx: SyntaxContext::root(span::Edition::CURRENT),
};
@@ -360,7 +361,7 @@ mod tests {
range: TextRange::empty(TextSize::new(0)),
anchor: span::SpanAnchor {
file_id: EditionedFileId::current_edition(FileId::from_raw(0)),
- ast_id: span::ErasedFileAstId::from_raw(0),
+ ast_id: span::ROOT_ERASED_FILE_AST_ID,
},
ctx: SyntaxContext::root(span::Edition::CURRENT),
};
diff --git a/crates/proc-macro-srv/src/tests/mod.rs b/crates/proc-macro-srv/src/tests/mod.rs
index d1ea89d2dc..08495f50bf 100644
--- a/crates/proc-macro-srv/src/tests/mod.rs
+++ b/crates/proc-macro-srv/src/tests/mod.rs
@@ -21,14 +21,14 @@ fn test_derive_empty() {
SUBTREE $$ 1 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT struct 42:[email protected]#ROOT2024
- IDENT S 42:[email protected]#ROOT2024
- PUNCH ; [alone] 42:[email protected]#ROOT2024
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT struct 42:Root[0000, 0]@0..6#ROOT2024
+ IDENT S 42:Root[0000, 0]@7..8#ROOT2024
+ PUNCH ; [alone] 42:Root[0000, 0]@8..9#ROOT2024
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024"#]],
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024"#]],
);
}
@@ -52,19 +52,19 @@ fn test_derive_error() {
LITERAL Str #[derive(DeriveError)] struct S ; 1
PUNCH ; [alone] 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT struct 42:[email protected]#ROOT2024
- IDENT S 42:[email protected]#ROOT2024
- PUNCH ; [alone] 42:[email protected]#ROOT2024
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT struct 42:Root[0000, 0]@0..6#ROOT2024
+ IDENT S 42:Root[0000, 0]@7..8#ROOT2024
+ PUNCH ; [alone] 42:Root[0000, 0]@8..9#ROOT2024
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT compile_error 42:[email protected]#ROOT2024
- PUNCH ! [alone] 42:[email protected]#ROOT2024
- SUBTREE () 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- LITERAL Str #[derive(DeriveError)] struct S ; 42:[email protected]#ROOT2024
- PUNCH ; [alone] 42:[email protected]#ROOT2024"#]],
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT compile_error 42:Root[0000, 0]@0..100#ROOT2024
+ PUNCH ! [alone] 42:Root[0000, 0]@0..100#ROOT2024
+ SUBTREE () 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL Str #[derive(DeriveError)] struct S ; 42:Root[0000, 0]@0..100#ROOT2024
+ PUNCH ; [alone] 42:Root[0000, 0]@0..100#ROOT2024"#]],
);
}
@@ -94,25 +94,25 @@ fn test_fn_like_macro_noop() {
PUNCH , [alone] 1
SUBTREE [] 1 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT ident 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Integer 0 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Integer 1 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- SUBTREE [] 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
-
-
-
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT ident 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Integer 0 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Integer 1 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- SUBTREE [] 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024"#]],
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT ident 42:Root[0000, 0]@0..5#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@5..6#ROOT2024
+ LITERAL Integer 0 42:Root[0000, 0]@7..8#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@8..9#ROOT2024
+ LITERAL Integer 1 42:Root[0000, 0]@10..11#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@11..12#ROOT2024
+ SUBTREE [] 42:Root[0000, 0]@13..14#ROOT2024 42:Root[0000, 0]@14..15#ROOT2024
+
+
+
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT ident 42:Root[0000, 0]@0..5#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@5..6#ROOT2024
+ LITERAL Integer 0 42:Root[0000, 0]@7..8#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@8..9#ROOT2024
+ LITERAL Integer 1 42:Root[0000, 0]@10..11#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@11..12#ROOT2024
+ SUBTREE [] 42:Root[0000, 0]@13..14#ROOT2024 42:Root[0000, 0]@14..15#ROOT2024"#]],
);
}
@@ -134,17 +134,17 @@ fn test_fn_like_macro_clone_ident_subtree() {
PUNCH , [alone] 1
SUBTREE [] 1 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT ident 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- SUBTREE [] 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT ident 42:Root[0000, 0]@0..5#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@5..6#ROOT2024
+ SUBTREE [] 42:Root[0000, 0]@7..8#ROOT2024 42:Root[0000, 0]@8..9#ROOT2024
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT ident 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- SUBTREE [] 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024"#]],
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT ident 42:Root[0000, 0]@0..5#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@5..6#ROOT2024
+ SUBTREE [] 42:Root[0000, 0]@7..9#ROOT2024 42:Root[0000, 0]@7..9#ROOT2024"#]],
);
}
@@ -162,13 +162,13 @@ fn test_fn_like_macro_clone_raw_ident() {
SUBTREE $$ 1 1
IDENT r#async 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT r#async 42:[email protected]#ROOT2024
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT r#async 42:Root[0000, 0]@0..7#ROOT2024
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT r#async 42:[email protected]#ROOT2024"#]],
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT r#async 42:Root[0000, 0]@0..7#ROOT2024"#]],
);
}
@@ -187,14 +187,14 @@ fn test_fn_like_fn_like_span_join() {
SUBTREE $$ 1 1
IDENT r#joined 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT foo 42:[email protected]#ROOT2024
- IDENT bar 42:[email protected]#ROOT2024
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT foo 42:Root[0000, 0]@0..3#ROOT2024
+ IDENT bar 42:Root[0000, 0]@8..11#ROOT2024
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT r#joined 42:[email protected]#ROOT2024"#]],
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT r#joined 42:Root[0000, 0]@0..11#ROOT2024"#]],
);
}
@@ -216,17 +216,17 @@ fn test_fn_like_fn_like_span_ops() {
IDENT resolved_at_def_site 1
IDENT start_span 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT set_def_site 42:[email protected]#ROOT2024
- IDENT resolved_at_def_site 42:[email protected]#ROOT2024
- IDENT start_span 42:[email protected]#ROOT2024
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT set_def_site 42:Root[0000, 0]@0..12#ROOT2024
+ IDENT resolved_at_def_site 42:Root[0000, 0]@13..33#ROOT2024
+ IDENT start_span 42:Root[0000, 0]@34..44#ROOT2024
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT set_def_site 41:[email protected]#ROOT2024
- IDENT resolved_at_def_site 42:[email protected]#ROOT2024
- IDENT start_span 42:[email protected]#ROOT2024"#]],
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT set_def_site 41:Root[0000, 0]@0..150#ROOT2024
+ IDENT resolved_at_def_site 42:Root[0000, 0]@13..33#ROOT2024
+ IDENT start_span 42:Root[0000, 0]@34..34#ROOT2024"#]],
);
}
@@ -259,28 +259,28 @@ fn test_fn_like_mk_literals() {
PUNCH - [alone] 1
LITERAL Integer 123 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
-
-
-
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- LITERAL ByteStr byte_string 42:[email protected]#ROOT2024
- LITERAL Char c 42:[email protected]#ROOT2024
- LITERAL Str string 42:[email protected]#ROOT2024
- LITERAL Str -string 42:[email protected]#ROOT2024
- LITERAL CStr cstring 42:[email protected]#ROOT2024
- LITERAL Float 3.14f64 42:[email protected]#ROOT2024
- PUNCH - [alone] 42:[email protected]#ROOT2024
- LITERAL Float 3.14f64 42:[email protected]#ROOT2024
- LITERAL Float 3.14 42:[email protected]#ROOT2024
- PUNCH - [alone] 42:[email protected]#ROOT2024
- LITERAL Float 3.14 42:[email protected]#ROOT2024
- LITERAL Integer 123i64 42:[email protected]#ROOT2024
- PUNCH - [alone] 42:[email protected]#ROOT2024
- LITERAL Integer 123i64 42:[email protected]#ROOT2024
- LITERAL Integer 123 42:[email protected]#ROOT2024
- PUNCH - [alone] 42:[email protected]#ROOT2024
- LITERAL Integer 123 42:[email protected]#ROOT2024"#]],
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+
+
+
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL ByteStr byte_string 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL Char c 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL Str string 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL Str -string 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL CStr cstring 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL Float 3.14f64 42:Root[0000, 0]@0..100#ROOT2024
+ PUNCH - [alone] 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL Float 3.14f64 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL Float 3.14 42:Root[0000, 0]@0..100#ROOT2024
+ PUNCH - [alone] 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL Float 3.14 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL Integer 123i64 42:Root[0000, 0]@0..100#ROOT2024
+ PUNCH - [alone] 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL Integer 123i64 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL Integer 123 42:Root[0000, 0]@0..100#ROOT2024
+ PUNCH - [alone] 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL Integer 123 42:Root[0000, 0]@0..100#ROOT2024"#]],
);
}
@@ -298,13 +298,13 @@ fn test_fn_like_mk_idents() {
IDENT standard 1
IDENT r#raw 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT standard 42:[email protected]#ROOT2024
- IDENT r#raw 42:[email protected]#ROOT2024"#]],
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT standard 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT r#raw 42:Root[0000, 0]@0..100#ROOT2024"#]],
);
}
@@ -360,51 +360,51 @@ fn test_fn_like_macro_clone_literals() {
PUNCH , [alone] 1
LITERAL CStr null 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- LITERAL Integer 1u16 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Integer 2_u32 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- PUNCH - [alone] 42:[email protected]#ROOT2024
- LITERAL Integer 4i64 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Float 3.14f32 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Str hello bridge 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Str suffixedsuffix 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL StrRaw(2) raw 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Char a 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Byte b 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL CStr null 42:[email protected]#ROOT2024
-
-
-
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- LITERAL Integer 1u16 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Integer 2_u32 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- PUNCH - [alone] 42:[email protected]#ROOT2024
- LITERAL Integer 4i64 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Float 3.14f32 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Str hello bridge 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Str suffixedsuffix 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL StrRaw(2) raw 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Char a 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL Byte b 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- LITERAL CStr null 42:[email protected]#ROOT2024"#]],
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL Integer 1u16 42:Root[0000, 0]@0..4#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@4..5#ROOT2024
+ LITERAL Integer 2_u32 42:Root[0000, 0]@6..11#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@11..12#ROOT2024
+ PUNCH - [alone] 42:Root[0000, 0]@13..14#ROOT2024
+ LITERAL Integer 4i64 42:Root[0000, 0]@14..18#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@18..19#ROOT2024
+ LITERAL Float 3.14f32 42:Root[0000, 0]@20..27#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@27..28#ROOT2024
+ LITERAL Str hello bridge 42:Root[0000, 0]@29..43#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@43..44#ROOT2024
+ LITERAL Str suffixedsuffix 42:Root[0000, 0]@45..61#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@61..62#ROOT2024
+ LITERAL StrRaw(2) raw 42:Root[0000, 0]@63..73#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@73..74#ROOT2024
+ LITERAL Char a 42:Root[0000, 0]@75..78#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@78..79#ROOT2024
+ LITERAL Byte b 42:Root[0000, 0]@80..84#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@84..85#ROOT2024
+ LITERAL CStr null 42:Root[0000, 0]@86..93#ROOT2024
+
+
+
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL Integer 1u16 42:Root[0000, 0]@0..4#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@4..5#ROOT2024
+ LITERAL Integer 2_u32 42:Root[0000, 0]@6..11#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@11..12#ROOT2024
+ PUNCH - [alone] 42:Root[0000, 0]@13..14#ROOT2024
+ LITERAL Integer 4i64 42:Root[0000, 0]@14..18#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@18..19#ROOT2024
+ LITERAL Float 3.14f32 42:Root[0000, 0]@20..27#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@27..28#ROOT2024
+ LITERAL Str hello bridge 42:Root[0000, 0]@29..43#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@43..44#ROOT2024
+ LITERAL Str suffixedsuffix 42:Root[0000, 0]@45..61#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@61..62#ROOT2024
+ LITERAL StrRaw(2) raw 42:Root[0000, 0]@63..73#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@73..74#ROOT2024
+ LITERAL Char a 42:Root[0000, 0]@75..78#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@78..79#ROOT2024
+ LITERAL Byte b 42:Root[0000, 0]@80..84#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@84..85#ROOT2024
+ LITERAL CStr null 42:Root[0000, 0]@86..93#ROOT2024"#]],
);
}
@@ -442,33 +442,33 @@ fn test_fn_like_macro_negative_literals() {
PUNCH - [alone] 1
LITERAL Float 2.7 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- PUNCH - [alone] 42:[email protected]#ROOT2024
- LITERAL Integer 1u16 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- PUNCH - [alone] 42:[email protected]#ROOT2024
- LITERAL Integer 2_u32 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- PUNCH - [alone] 42:[email protected]#ROOT2024
- LITERAL Float 3.14f32 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- PUNCH - [alone] 42:[email protected]#ROOT2024
- LITERAL Float 2.7 42:[email protected]#ROOT2024
-
-
-
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- PUNCH - [alone] 42:[email protected]#ROOT2024
- LITERAL Integer 1u16 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- PUNCH - [alone] 42:[email protected]#ROOT2024
- LITERAL Integer 2_u32 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- PUNCH - [alone] 42:[email protected]#ROOT2024
- LITERAL Float 3.14f32 42:[email protected]#ROOT2024
- PUNCH , [alone] 42:[email protected]#ROOT2024
- PUNCH - [alone] 42:[email protected]#ROOT2024
- LITERAL Float 2.7 42:[email protected]#ROOT2024"#]],
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ PUNCH - [alone] 42:Root[0000, 0]@0..1#ROOT2024
+ LITERAL Integer 1u16 42:Root[0000, 0]@1..5#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@5..6#ROOT2024
+ PUNCH - [alone] 42:Root[0000, 0]@7..8#ROOT2024
+ LITERAL Integer 2_u32 42:Root[0000, 0]@9..14#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@14..15#ROOT2024
+ PUNCH - [alone] 42:Root[0000, 0]@16..17#ROOT2024
+ LITERAL Float 3.14f32 42:Root[0000, 0]@17..24#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@24..25#ROOT2024
+ PUNCH - [alone] 42:Root[0000, 0]@26..27#ROOT2024
+ LITERAL Float 2.7 42:Root[0000, 0]@28..31#ROOT2024
+
+
+
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ PUNCH - [alone] 42:Root[0000, 0]@0..1#ROOT2024
+ LITERAL Integer 1u16 42:Root[0000, 0]@1..5#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@5..6#ROOT2024
+ PUNCH - [alone] 42:Root[0000, 0]@7..8#ROOT2024
+ LITERAL Integer 2_u32 42:Root[0000, 0]@9..14#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@14..15#ROOT2024
+ PUNCH - [alone] 42:Root[0000, 0]@16..17#ROOT2024
+ LITERAL Float 3.14f32 42:Root[0000, 0]@17..24#ROOT2024
+ PUNCH , [alone] 42:Root[0000, 0]@24..25#ROOT2024
+ PUNCH - [alone] 42:Root[0000, 0]@26..27#ROOT2024
+ LITERAL Float 2.7 42:Root[0000, 0]@28..31#ROOT2024"#]],
);
}
@@ -498,21 +498,21 @@ fn test_attr_macro() {
LITERAL Str #[attr_error(some arguments)] mod m {} 1
PUNCH ; [alone] 1"#]],
expect![[r#"
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT mod 42:[email protected]#ROOT2024
- IDENT m 42:[email protected]#ROOT2024
- SUBTREE {} 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
-
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT some 42:[email protected]#ROOT2024
- IDENT arguments 42:[email protected]#ROOT2024
-
- SUBTREE $$ 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- IDENT compile_error 42:[email protected]#ROOT2024
- PUNCH ! [alone] 42:[email protected]#ROOT2024
- SUBTREE () 42:[email protected]#ROOT2024 42:[email protected]#ROOT2024
- LITERAL Str #[attr_error(some arguments)] mod m {} 42:[email protected]#ROOT2024
- PUNCH ; [alone] 42:[email protected]#ROOT2024"#]],
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT mod 42:Root[0000, 0]@0..3#ROOT2024
+ IDENT m 42:Root[0000, 0]@4..5#ROOT2024
+ SUBTREE {} 42:Root[0000, 0]@6..7#ROOT2024 42:Root[0000, 0]@7..8#ROOT2024
+
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT some 42:Root[0000, 0]@0..4#ROOT2024
+ IDENT arguments 42:Root[0000, 0]@5..14#ROOT2024
+
+ SUBTREE $$ 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ IDENT compile_error 42:Root[0000, 0]@0..100#ROOT2024
+ PUNCH ! [alone] 42:Root[0000, 0]@0..100#ROOT2024
+ SUBTREE () 42:Root[0000, 0]@0..100#ROOT2024 42:Root[0000, 0]@0..100#ROOT2024
+ LITERAL Str #[attr_error(some arguments)] mod m {} 42:Root[0000, 0]@0..100#ROOT2024
+ PUNCH ; [alone] 42:Root[0000, 0]@0..100#ROOT2024"#]],
);
}
diff --git a/crates/proc-macro-srv/src/tests/utils.rs b/crates/proc-macro-srv/src/tests/utils.rs
index a0a45b269e..e0a69608de 100644
--- a/crates/proc-macro-srv/src/tests/utils.rs
+++ b/crates/proc-macro-srv/src/tests/utils.rs
@@ -1,7 +1,10 @@
//! utils used in proc-macro tests
use expect_test::Expect;
-use span::{EditionedFileId, ErasedFileAstId, FileId, Span, SpanAnchor, SyntaxContext, TokenId};
+use span::{
+ EditionedFileId, FIXUP_ERASED_FILE_AST_ID_MARKER, FileId, ROOT_ERASED_FILE_AST_ID, Span,
+ SpanAnchor, SyntaxContext, TokenId,
+};
use tt::TextRange;
use crate::{EnvSnapshot, ProcMacroSrv, dylib, proc_macro_test_dylib_path};
@@ -65,8 +68,17 @@ fn assert_expand_impl(
let input_ts_string = format!("{input_ts:?}");
let attr_ts_string = attr_ts.as_ref().map(|it| format!("{it:?}"));
- let res =
- expander.expand(macro_name, input_ts, attr_ts, def_site, call_site, mixed_site).unwrap();
+ let res = expander
+ .expand(
+ macro_name,
+ input_ts,
+ attr_ts,
+ def_site,
+ call_site,
+ mixed_site,
+ FIXUP_ERASED_FILE_AST_ID_MARKER,
+ )
+ .unwrap();
expect.assert_eq(&format!(
"{input_ts_string}\n\n{}\n\n{res:?}",
attr_ts_string.unwrap_or_default()
@@ -76,7 +88,7 @@ fn assert_expand_impl(
range: TextRange::new(0.into(), 150.into()),
anchor: SpanAnchor {
file_id: EditionedFileId::current_edition(FileId::from_raw(41)),
- ast_id: ErasedFileAstId::from_raw(1),
+ ast_id: ROOT_ERASED_FILE_AST_ID,
},
ctx: SyntaxContext::root(span::Edition::CURRENT),
};
@@ -84,7 +96,7 @@ fn assert_expand_impl(
range: TextRange::new(0.into(), 100.into()),
anchor: SpanAnchor {
file_id: EditionedFileId::current_edition(FileId::from_raw(42)),
- ast_id: ErasedFileAstId::from_raw(2),
+ ast_id: ROOT_ERASED_FILE_AST_ID,
},
ctx: SyntaxContext::root(span::Edition::CURRENT),
};
@@ -98,7 +110,17 @@ fn assert_expand_impl(
let fixture_string = format!("{fixture:?}");
let attr_string = attr.as_ref().map(|it| format!("{it:?}"));
- let res = expander.expand(macro_name, fixture, attr, def_site, call_site, mixed_site).unwrap();
+ let res = expander
+ .expand(
+ macro_name,
+ fixture,
+ attr,
+ def_site,
+ call_site,
+ mixed_site,
+ FIXUP_ERASED_FILE_AST_ID_MARKER,
+ )
+ .unwrap();
expect_spanned
.assert_eq(&format!("{fixture_string}\n\n{}\n\n{res:#?}", attr_string.unwrap_or_default()));
}
diff --git a/crates/span/Cargo.toml b/crates/span/Cargo.toml
index b3b401c3db..966962bab3 100644
--- a/crates/span/Cargo.toml
+++ b/crates/span/Cargo.toml
@@ -22,6 +22,9 @@ vfs.workspace = true
syntax.workspace = true
stdx.workspace = true
+[dev-dependencies]
+syntax.workspace = true
+
[features]
default = ["salsa"]
diff --git a/crates/span/src/ast_id.rs b/crates/span/src/ast_id.rs
index 228fba1fa0..51a693ca01 100644
--- a/crates/span/src/ast_id.rs
+++ b/crates/span/src/ast_id.rs
@@ -4,137 +4,534 @@
//! Specifically, it enumerates all items in a file and uses position of a an
//! item as an ID. That way, id's don't change unless the set of items itself
//! changes.
+//!
+//! These IDs are tricky. If one of them invalidates, its interned ID invalidates,
+//! and this can cause *a lot* to be recomputed. For example, if you invalidate the ID
+//! of a struct, and that struct has an impl (any impl!) this will cause the `Self`
+//! type of the impl to invalidate, which will cause the all impls queries to be
+//! invalidated, which will cause every trait solve query in this crate *and* all
+//! transitive reverse dependencies to be invalidated, which is pretty much the worst
+//! thing that can happen incrementality wise.
+//!
+//! So we want these IDs to stay as stable as possible. For top-level items, we store
+//! their kind and name, which should be unique, but since they can still not be, we
+//! also store an index disambiguator. For nested items, we also store the ID of their
+//! parent. For macro calls, we store the macro name and an index. There aren't usually
+//! a lot of macro calls in item position, and invalidation in bodies is not much of
+//! a problem, so this should be enough.
use std::{
any::type_name,
fmt,
- hash::{BuildHasher, BuildHasherDefault, Hash, Hasher},
+ hash::{BuildHasher, Hash, Hasher},
marker::PhantomData,
};
use la_arena::{Arena, Idx, RawIdx};
-use rustc_hash::FxHasher;
-use syntax::{AstNode, AstPtr, SyntaxNode, SyntaxNodePtr, ast};
+use rustc_hash::{FxBuildHasher, FxHashMap};
+use syntax::{
+ AstNode, AstPtr, SyntaxKind, SyntaxNode, SyntaxNodePtr,
+ ast::{self, HasName},
+ match_ast,
+};
+
+// The first index is always the root node's AstId
+/// The root ast id always points to the encompassing file, using this in spans is discouraged as
+/// any range relative to it will be effectively absolute, ruining the entire point of anchored
+/// relative text ranges.
+pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId =
+ ErasedFileAstId(pack_hash_index_and_kind(0, 0, ErasedFileAstIdKind::Root as u32));
+
+/// ErasedFileAstId used as the span for syntax node fixups. Any Span containing this file id is to be
+/// considered fake.
+pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId =
+ ErasedFileAstId(pack_hash_index_and_kind(0, 0, ErasedFileAstIdKind::Fixup as u32));
-/// See crates\hir-expand\src\ast_id_map.rs
/// This is a type erased FileAstId.
-#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct ErasedFileAstId(u32);
+impl fmt::Debug for ErasedFileAstId {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let kind = self.kind();
+ macro_rules! kind {
+ ($($kind:ident),* $(,)?) => {
+ if false {
+ // Ensure we covered all variants.
+ match ErasedFileAstIdKind::Root {
+ $( ErasedFileAstIdKind::$kind => {} )*
+ }
+ unreachable!()
+ }
+ $( else if kind == ErasedFileAstIdKind::$kind as u32 {
+ stringify!($kind)
+ } )*
+ else {
+ "Unknown"
+ }
+ };
+ }
+ let kind = kind!(
+ Root,
+ Enum,
+ Struct,
+ Union,
+ ExternCrate,
+ MacroDef,
+ MacroRules,
+ Module,
+ Static,
+ Trait,
+ TraitAlias,
+ Variant,
+ Const,
+ Fn,
+ MacroCall,
+ TypeAlias,
+ ExternBlock,
+ Use,
+ Impl,
+ BlockExpr,
+ Fixup,
+ );
+ if f.alternate() {
+ write!(f, "{kind}[{:04X}, {}]", self.hash_value(), self.index())
+ } else {
+ f.debug_struct("ErasedFileAstId")
+ .field("kind", &format_args!("{kind}"))
+ .field("index", &self.index())
+ .field("hash", &format_args!("{:04X}", self.hash_value()))
+ .finish()
+ }
+ }
+}
+
+#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
+enum ErasedFileAstIdKind {
+ Root,
+ // The following are associated with `ErasedHasNameFileAstId`.
+ Enum,
+ Struct,
+ Union,
+ ExternCrate,
+ MacroDef,
+ MacroRules,
+ Module,
+ Static,
+ Trait,
+ TraitAlias,
+ // Until here associated with `ErasedHasNameFileAstId`.
+ // The following are associated with `ErasedAssocItemFileAstId`.
+ Variant,
+ Const,
+ Fn,
+ MacroCall,
+ TypeAlias,
+ // Until here associated with `ErasedAssocItemFileAstId`.
+ // Extern blocks don't really have any identifying property unfortunately.
+ ExternBlock,
+ // FIXME: If we store the final `UseTree` instead of the top-level `Use`, we can store its name,
+ // and be way more granular for incrementality, at the expense of increased memory usage.
+ // Use IDs aren't used a lot. The main thing that stores them is the def map. So everything that
+ // uses the def map will be invalidated. That includes infers, and so is pretty bad, but our
+ // def map incrementality story is pretty bad anyway and needs to be improved (see
+ // https://rust-lang.zulipchat.com/#narrow/channel/185405-t-compiler.2Frust-analyzer/topic/.60infer.60.20queries.20and.20splitting.20.60DefMap.60).
+ // So I left this as-is for now, as the def map improvement should also mitigate this.
+ Use,
+ /// Associated with [`ImplFileAstId`].
+ Impl,
+ /// Associated with [`BlockExprFileAstId`].
+ BlockExpr,
+ /// Keep this last.
+ Fixup,
+}
+
+// First hash, then index, then kind.
+const HASH_BITS: u32 = 16;
+const INDEX_BITS: u32 = 11;
+const KIND_BITS: u32 = 5;
+const _: () = assert!(ErasedFileAstIdKind::Fixup as u32 <= ((1 << KIND_BITS) - 1));
+const _: () = assert!(HASH_BITS + INDEX_BITS + KIND_BITS == u32::BITS);
+
+#[inline]
+const fn u16_hash(hash: u64) -> u16 {
+ // We do basically the same as `FxHasher`. We don't use rustc-hash and truncate because the
+ // higher bits have more entropy, but unlike rustc-hash we don't rotate because it rotates
+ // for hashmaps that just use the low bits, but we compare all bits.
+ const K: u16 = 0xecc5;
+ let (part1, part2, part3, part4) =
+ (hash as u16, (hash >> 16) as u16, (hash >> 32) as u16, (hash >> 48) as u16);
+ part1
+ .wrapping_add(part2)
+ .wrapping_mul(K)
+ .wrapping_add(part3)
+ .wrapping_mul(K)
+ .wrapping_add(part4)
+ .wrapping_mul(K)
+}
+
+#[inline]
+const fn pack_hash_index_and_kind(hash: u16, index: u32, kind: u32) -> u32 {
+ (hash as u32) | (index << HASH_BITS) | (kind << (HASH_BITS + INDEX_BITS))
+}
+
impl ErasedFileAstId {
- pub const fn into_raw(self) -> u32 {
- self.0
+ #[inline]
+ fn hash_value(self) -> u16 {
+ self.0 as u16
}
- pub const fn from_raw(u32: u32) -> Self {
- Self(u32)
+
+ #[inline]
+ fn index(self) -> u32 {
+ (self.0 << KIND_BITS) >> (HASH_BITS + KIND_BITS)
}
-}
-impl fmt::Display for ErasedFileAstId {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
+ #[inline]
+ fn kind(self) -> u32 {
+ self.0 >> (HASH_BITS + INDEX_BITS)
}
-}
-impl fmt::Debug for ErasedFileAstId {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- self.0.fmt(f)
+
+ fn ast_id_for(
+ node: &SyntaxNode,
+ index_map: &mut ErasedAstIdNextIndexMap,
+ parent: Option<&ErasedFileAstId>,
+ ) -> Option<ErasedFileAstId> {
+ // Blocks are deliberately not here - we only want to allocate a block if it contains items.
+ has_name_ast_id(node, index_map)
+ .or_else(|| assoc_item_ast_id(node, index_map, parent))
+ .or_else(|| extern_block_ast_id(node, index_map))
+ .or_else(|| use_ast_id(node, index_map))
+ .or_else(|| impl_ast_id(node, index_map))
+ }
+
+ fn should_alloc(node: &SyntaxNode) -> bool {
+ should_alloc_has_name(node)
+ || should_alloc_assoc_item(node)
+ || ast::ExternBlock::can_cast(node.kind())
+ || ast::Use::can_cast(node.kind())
+ || ast::Impl::can_cast(node.kind())
+ }
+
+ #[inline]
+ pub fn into_raw(self) -> u32 {
+ self.0
+ }
+
+ #[inline]
+ pub fn from_raw(v: u32) -> Self {
+ Self(v)
}
}
+pub trait AstIdNode: AstNode {}
+
/// `AstId` points to an AST node in a specific file.
-pub struct FileAstId<N: AstIdNode> {
+pub struct FileAstId<N> {
raw: ErasedFileAstId,
- covariant: PhantomData<fn() -> N>,
+ _marker: PhantomData<fn() -> N>,
}
-impl<N: AstIdNode> Clone for FileAstId<N> {
+/// Traits are manually implemented because `derive` adds redundant bounds.
+impl<N> Clone for FileAstId<N> {
+ #[inline]
fn clone(&self) -> FileAstId<N> {
*self
}
}
-impl<N: AstIdNode> Copy for FileAstId<N> {}
+impl<N> Copy for FileAstId<N> {}
-impl<N: AstIdNode> PartialEq for FileAstId<N> {
+impl<N> PartialEq for FileAstId<N> {
fn eq(&self, other: &Self) -> bool {
self.raw == other.raw
}
}
-impl<N: AstIdNode> Eq for FileAstId<N> {}
-impl<N: AstIdNode> Hash for FileAstId<N> {
+impl<N> Eq for FileAstId<N> {}
+impl<N> Hash for FileAstId<N> {
fn hash<H: Hasher>(&self, hasher: &mut H) {
self.raw.hash(hasher);
}
}
-impl<N: AstIdNode> fmt::Debug for FileAstId<N> {
+impl<N> fmt::Debug for FileAstId<N> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- write!(f, "FileAstId::<{}>({})", type_name::<N>(), self.raw)
+ write!(f, "FileAstId::<{}>({:?})", type_name::<N>(), self.raw)
}
}
-impl<N: AstIdNode> FileAstId<N> {
+impl<N> FileAstId<N> {
// Can't make this a From implementation because of coherence
+ #[inline]
pub fn upcast<M: AstIdNode>(self) -> FileAstId<M>
where
N: Into<M>,
{
- FileAstId { raw: self.raw, covariant: PhantomData }
+ FileAstId { raw: self.raw, _marker: PhantomData }
}
+ #[inline]
pub fn erase(self) -> ErasedFileAstId {
self.raw
}
}
-pub trait AstIdNode: AstNode {}
-macro_rules! register_ast_id_node {
+#[derive(Hash)]
+struct ErasedHasNameFileAstId<'a> {
+ kind: SyntaxKind,
+ name: &'a str,
+}
+
+/// This holds the ast ID for variants too (they're a kind of assoc item).
+#[derive(Hash)]
+struct ErasedAssocItemFileAstId<'a> {
+ /// Subtle: items in `extern` blocks **do not** store the ID of the extern block here.
+ /// Instead this is left empty. The reason is that `ExternBlockFileAstId` is pretty unstable
+ /// (it contains only an index), and extern blocks don't introduce a new scope, so storing
+ /// the extern block ID will do more harm to incrementality than help.
+ parent: Option<ErasedFileAstId>,
+ properties: ErasedHasNameFileAstId<'a>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+struct ImplFileAstId<'a> {
+ /// This can be `None` if the `Self` type is not a named type, or if it is inside a macro call.
+ self_ty_name: Option<&'a str>,
+ /// This can be `None` if this is an inherent impl, or if the trait name is inside a macro call.
+ trait_name: Option<&'a str>,
+}
+
+#[derive(Debug, Clone, PartialEq, Eq, Hash)]
+struct BlockExprFileAstId {
+ parent: Option<ErasedFileAstId>,
+}
+
+impl AstIdNode for ast::ExternBlock {}
+
+fn extern_block_ast_id(
+ node: &SyntaxNode,
+ index_map: &mut ErasedAstIdNextIndexMap,
+) -> Option<ErasedFileAstId> {
+ if ast::ExternBlock::can_cast(node.kind()) {
+ Some(index_map.new_id(ErasedFileAstIdKind::ExternBlock, ()))
+ } else {
+ None
+ }
+}
+
+impl AstIdNode for ast::Use {}
+
+fn use_ast_id(
+ node: &SyntaxNode,
+ index_map: &mut ErasedAstIdNextIndexMap,
+) -> Option<ErasedFileAstId> {
+ if ast::Use::can_cast(node.kind()) {
+ Some(index_map.new_id(ErasedFileAstIdKind::Use, ()))
+ } else {
+ None
+ }
+}
+
+impl AstIdNode for ast::Impl {}
+
+fn impl_ast_id(
+ node: &SyntaxNode,
+ index_map: &mut ErasedAstIdNextIndexMap,
+) -> Option<ErasedFileAstId> {
+ if let Some(node) = ast::Impl::cast(node.clone()) {
+ let type_as_name = |ty: Option<ast::Type>| match ty? {
+ ast::Type::PathType(it) => Some(it.path()?.segment()?.name_ref()?),
+ _ => None,
+ };
+ let self_ty_name = type_as_name(node.self_ty());
+ let trait_name = type_as_name(node.trait_());
+ let data = ImplFileAstId {
+ self_ty_name: self_ty_name.as_ref().map(|it| it.text_non_mutable()),
+ trait_name: trait_name.as_ref().map(|it| it.text_non_mutable()),
+ };
+ Some(index_map.new_id(ErasedFileAstIdKind::Impl, data))
+ } else {
+ None
+ }
+}
+
+// Blocks aren't `AstIdNode`s deliberately, because unlike other nodes, not all blocks get their own
+// ast id, only if they have items. To account for that we have a different, fallible, API for blocks.
+// impl !AstIdNode for ast::BlockExpr {}
+
+fn block_expr_ast_id(
+ node: &SyntaxNode,
+ index_map: &mut ErasedAstIdNextIndexMap,
+ parent: Option<&ErasedFileAstId>,
+) -> Option<ErasedFileAstId> {
+ if ast::BlockExpr::can_cast(node.kind()) {
+ Some(
+ index_map.new_id(
+ ErasedFileAstIdKind::BlockExpr,
+ BlockExprFileAstId { parent: parent.copied() },
+ ),
+ )
+ } else {
+ None
+ }
+}
+
+#[derive(Default)]
+struct ErasedAstIdNextIndexMap(FxHashMap<(ErasedFileAstIdKind, u16), u32>);
+
+impl ErasedAstIdNextIndexMap {
+ #[inline]
+ fn new_id(&mut self, kind: ErasedFileAstIdKind, data: impl Hash) -> ErasedFileAstId {
+ let hash = FxBuildHasher.hash_one(&data);
+ let initial_hash = u16_hash(hash);
+ // Even though 2^INDEX_BITS=2048 items with the same hash seems like a lot,
+ // it could happen with macro calls or `use`s in macro-generated files. So we want
+ // to handle it gracefully. We just increment the hash.
+ let mut hash = initial_hash;
+ let index = loop {
+ match self.0.entry((kind, hash)) {
+ std::collections::hash_map::Entry::Occupied(mut entry) => {
+ let i = entry.get_mut();
+ if *i < ((1 << INDEX_BITS) - 1) {
+ *i += 1;
+ break *i;
+ }
+ }
+ std::collections::hash_map::Entry::Vacant(entry) => {
+ entry.insert(0);
+ break 0;
+ }
+ }
+ hash = hash.wrapping_add(1);
+ if hash == initial_hash {
+ // That's 2^27=134,217,728 items!
+ panic!("you have way too many items in the same file!");
+ }
+ };
+ let kind = kind as u32;
+ ErasedFileAstId(pack_hash_index_and_kind(hash, index, kind))
+ }
+}
+
+macro_rules! register_enum_ast_id {
(impl AstIdNode for $($ident:ident),+ ) => {
$(
impl AstIdNode for ast::$ident {}
)+
- fn should_alloc_id(kind: syntax::SyntaxKind) -> bool {
- $(
- ast::$ident::can_cast(kind)
- )||+
+ };
+}
+register_enum_ast_id! {
+ impl AstIdNode for
+ Item, AnyHasGenericParams, Adt, Macro,
+ AssocItem
+}
+
+macro_rules! register_has_name_ast_id {
+ (impl AstIdNode for $($ident:ident = $name_method:ident),+ ) => {
+ $(
+ impl AstIdNode for ast::$ident {}
+ )+
+
+ fn has_name_ast_id(node: &SyntaxNode, index_map: &mut ErasedAstIdNextIndexMap) -> Option<ErasedFileAstId> {
+ let kind = node.kind();
+ match_ast! {
+ match node {
+ $(
+ ast::$ident(node) => {
+ let name = node.$name_method();
+ let name = name.as_ref().map_or("", |it| it.text_non_mutable());
+ let result = ErasedHasNameFileAstId {
+ kind,
+ name,
+ };
+ Some(index_map.new_id(ErasedFileAstIdKind::$ident, result))
+ },
+ )*
+ _ => None,
+ }
+ }
+ }
+
+ fn should_alloc_has_name(node: &SyntaxNode) -> bool {
+ let kind = node.kind();
+ false $( || ast::$ident::can_cast(kind) )*
}
};
}
-register_ast_id_node! {
+register_has_name_ast_id! {
impl AstIdNode for
- Item, AnyHasGenericParams,
- Adt,
- Enum,
- Variant,
- Struct,
- Union,
- AssocItem,
- Const,
- Fn,
- MacroCall,
- TypeAlias,
- ExternBlock,
- ExternCrate,
- Impl,
- Macro,
- MacroDef,
- MacroRules,
- Module,
- Static,
- Trait,
- TraitAlias,
- Use,
- BlockExpr, ConstArg
+ Enum = name,
+ Struct = name,
+ Union = name,
+ ExternCrate = name_ref,
+ MacroDef = name,
+ MacroRules = name,
+ Module = name,
+ Static = name,
+ Trait = name,
+ TraitAlias = name
+}
+
+macro_rules! register_assoc_item_ast_id {
+ (impl AstIdNode for $($ident:ident = $name_callback:expr),+ ) => {
+ $(
+ impl AstIdNode for ast::$ident {}
+ )+
+
+ fn assoc_item_ast_id(
+ node: &SyntaxNode,
+ index_map: &mut ErasedAstIdNextIndexMap,
+ parent: Option<&ErasedFileAstId>,
+ ) -> Option<ErasedFileAstId> {
+ let kind = node.kind();
+ match_ast! {
+ match node {
+ $(
+ ast::$ident(node) => {
+ let name = $name_callback(node);
+ let name = name.as_ref().map_or("", |it| it.text_non_mutable());
+ let properties = ErasedHasNameFileAstId {
+ kind,
+ name,
+ };
+ let result = ErasedAssocItemFileAstId {
+ parent: parent.copied(),
+ properties,
+ };
+ Some(index_map.new_id(ErasedFileAstIdKind::$ident, result))
+ },
+ )*
+ _ => None,
+ }
+ }
+ }
+
+ fn should_alloc_assoc_item(node: &SyntaxNode) -> bool {
+ let kind = node.kind();
+ false $( || ast::$ident::can_cast(kind) )*
+ }
+ };
+}
+register_assoc_item_ast_id! {
+ impl AstIdNode for
+ Variant = |it: ast::Variant| it.name(),
+ Const = |it: ast::Const| it.name(),
+ Fn = |it: ast::Fn| it.name(),
+ MacroCall = |it: ast::MacroCall| it.path().and_then(|path| path.segment()?.name_ref()),
+ TypeAlias = |it: ast::TypeAlias| it.name()
}
/// Maps items' `SyntaxNode`s to `ErasedFileAstId`s and back.
#[derive(Default)]
pub struct AstIdMap {
- /// Maps stable id to unstable ptr.
- arena: Arena<SyntaxNodePtr>,
- /// Reverse: map ptr to id.
- map: hashbrown::HashTable<Idx<SyntaxNodePtr>>,
+ /// An arena of the ptrs and their associated ID.
+ arena: Arena<(SyntaxNodePtr, ErasedFileAstId)>,
+ /// Map ptr to id.
+ ptr_map: hashbrown::HashTable<ArenaId>,
+ /// Map id to ptr.
+ id_map: hashbrown::HashTable<ArenaId>,
}
+type ArenaId = Idx<(SyntaxNodePtr, ErasedFileAstId)>;
+
impl fmt::Debug for AstIdMap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("AstIdMap").field("arena", &self.arena).finish()
@@ -148,31 +545,116 @@ impl PartialEq for AstIdMap {
}
impl Eq for AstIdMap {}
+#[derive(Debug, Clone, Copy, PartialEq, Eq)]
+enum ContainsItems {
+ Yes,
+ No,
+}
+
impl AstIdMap {
pub fn from_source(node: &SyntaxNode) -> AstIdMap {
assert!(node.parent().is_none());
let mut res = AstIdMap::default();
+ let mut index_map = ErasedAstIdNextIndexMap::default();
+
+ // Ensure we allocate the root.
+ res.arena.alloc((SyntaxNodePtr::new(node), ROOT_ERASED_FILE_AST_ID));
- // make sure to allocate the root node
- if !should_alloc_id(node.kind()) {
- res.alloc(node);
- }
// By walking the tree in breadth-first order we make sure that parents
// get lower ids then children. That is, adding a new child does not
// change parent's id. This means that, say, adding a new function to a
// trait does not change ids of top-level items, which helps caching.
- bdfs(node, |it| {
- if should_alloc_id(it.kind()) {
- res.alloc(&it);
- TreeOrder::BreadthFirst
- } else {
- TreeOrder::DepthFirst
+
+ // This contains the stack of the `BlockExpr`s we are under. We do this
+ // so we only allocate `BlockExpr`s if they contain items.
+ // The general idea is: when we enter a block we push `(block, false)` here.
+ // Items inside the block are attributed to the block's container, not the block.
+ // For the first item we find inside a block, we make this `(block, true)`
+ // and create an ast id for the block. When exiting the block we pop it,
+ // whether or not we created an ast id for it.
+ // It may seem that with this setup we will generate an ID for blocks that
+ // have no items directly but have items inside other items inside them.
+ // This is true, but it doesn't matter, because such blocks can't exist.
+ // After all, the block will then contain the *outer* item, so we allocate
+ // an ID for it anyway.
+ let mut blocks = Vec::new();
+ let mut curr_layer = vec![(node.clone(), None)];
+ let mut next_layer = vec![];
+ while !curr_layer.is_empty() {
+ curr_layer.drain(..).for_each(|(node, parent_idx)| {
+ let mut preorder = node.preorder();
+ while let Some(event) = preorder.next() {
+ match event {
+ syntax::WalkEvent::Enter(node) => {
+ if ast::BlockExpr::can_cast(node.kind()) {
+ blocks.push((node, ContainsItems::No));
+ } else if ErasedFileAstId::should_alloc(&node) {
+ // Allocate blocks on-demand, only if they have items.
+ // We don't associate items with blocks, only with items, since block IDs can be quite unstable.
+ // FIXME: Is this the correct thing to do? Macro calls might actually be more incremental if
+ // associated with blocks (not sure). Either way it's not a big deal.
+ if let Some((
+ last_block_node,
+ already_allocated @ ContainsItems::No,
+ )) = blocks.last_mut()
+ {
+ let block_ast_id = block_expr_ast_id(
+ last_block_node,
+ &mut index_map,
+ parent_of(parent_idx, &res),
+ )
+ .expect("not a BlockExpr");
+ res.arena
+ .alloc((SyntaxNodePtr::new(last_block_node), block_ast_id));
+ *already_allocated = ContainsItems::Yes;
+ }
+
+ let parent = parent_of(parent_idx, &res);
+ let ast_id =
+ ErasedFileAstId::ast_id_for(&node, &mut index_map, parent)
+ .expect("this node should have an ast id");
+ let idx = res.arena.alloc((SyntaxNodePtr::new(&node), ast_id));
+
+ next_layer.extend(node.children().map(|child| (child, Some(idx))));
+ preorder.skip_subtree();
+ }
+ }
+ syntax::WalkEvent::Leave(node) => {
+ if ast::BlockExpr::can_cast(node.kind()) {
+ assert_eq!(
+ blocks.pop().map(|it| it.0),
+ Some(node),
+ "left a BlockExpr we never entered"
+ );
+ }
+ }
+ }
+ }
+ });
+ std::mem::swap(&mut curr_layer, &mut next_layer);
+ assert!(blocks.is_empty(), "didn't leave all BlockExprs");
+ }
+
+ res.ptr_map = hashbrown::HashTable::with_capacity(res.arena.len());
+ res.id_map = hashbrown::HashTable::with_capacity(res.arena.len());
+ for (idx, (ptr, ast_id)) in res.arena.iter() {
+ let ptr_hash = hash_ptr(ptr);
+ let ast_id_hash = hash_ast_id(ast_id);
+ match res.ptr_map.entry(
+ ptr_hash,
+ |idx2| *idx2 == idx,
+ |&idx| hash_ptr(&res.arena[idx].0),
+ ) {
+ hashbrown::hash_table::Entry::Occupied(_) => unreachable!(),
+ hashbrown::hash_table::Entry::Vacant(entry) => {
+ entry.insert(idx);
+ }
}
- });
- res.map = hashbrown::HashTable::with_capacity(res.arena.len());
- for (idx, ptr) in res.arena.iter() {
- let hash = hash_ptr(ptr);
- match res.map.entry(hash, |&idx2| idx2 == idx, |&idx| hash_ptr(&res.arena[idx])) {
+ match res.id_map.entry(
+ ast_id_hash,
+ |idx2| *idx2 == idx,
+ |&idx| hash_ast_id(&res.arena[idx].1),
+ ) {
hashbrown::hash_table::Entry::Occupied(_) => unreachable!(),
hashbrown::hash_table::Entry::Vacant(entry) => {
entry.insert(idx);
@@ -180,98 +662,235 @@ impl AstIdMap {
}
}
res.arena.shrink_to_fit();
- res
+ return res;
+
+ fn parent_of(parent_idx: Option<ArenaId>, res: &AstIdMap) -> Option<&ErasedFileAstId> {
+ let mut parent = parent_idx.map(|parent_idx| &res.arena[parent_idx].1);
+ if parent.is_some_and(|parent| parent.kind() == ErasedFileAstIdKind::ExternBlock as u32)
+ {
+ // See the comment on `ErasedAssocItemFileAstId` for why is this.
+ // FIXME: Technically there could be an extern block inside another item, e.g.:
+ // ```
+ // fn foo() {
+ // extern "C" {
+ // fn bar();
+ // }
+ // }
+ // ```
+ // Here we want to make `foo()` the parent of `bar()`, but we make it `None`.
+ // Shouldn't be a big deal though.
+ parent = None;
+ }
+ parent
+ }
}
/// The [`AstId`] of the root node
pub fn root(&self) -> SyntaxNodePtr {
- self.arena[Idx::from_raw(RawIdx::from_u32(0))]
+ self.arena[Idx::from_raw(RawIdx::from_u32(0))].0
}
pub fn ast_id<N: AstIdNode>(&self, item: &N) -> FileAstId<N> {
- let raw = self.erased_ast_id(item.syntax());
- FileAstId { raw, covariant: PhantomData }
+ self.ast_id_for_ptr(AstPtr::new(item))
+ }
+
+ /// Blocks may not be allocated (if they have no items), so they have a different API.
+ pub fn ast_id_for_block(&self, block: &ast::BlockExpr) -> Option<FileAstId<ast::BlockExpr>> {
+ self.ast_id_for_ptr_for_block(AstPtr::new(block))
}
pub fn ast_id_for_ptr<N: AstIdNode>(&self, ptr: AstPtr<N>) -> FileAstId<N> {
let ptr = ptr.syntax_node_ptr();
- let hash = hash_ptr(&ptr);
- match self.map.find(hash, |&idx| self.arena[idx] == ptr) {
- Some(&raw) => FileAstId {
- raw: ErasedFileAstId(raw.into_raw().into_u32()),
- covariant: PhantomData,
- },
- None => panic!(
- "Can't find {:?} in AstIdMap:\n{:?}",
+ FileAstId { raw: self.erased_ast_id(ptr), _marker: PhantomData }
+ }
+
+ /// Blocks may not be allocated (if they have no items), so they have a different API.
+ pub fn ast_id_for_ptr_for_block(
+ &self,
+ ptr: AstPtr<ast::BlockExpr>,
+ ) -> Option<FileAstId<ast::BlockExpr>> {
+ let ptr = ptr.syntax_node_ptr();
+ self.try_erased_ast_id(ptr).map(|raw| FileAstId { raw, _marker: PhantomData })
+ }
+
+ fn erased_ast_id(&self, ptr: SyntaxNodePtr) -> ErasedFileAstId {
+ self.try_erased_ast_id(ptr).unwrap_or_else(|| {
+ panic!(
+ "Can't find SyntaxNodePtr {:?} in AstIdMap:\n{:?}",
ptr,
self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
- ),
- }
+ )
+ })
}
- pub fn get<N: AstIdNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
- AstPtr::try_from_raw(self.arena[Idx::from_raw(RawIdx::from_u32(id.raw.into_raw()))])
- .unwrap()
+ fn try_erased_ast_id(&self, ptr: SyntaxNodePtr) -> Option<ErasedFileAstId> {
+ let hash = hash_ptr(&ptr);
+ let idx = *self.ptr_map.find(hash, |&idx| self.arena[idx].0 == ptr)?;
+ Some(self.arena[idx].1)
}
- pub fn get_erased(&self, id: ErasedFileAstId) -> SyntaxNodePtr {
- self.arena[Idx::from_raw(RawIdx::from_u32(id.into_raw()))]
+ // Don't bound on `AstIdNode` here, because `BlockExpr`s are also valid here (`ast::BlockExpr`
+ // doesn't always have a matching `FileAstId`, but a `FileAstId<ast::BlockExpr>` always has
+ // a matching node).
+ pub fn get<N: AstNode>(&self, id: FileAstId<N>) -> AstPtr<N> {
+ let ptr = self.get_erased(id.raw);
+ AstPtr::try_from_raw(ptr)
+ .unwrap_or_else(|| panic!("AstIdMap node mismatch with node `{ptr:?}`"))
}
- fn erased_ast_id(&self, item: &SyntaxNode) -> ErasedFileAstId {
- let ptr = SyntaxNodePtr::new(item);
- let hash = hash_ptr(&ptr);
- match self.map.find(hash, |&idx| self.arena[idx] == ptr) {
- Some(&idx) => ErasedFileAstId(idx.into_raw().into_u32()),
+ pub fn get_erased(&self, id: ErasedFileAstId) -> SyntaxNodePtr {
+ let hash = hash_ast_id(&id);
+ match self.id_map.find(hash, |&idx| self.arena[idx].1 == id) {
+ Some(&idx) => self.arena[idx].0,
None => panic!(
- "Can't find {:?} in AstIdMap:\n{:?}\n source text: {}",
- item,
+ "Can't find ast id {:?} in AstIdMap:\n{:?}",
+ id,
self.arena.iter().map(|(_id, i)| i).collect::<Vec<_>>(),
- item
),
}
}
-
- fn alloc(&mut self, item: &SyntaxNode) -> ErasedFileAstId {
- ErasedFileAstId(self.arena.alloc(SyntaxNodePtr::new(item)).into_raw().into_u32())
- }
}
+#[inline]
fn hash_ptr(ptr: &SyntaxNodePtr) -> u64 {
- BuildHasherDefault::<FxHasher>::default().hash_one(ptr)
-}
-
-#[derive(Copy, Clone, PartialEq, Eq)]
-enum TreeOrder {
- BreadthFirst,
- DepthFirst,
-}
-
-/// 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
-/// `f` returns [`TreeOrder::BreadthFirst`] are visited breadth-first, all the other nodes are explored
-/// [`TreeOrder::DepthFirst`].
-///
-/// In other words, the size of the bfs queue is bound by the number of "true"
-/// nodes.
-fn bdfs(node: &SyntaxNode, mut f: impl FnMut(SyntaxNode) -> TreeOrder) {
- let mut curr_layer = vec![node.clone()];
- let mut next_layer = vec![];
- while !curr_layer.is_empty() {
- curr_layer.drain(..).for_each(|node| {
- let mut preorder = node.preorder();
- while let Some(event) = preorder.next() {
- match event {
- syntax::WalkEvent::Enter(node) => {
- if f(node.clone()) == TreeOrder::BreadthFirst {
- next_layer.extend(node.children());
- preorder.skip_subtree();
- }
- }
- syntax::WalkEvent::Leave(_) => {}
- }
+ FxBuildHasher.hash_one(ptr)
+}
+
+#[inline]
+fn hash_ast_id(ptr: &ErasedFileAstId) -> u64 {
+ FxBuildHasher.hash_one(ptr)
+}
+
+#[cfg(test)]
+mod tests {
+ use syntax::{AstNode, Edition, SourceFile, SyntaxKind, SyntaxNodePtr, WalkEvent, ast};
+
+ use crate::AstIdMap;
+
+ #[test]
+ fn check_all_nodes() {
+ let syntax = SourceFile::parse(
+ r#"
+extern crate foo;
+fn foo() {
+ union U {}
+}
+struct S;
+macro_rules! m {}
+macro m2() {}
+trait Trait {}
+impl Trait for S {}
+impl S {}
+impl m!() {}
+impl m2!() for m!() {}
+type T = i32;
+enum E {
+ V1(),
+ V2 {},
+ V3,
+}
+struct S; // duplicate
+extern "C" {
+ static S: i32;
+}
+static mut S: i32 = 0;
+const FOO: i32 = 0;
+ "#,
+ Edition::CURRENT,
+ )
+ .syntax_node();
+ let ast_id_map = AstIdMap::from_source(&syntax);
+ for node in syntax.preorder() {
+ let WalkEvent::Enter(node) = node else { continue };
+ if !matches!(
+ node.kind(),
+ SyntaxKind::EXTERN_CRATE
+ | SyntaxKind::FN
+ | SyntaxKind::UNION
+ | SyntaxKind::STRUCT
+ | SyntaxKind::MACRO_RULES
+ | SyntaxKind::MACRO_DEF
+ | SyntaxKind::MACRO_CALL
+ | SyntaxKind::TRAIT
+ | SyntaxKind::IMPL
+ | SyntaxKind::TYPE_ALIAS
+ | SyntaxKind::ENUM
+ | SyntaxKind::VARIANT
+ | SyntaxKind::EXTERN_BLOCK
+ | SyntaxKind::STATIC
+ | SyntaxKind::CONST
+ ) {
+ continue;
}
- });
- std::mem::swap(&mut curr_layer, &mut next_layer);
+ let ptr = SyntaxNodePtr::new(&node);
+ let ast_id = ast_id_map.erased_ast_id(ptr);
+ let turn_back = ast_id_map.get_erased(ast_id);
+ assert_eq!(ptr, turn_back);
+ }
+ }
+
+ #[test]
+ fn different_names_get_different_hashes() {
+ let syntax = SourceFile::parse(
+ r#"
+fn foo() {}
+fn bar() {}
+ "#,
+ Edition::CURRENT,
+ )
+ .syntax_node();
+ let ast_id_map = AstIdMap::from_source(&syntax);
+ let fns = syntax.descendants().filter_map(ast::Fn::cast).collect::<Vec<_>>();
+ let [foo_fn, bar_fn] = fns.as_slice() else {
+ panic!("not exactly 2 functions");
+ };
+ let foo_fn_id = ast_id_map.ast_id(foo_fn);
+ let bar_fn_id = ast_id_map.ast_id(bar_fn);
+ assert_ne!(foo_fn_id.raw.hash_value(), bar_fn_id.raw.hash_value(), "hashes are equal");
+ }
+
+ #[test]
+ fn different_parents_get_different_hashes() {
+ let syntax = SourceFile::parse(
+ r#"
+fn foo() {
+ m!();
+}
+fn bar() {
+ m!();
+}
+ "#,
+ Edition::CURRENT,
+ )
+ .syntax_node();
+ let ast_id_map = AstIdMap::from_source(&syntax);
+ let macro_calls = syntax.descendants().filter_map(ast::MacroCall::cast).collect::<Vec<_>>();
+ let [macro_call_foo, macro_call_bar] = macro_calls.as_slice() else {
+ panic!("not exactly 2 macro calls");
+ };
+ let macro_call_foo_id = ast_id_map.ast_id(macro_call_foo);
+ let macro_call_bar_id = ast_id_map.ast_id(macro_call_bar);
+ assert_ne!(
+ macro_call_foo_id.raw.hash_value(),
+ macro_call_bar_id.raw.hash_value(),
+ "hashes are equal"
+ );
+ }
+
+ #[test]
+ fn blocks_with_no_items_have_no_id() {
+ let syntax = SourceFile::parse(
+ r#"
+fn foo() {
+ let foo = 1;
+ bar(foo);
+}
+ "#,
+ Edition::CURRENT,
+ )
+ .syntax_node();
+ let ast_id_map = AstIdMap::from_source(&syntax);
+ let block = syntax.descendants().find_map(ast::BlockExpr::cast).expect("no block");
+ assert!(ast_id_map.ast_id_for_block(&block).is_none());
}
}
diff --git a/crates/span/src/lib.rs b/crates/span/src/lib.rs
index f81648ac42..b81d08eed6 100644
--- a/crates/span/src/lib.rs
+++ b/crates/span/src/lib.rs
@@ -6,7 +6,10 @@ mod hygiene;
mod map;
pub use self::{
- ast_id::{AstIdMap, AstIdNode, ErasedFileAstId, FileAstId},
+ ast_id::{
+ AstIdMap, AstIdNode, ErasedFileAstId, FIXUP_ERASED_FILE_AST_ID_MARKER, FileAstId,
+ ROOT_ERASED_FILE_AST_ID,
+ },
hygiene::{SyntaxContext, Transparency},
map::{RealSpanMap, SpanMap},
};
@@ -15,19 +18,6 @@ pub use syntax::Edition;
pub use text_size::{TextRange, TextSize};
pub use vfs::FileId;
-// The first index is always the root node's AstId
-/// The root ast id always points to the encompassing file, using this in spans is discouraged as
-/// any range relative to it will be effectively absolute, ruining the entire point of anchored
-/// relative text ranges.
-pub const ROOT_ERASED_FILE_AST_ID: ErasedFileAstId = ErasedFileAstId::from_raw(0);
-
-/// FileId used as the span for syntax node fixups. Any Span containing this file id is to be
-/// considered fake.
-pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId =
- // we pick the second to last for this in case we ever consider making this a NonMaxU32, this
- // is required to be stable for the proc-macro-server
- ErasedFileAstId::from_raw(!0 - 1);
-
pub type Span = SpanData<SyntaxContext>;
impl Span {
@@ -60,7 +50,7 @@ impl<Ctx: fmt::Debug> fmt::Debug for SpanData<Ctx> {
if f.alternate() {
fmt::Debug::fmt(&self.anchor.file_id.file_id().index(), f)?;
f.write_char(':')?;
- fmt::Debug::fmt(&self.anchor.ast_id.into_raw(), f)?;
+ write!(f, "{:#?}", self.anchor.ast_id)?;
f.write_char('@')?;
fmt::Debug::fmt(&self.range, f)?;
f.write_char('#')?;
@@ -85,7 +75,7 @@ impl fmt::Display for Span {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.anchor.file_id.file_id().index(), f)?;
f.write_char(':')?;
- fmt::Debug::fmt(&self.anchor.ast_id.into_raw(), f)?;
+ write!(f, "{:#?}", self.anchor.ast_id)?;
f.write_char('@')?;
fmt::Debug::fmt(&self.range, f)?;
f.write_char('#')?;
@@ -101,7 +91,7 @@ pub struct SpanAnchor {
impl fmt::Debug for SpanAnchor {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
- f.debug_tuple("SpanAnchor").field(&self.file_id).field(&self.ast_id.into_raw()).finish()
+ f.debug_tuple("SpanAnchor").field(&self.file_id).field(&self.ast_id).finish()
}
}
diff --git a/crates/span/src/map.rs b/crates/span/src/map.rs
index cc7a886643..f58201793d 100644
--- a/crates/span/src/map.rs
+++ b/crates/span/src/map.rs
@@ -169,7 +169,7 @@ impl fmt::Display for RealSpanMap {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f, "RealSpanMap({:?}):", self.file_id)?;
for span in self.pairs.iter() {
- writeln!(f, "{}: {}", u32::from(span.0), span.1.into_raw())?;
+ writeln!(f, "{}: {:#?}", u32::from(span.0), span.1)?;
}
Ok(())
}
diff --git a/crates/syntax/src/ast/node_ext.rs b/crates/syntax/src/ast/node_ext.rs
index dcf853427e..f5530c5fff 100644
--- a/crates/syntax/src/ast/node_ext.rs
+++ b/crates/syntax/src/ast/node_ext.rs
@@ -30,6 +30,16 @@ impl ast::Name {
pub fn text(&self) -> TokenText<'_> {
text_of_first_token(self.syntax())
}
+ pub fn text_non_mutable(&self) -> &str {
+ fn first_token(green_ref: &GreenNodeData) -> &GreenTokenData {
+ green_ref.children().next().and_then(NodeOrToken::into_token).unwrap()
+ }
+
+ match self.syntax().green() {
+ Cow::Borrowed(green_ref) => first_token(green_ref).text(),
+ Cow::Owned(_) => unreachable!(),
+ }
+ }
}
impl ast::NameRef {