Unnamed repository; edit this file 'description' to name the repository.
Use stable AST IDs
Instead of simple numbering, we hash important bits, like the name of the item. This will allow for much better incrementality, e.g. when you add an item. Currently, this invalidates the IDs of all following items, which invalidates pretty much everything.
Chayim Refael Friedman 11 months ago
parent 5b2c8bc · commit 4bcf03e
-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 {