Unnamed repository; edit this file 'description' to name the repository.
-rw-r--r--crates/hir-def/src/data/adt.rs8
-rw-r--r--crates/hir-def/src/db.rs10
-rw-r--r--crates/hir-def/src/item_tree.rs3
-rw-r--r--crates/hir-def/src/item_tree/lower.rs21
-rw-r--r--crates/hir-def/src/item_tree/pretty.rs18
-rw-r--r--crates/hir-def/src/item_tree/tests.rs6
-rw-r--r--crates/hir-def/src/lib.rs11
-rw-r--r--crates/hir-def/src/macro_expansion_tests/mbe.rs2
-rw-r--r--crates/hir-expand/src/attrs.rs6
-rw-r--r--crates/hir-expand/src/builtin_fn_macro.rs113
-rw-r--r--crates/hir-expand/src/lib.rs53
-rw-r--r--crates/hir-expand/src/quote.rs7
-rw-r--r--crates/hir-expand/src/span_map.rs1
-rw-r--r--crates/hir/src/lib.rs9
-rw-r--r--crates/hir/src/semantics.rs43
-rw-r--r--crates/ide-completion/src/completions/env_vars.rs43
-rw-r--r--crates/ide-completion/src/lib.rs2
-rw-r--r--crates/ide/src/goto_definition.rs51
-rw-r--r--crates/ide/src/syntax_highlighting/test_data/highlight_macros.html2
-rw-r--r--crates/proc-macro-srv/src/tests/mod.rs177
-rw-r--r--crates/proc-macro-srv/src/tests/utils.rs2
-rw-r--r--crates/span/src/hygiene.rs12
-rw-r--r--crates/span/src/lib.rs22
-rw-r--r--crates/span/src/map.rs33
-rw-r--r--crates/stdx/src/anymap.rs15
-rw-r--r--crates/tt/src/lib.rs48
26 files changed, 456 insertions, 262 deletions
diff --git a/crates/hir-def/src/data/adt.rs b/crates/hir-def/src/data/adt.rs
index 5790e600f6..a7461b78af 100644
--- a/crates/hir-def/src/data/adt.rs
+++ b/crates/hir-def/src/data/adt.rs
@@ -191,9 +191,9 @@ impl StructData {
let krate = loc.container.krate;
let item_tree = loc.id.item_tree(db);
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
- let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
+ let cfg_options = db.crate_graph()[krate].cfg_options.clone();
- let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
+ let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
let mut flags = StructFlags::NO_FLAGS;
if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
@@ -248,9 +248,9 @@ impl StructData {
let krate = loc.container.krate;
let item_tree = loc.id.item_tree(db);
let repr = repr_from_value(db, krate, &item_tree, ModItem::from(loc.id.value).into());
- let cfg_options = db.crate_graph()[loc.container.krate].cfg_options.clone();
+ let cfg_options = db.crate_graph()[krate].cfg_options.clone();
- let attrs = item_tree.attrs(db, loc.container.krate, ModItem::from(loc.id.value).into());
+ let attrs = item_tree.attrs(db, krate, ModItem::from(loc.id.value).into());
let mut flags = StructFlags::NO_FLAGS;
if attrs.by_key("rustc_has_incoherent_inherent_impls").exists() {
flags |= StructFlags::IS_RUSTC_HAS_INCOHERENT_INHERENT_IMPL;
diff --git a/crates/hir-def/src/db.rs b/crates/hir-def/src/db.rs
index 544ed6bc34..d3f436be0b 100644
--- a/crates/hir-def/src/db.rs
+++ b/crates/hir-def/src/db.rs
@@ -309,13 +309,10 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId {
kind: kind(loc.expander, loc.id.file_id(), makro.ast_id.upcast()),
local_inner: false,
allow_internal_unsafe: loc.allow_internal_unsafe,
- span: db
- .span_map(loc.id.file_id())
- .span_for_range(db.ast_id_map(loc.id.file_id()).get(makro.ast_id).text_range()),
+ span: makro.def_site,
edition: loc.edition,
}
}
-
MacroId::MacroRulesId(it) => {
let loc: MacroRulesLoc = it.lookup(db);
@@ -328,9 +325,7 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId {
allow_internal_unsafe: loc
.flags
.contains(MacroRulesLocFlags::ALLOW_INTERNAL_UNSAFE),
- span: db
- .span_map(loc.id.file_id())
- .span_for_range(db.ast_id_map(loc.id.file_id()).get(makro.ast_id).text_range()),
+ span: makro.def_site,
edition: loc.edition,
}
}
@@ -348,6 +343,7 @@ fn macro_def(db: &dyn DefDatabase, id: MacroId) -> MacroDefId {
),
local_inner: false,
allow_internal_unsafe: false,
+ // FIXME: This is wrong, this should point to the name
span: db
.span_map(loc.id.file_id())
.span_for_range(db.ast_id_map(loc.id.file_id()).get(makro.ast_id).text_range()),
diff --git a/crates/hir-def/src/item_tree.rs b/crates/hir-def/src/item_tree.rs
index bd3d377ec0..6450e3a255 100644
--- a/crates/hir-def/src/item_tree.rs
+++ b/crates/hir-def/src/item_tree.rs
@@ -790,7 +790,6 @@ pub struct MacroCall {
pub path: Interned<ModPath>,
pub ast_id: FileAstId<ast::MacroCall>,
pub expand_to: ExpandTo,
- // FIXME: We need to move this out. It invalidates the item tree when typing inside the macro call.
pub call_site: Span,
}
@@ -799,6 +798,7 @@ pub struct MacroRules {
/// The name of the declared macro.
pub name: Name,
pub ast_id: FileAstId<ast::MacroRules>,
+ pub def_site: Span,
}
/// "Macros 2.0" macro definition.
@@ -807,6 +807,7 @@ pub struct Macro2 {
pub name: Name,
pub visibility: RawVisibilityId,
pub ast_id: FileAstId<ast::MacroDef>,
+ pub def_site: Span,
}
impl Use {
diff --git a/crates/hir-def/src/item_tree/lower.rs b/crates/hir-def/src/item_tree/lower.rs
index bf3d54f4ca..a1755b110e 100644
--- a/crates/hir-def/src/item_tree/lower.rs
+++ b/crates/hir-def/src/item_tree/lower.rs
@@ -560,35 +560,34 @@ impl<'a> Ctx<'a> {
fn lower_macro_call(&mut self, m: &ast::MacroCall) -> Option<FileItemTreeId<MacroCall>> {
let span_map = self.span_map();
- let path = Interned::new(ModPath::from_src(self.db.upcast(), m.path()?, &mut |range| {
+ let path = m.path()?;
+ let range = path.syntax().text_range();
+ let path = Interned::new(ModPath::from_src(self.db.upcast(), path, &mut |range| {
span_map.span_for_range(range).ctx
})?);
let ast_id = self.source_ast_id_map.ast_id(m);
let expand_to = hir_expand::ExpandTo::from_call_site(m);
- let res = MacroCall {
- path,
- ast_id,
- expand_to,
- call_site: span_map.span_for_range(m.syntax().text_range()),
- };
+ let res = MacroCall { path, ast_id, expand_to, call_site: span_map.span_for_range(range) };
Some(id(self.data().macro_calls.alloc(res)))
}
fn lower_macro_rules(&mut self, m: &ast::MacroRules) -> Option<FileItemTreeId<MacroRules>> {
- let name = m.name().map(|it| it.as_name())?;
+ let name = m.name()?;
+ let def_site = self.span_map().span_for_range(name.syntax().text_range());
let ast_id = self.source_ast_id_map.ast_id(m);
- let res = MacroRules { name, ast_id };
+ let res = MacroRules { name: name.as_name(), ast_id, def_site };
Some(id(self.data().macro_rules.alloc(res)))
}
fn lower_macro_def(&mut self, m: &ast::MacroDef) -> Option<FileItemTreeId<Macro2>> {
- let name = m.name().map(|it| it.as_name())?;
+ let name = m.name()?;
+ let def_site = self.span_map().span_for_range(name.syntax().text_range());
let ast_id = self.source_ast_id_map.ast_id(m);
let visibility = self.lower_visibility(m);
- let res = Macro2 { name, ast_id, visibility };
+ let res = Macro2 { name: name.as_name(), ast_id, visibility, def_site };
Some(id(self.data().macro_defs.alloc(res)))
}
diff --git a/crates/hir-def/src/item_tree/pretty.rs b/crates/hir-def/src/item_tree/pretty.rs
index 87c90a4c6a..7d5fdf056b 100644
--- a/crates/hir-def/src/item_tree/pretty.rs
+++ b/crates/hir-def/src/item_tree/pretty.rs
@@ -498,13 +498,23 @@ impl Printer<'_> {
wln!(self, "{}!(...);", path.display(self.db.upcast()));
}
ModItem::MacroRules(it) => {
- let MacroRules { name, ast_id } = &self.tree[it];
- self.print_ast_id(ast_id.erase());
+ let MacroRules { name, ast_id, def_site } = &self.tree[it];
+ let _ = writeln!(
+ self,
+ "// AstId: {:?}, Span: {}",
+ ast_id.erase().into_raw(),
+ def_site,
+ );
wln!(self, "macro_rules! {} {{ ... }}", name.display(self.db.upcast()));
}
ModItem::Macro2(it) => {
- let Macro2 { name, visibility, ast_id } = &self.tree[it];
- self.print_ast_id(ast_id.erase());
+ let Macro2 { name, visibility, ast_id, def_site } = &self.tree[it];
+ let _ = writeln!(
+ self,
+ "// AstId: {:?}, Span: {}",
+ ast_id.erase().into_raw(),
+ def_site,
+ );
self.print_visibility(*visibility);
wln!(self, "macro {} {{ ... }}", name.display(self.db.upcast()));
}
diff --git a/crates/hir-def/src/item_tree/tests.rs b/crates/hir-def/src/item_tree/tests.rs
index 26f7b41c77..0c11f34841 100644
--- a/crates/hir-def/src/item_tree/tests.rs
+++ b/crates/hir-def/src/item_tree/tests.rs
@@ -272,13 +272,13 @@ pub macro m2() {}
m!();
"#,
expect![[r#"
- // AstId: 1
+ // AstId: 1, Span: 0:[email protected]#0
macro_rules! m { ... }
- // AstId: 2
+ // AstId: 2, Span: 0:[email protected]#0
pub macro m2 { ... }
- // AstId: 3, Span: 0:[email protected]#0, ExpandTo: Items
+ // AstId: 3, Span: 0:[email protected]#0, ExpandTo: Items
m!(...);
"#]],
);
diff --git a/crates/hir-def/src/lib.rs b/crates/hir-def/src/lib.rs
index 977782dfaf..6ff350c75a 100644
--- a/crates/hir-def/src/lib.rs
+++ b/crates/hir-def/src/lib.rs
@@ -1342,17 +1342,18 @@ impl AsMacroCall for InFile<&ast::MacroCall> {
let ast_id = AstId::new(self.file_id, db.ast_id_map(self.file_id).ast_id(self.value));
let span_map = db.span_map(self.file_id);
let path = self.value.path().and_then(|path| {
- path::ModPath::from_src(db, path, &mut |range| {
+ let range = path.syntax().text_range();
+ let mod_path = path::ModPath::from_src(db, path, &mut |range| {
span_map.as_ref().span_for_range(range).ctx
- })
+ })?;
+ let call_site = span_map.span_for_range(range);
+ Some((call_site, mod_path))
});
- let Some(path) = path else {
+ let Some((call_site, path)) = path else {
return Ok(ExpandResult::only_err(ExpandError::other("malformed macro invocation")));
};
- let call_site = span_map.span_for_range(self.value.syntax().text_range());
-
macro_call_as_call_id_with_eager(
db,
&AstIdWithPath::new(ast_id.file_id, ast_id.value, path),
diff --git a/crates/hir-def/src/macro_expansion_tests/mbe.rs b/crates/hir-def/src/macro_expansion_tests/mbe.rs
index edc8247f16..965f329acb 100644
--- a/crates/hir-def/src/macro_expansion_tests/mbe.rs
+++ b/crates/hir-def/src/macro_expansion_tests/mbe.rs
@@ -171,7 +171,7 @@ fn main(foo: ()) {
}
fn main(foo: ()) {
- /* error: unresolved macro unresolved */"helloworld!"#0:[email protected]#2#;
+ /* error: unresolved macro unresolved */"helloworld!"#0:[email protected]#0#;
}
}
diff --git a/crates/hir-expand/src/attrs.rs b/crates/hir-expand/src/attrs.rs
index 7793e99532..686a3ad192 100644
--- a/crates/hir-expand/src/attrs.rs
+++ b/crates/hir-expand/src/attrs.rs
@@ -201,10 +201,12 @@ impl Attr {
span_map: SpanMapRef<'_>,
id: AttrId,
) -> Option<Attr> {
- let path = Interned::new(ModPath::from_src(db, ast.path()?, &mut |range| {
+ let path = ast.path()?;
+ let range = path.syntax().text_range();
+ let path = Interned::new(ModPath::from_src(db, path, &mut |range| {
span_map.span_for_range(range).ctx
})?);
- let span = span_map.span_for_range(ast.syntax().text_range());
+ let span = span_map.span_for_range(range);
let input = if let Some(ast::Expr::Literal(lit)) = ast.expr() {
let value = match lit.kind() {
ast::LiteralKind::String(string) => string.value()?.into(),
diff --git a/crates/hir-expand/src/builtin_fn_macro.rs b/crates/hir-expand/src/builtin_fn_macro.rs
index 0fd0c25dcc..6f3c01ba8c 100644
--- a/crates/hir-expand/src/builtin_fn_macro.rs
+++ b/crates/hir-expand/src/builtin_fn_macro.rs
@@ -19,14 +19,14 @@ use crate::{
};
macro_rules! register_builtin {
- ( LAZY: $(($name:ident, $kind: ident) => $expand:ident),* , EAGER: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
+ ( $LAZY:ident: $(($name:ident, $kind: ident) => $expand:ident),* , $EAGER:ident: $(($e_name:ident, $e_kind: ident) => $e_expand:ident),* ) => {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
- pub enum BuiltinFnLikeExpander {
+ pub enum $LAZY {
$($kind),*
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
- pub enum EagerExpander {
+ pub enum $EAGER {
$($e_kind),*
}
@@ -84,6 +84,17 @@ impl EagerExpander {
pub fn is_include(&self) -> bool {
matches!(self, EagerExpander::Include)
}
+
+ pub fn is_include_like(&self) -> bool {
+ matches!(
+ self,
+ EagerExpander::Include | EagerExpander::IncludeStr | EagerExpander::IncludeBytes
+ )
+ }
+
+ pub fn is_env_or_option_env(&self) -> bool {
+ matches!(self, EagerExpander::Env | EagerExpander::OptionEnv)
+ }
}
pub fn find_builtin_macro(
@@ -93,7 +104,7 @@ pub fn find_builtin_macro(
}
register_builtin! {
- LAZY:
+ BuiltinFnLikeExpander:
(column, Column) => line_expand,
(file, File) => file_expand,
(line, Line) => line_expand,
@@ -114,7 +125,7 @@ register_builtin! {
(format_args_nl, FormatArgsNl) => format_args_nl_expand,
(quote, Quote) => quote_expand,
- EAGER:
+ EagerExpander:
(compile_error, CompileError) => compile_error_expand,
(concat, Concat) => concat_expand,
(concat_idents, ConcatIdents) => concat_idents_expand,
@@ -426,22 +437,25 @@ fn use_panic_2021(db: &dyn ExpandDatabase, span: Span) -> bool {
}
}
-fn unquote_str(lit: &tt::Literal) -> Option<String> {
+fn unquote_str(lit: &tt::Literal) -> Option<(String, Span)> {
+ let span = lit.span;
let lit = ast::make::tokens::literal(&lit.to_string());
let token = ast::String::cast(lit)?;
- token.value().map(|it| it.into_owned())
+ token.value().map(|it| (it.into_owned(), span))
}
-fn unquote_char(lit: &tt::Literal) -> Option<char> {
+fn unquote_char(lit: &tt::Literal) -> Option<(char, Span)> {
+ let span = lit.span;
let lit = ast::make::tokens::literal(&lit.to_string());
let token = ast::Char::cast(lit)?;
- token.value()
+ token.value().zip(Some(span))
}
-fn unquote_byte_string(lit: &tt::Literal) -> Option<Vec<u8>> {
+fn unquote_byte_string(lit: &tt::Literal) -> Option<(Vec<u8>, Span)> {
+ let span = lit.span;
let lit = ast::make::tokens::literal(&lit.to_string());
let token = ast::ByteString::cast(lit)?;
- token.value().map(|it| it.into_owned())
+ token.value().map(|it| (it.into_owned(), span))
}
fn compile_error_expand(
@@ -452,7 +466,7 @@ fn compile_error_expand(
) -> ExpandResult<tt::Subtree> {
let err = match &*tt.token_trees {
[tt::TokenTree::Leaf(tt::Leaf::Literal(it))] => match unquote_str(it) {
- Some(unquoted) => ExpandError::other(unquoted.into_boxed_str()),
+ Some((unquoted, _)) => ExpandError::other(unquoted.into_boxed_str()),
None => ExpandError::other("`compile_error!` argument must be a string"),
},
_ => ExpandError::other("`compile_error!` argument must be a string"),
@@ -465,10 +479,16 @@ fn concat_expand(
_db: &dyn ExpandDatabase,
_arg_id: MacroCallId,
tt: &tt::Subtree,
- span: Span,
+ _: Span,
) -> ExpandResult<tt::Subtree> {
let mut err = None;
let mut text = String::new();
+ let mut span: Option<Span> = None;
+ let mut record_span = |s: Span| match &mut span {
+ Some(span) if span.anchor == s.anchor => span.range = span.range.cover(s.range),
+ Some(_) => (),
+ None => span = Some(s),
+ };
for (i, mut t) in tt.token_trees.iter().enumerate() {
// FIXME: hack on top of a hack: `$e:expr` captures get surrounded in parentheses
// to ensure the right parsing order, so skip the parentheses here. Ideally we'd
@@ -486,11 +506,14 @@ fn concat_expand(
// concat works with string and char literals, so remove any quotes.
// It also works with integer, float and boolean literals, so just use the rest
// as-is.
- if let Some(c) = unquote_char(it) {
+ if let Some((c, span)) = unquote_char(it) {
text.push(c);
+ record_span(span);
} else {
- let component = unquote_str(it).unwrap_or_else(|| it.text.to_string());
+ let (component, span) =
+ unquote_str(it).unwrap_or_else(|| (it.text.to_string(), it.span));
text.push_str(&component);
+ record_span(span);
}
}
// handle boolean literals
@@ -498,6 +521,7 @@ fn concat_expand(
if i % 2 == 0 && (id.text == "true" || id.text == "false") =>
{
text.push_str(id.text.as_str());
+ record_span(id.span);
}
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
_ => {
@@ -505,6 +529,7 @@ fn concat_expand(
}
}
}
+ let span = span.unwrap_or(tt.delimiter.open);
ExpandResult { value: quote!(span =>#text), err }
}
@@ -512,18 +537,25 @@ fn concat_bytes_expand(
_db: &dyn ExpandDatabase,
_arg_id: MacroCallId,
tt: &tt::Subtree,
- span: Span,
+ call_site: Span,
) -> ExpandResult<tt::Subtree> {
let mut bytes = Vec::new();
let mut err = None;
+ let mut span: Option<Span> = None;
+ let mut record_span = |s: Span| match &mut span {
+ Some(span) if span.anchor == s.anchor => span.range = span.range.cover(s.range),
+ Some(_) => (),
+ None => span = Some(s),
+ };
for (i, t) in tt.token_trees.iter().enumerate() {
match t {
tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
let token = ast::make::tokens::literal(&lit.to_string());
+ record_span(lit.span);
match token.kind() {
syntax::SyntaxKind::BYTE => bytes.push(token.text().to_owned()),
syntax::SyntaxKind::BYTE_STRING => {
- let components = unquote_byte_string(lit).unwrap_or_default();
+ let components = unquote_byte_string(lit).map_or(vec![], |(it, _)| it);
components.into_iter().for_each(|it| bytes.push(it.to_string()));
}
_ => {
@@ -534,7 +566,7 @@ fn concat_bytes_expand(
}
tt::TokenTree::Leaf(tt::Leaf::Punct(punct)) if i % 2 == 1 && punct.char == ',' => (),
tt::TokenTree::Subtree(tree) if tree.delimiter.kind == tt::DelimiterKind::Bracket => {
- if let Err(e) = concat_bytes_expand_subtree(tree, &mut bytes) {
+ if let Err(e) = concat_bytes_expand_subtree(tree, &mut bytes, &mut record_span) {
err.get_or_insert(e);
break;
}
@@ -546,17 +578,24 @@ fn concat_bytes_expand(
}
}
let value = tt::Subtree {
- delimiter: tt::Delimiter { open: span, close: span, kind: tt::DelimiterKind::Bracket },
+ delimiter: tt::Delimiter {
+ open: call_site,
+ close: call_site,
+ kind: tt::DelimiterKind::Bracket,
+ },
token_trees: {
Itertools::intersperse_with(
bytes.into_iter().map(|it| {
- tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal { text: it.into(), span }))
+ tt::TokenTree::Leaf(tt::Leaf::Literal(tt::Literal {
+ text: it.into(),
+ span: span.unwrap_or(call_site),
+ }))
}),
|| {
tt::TokenTree::Leaf(tt::Leaf::Punct(tt::Punct {
char: ',',
spacing: tt::Spacing::Alone,
- span,
+ span: call_site,
}))
},
)
@@ -569,13 +608,15 @@ fn concat_bytes_expand(
fn concat_bytes_expand_subtree(
tree: &tt::Subtree,
bytes: &mut Vec<String>,
+ mut record_span: impl FnMut(Span),
) -> Result<(), ExpandError> {
for (ti, tt) in tree.token_trees.iter().enumerate() {
match tt {
- tt::TokenTree::Leaf(tt::Leaf::Literal(lit)) => {
- let lit = ast::make::tokens::literal(&lit.to_string());
+ tt::TokenTree::Leaf(tt::Leaf::Literal(it)) => {
+ let lit = ast::make::tokens::literal(&it.to_string());
match lit.kind() {
syntax::SyntaxKind::BYTE | syntax::SyntaxKind::INT_NUMBER => {
+ record_span(it.span);
bytes.push(lit.text().to_owned())
}
_ => {
@@ -635,7 +676,7 @@ fn relative_file(
}
}
-fn parse_string(tt: &tt::Subtree) -> Result<String, ExpandError> {
+fn parse_string(tt: &tt::Subtree) -> Result<(String, Span), ExpandError> {
tt.token_trees
.first()
.and_then(|tt| match tt {
@@ -675,7 +716,7 @@ pub fn include_input_to_file_id(
arg_id: MacroCallId,
arg: &tt::Subtree,
) -> Result<FileId, ExpandError> {
- relative_file(db, arg_id, &parse_string(arg)?, false)
+ relative_file(db, arg_id, &parse_string(arg)?.0, false)
}
fn include_bytes_expand(
@@ -701,7 +742,7 @@ fn include_str_expand(
tt: &tt::Subtree,
span: Span,
) -> ExpandResult<tt::Subtree> {
- let path = match parse_string(tt) {
+ let (path, span) = match parse_string(tt) {
Ok(it) => it,
Err(e) => {
return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e)
@@ -736,7 +777,7 @@ fn env_expand(
tt: &tt::Subtree,
span: Span,
) -> ExpandResult<tt::Subtree> {
- let key = match parse_string(tt) {
+ let (key, span) = match parse_string(tt) {
Ok(it) => it,
Err(e) => {
return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e)
@@ -766,18 +807,24 @@ fn option_env_expand(
db: &dyn ExpandDatabase,
arg_id: MacroCallId,
tt: &tt::Subtree,
- span: Span,
+ call_site: Span,
) -> ExpandResult<tt::Subtree> {
- let key = match parse_string(tt) {
+ let (key, span) = match parse_string(tt) {
Ok(it) => it,
Err(e) => {
- return ExpandResult::new(tt::Subtree::empty(DelimSpan { open: span, close: span }), e)
+ return ExpandResult::new(
+ tt::Subtree::empty(DelimSpan { open: call_site, close: call_site }),
+ e,
+ )
}
};
- let dollar_crate = dollar_crate(span);
+ let dollar_crate = dollar_crate(call_site);
let expanded = match get_env_inner(db, arg_id, &key) {
- None => quote! {span => #dollar_crate::option::Option::None::<&str> },
- Some(s) => quote! {span => #dollar_crate::option::Option::Some(#s) },
+ None => quote! {call_site => #dollar_crate::option::Option::None::<&str> },
+ Some(s) => {
+ let s = quote! (span => #s);
+ quote! {call_site => #dollar_crate::option::Option::Some(#s) }
+ }
};
ExpandResult::ok(expanded)
diff --git a/crates/hir-expand/src/lib.rs b/crates/hir-expand/src/lib.rs
index cf1bdce7ed..91273a3ff0 100644
--- a/crates/hir-expand/src/lib.rs
+++ b/crates/hir-expand/src/lib.rs
@@ -323,6 +323,9 @@ impl HirFileIdExt for HirFileId {
}
pub trait MacroFileIdExt {
+ fn is_env_or_option_env(&self, db: &dyn ExpandDatabase) -> bool;
+ fn is_include_like_macro(&self, db: &dyn ExpandDatabase) -> bool;
+ fn eager_arg(&self, db: &dyn ExpandDatabase) -> Option<MacroCallId>;
fn expansion_level(self, db: &dyn ExpandDatabase) -> u32;
/// If this is a macro call, returns the syntax node of the call.
fn call_node(self, db: &dyn ExpandDatabase) -> InFile<SyntaxNode>;
@@ -389,18 +392,34 @@ impl MacroFileIdExt for MacroFileId {
db.lookup_intern_macro_call(self.macro_call_id).def.is_include()
}
+ fn is_include_like_macro(&self, db: &dyn ExpandDatabase) -> bool {
+ db.lookup_intern_macro_call(self.macro_call_id).def.is_include_like()
+ }
+
+ fn is_env_or_option_env(&self, db: &dyn ExpandDatabase) -> bool {
+ db.lookup_intern_macro_call(self.macro_call_id).def.is_env_or_option_env()
+ }
+
fn is_eager(&self, db: &dyn ExpandDatabase) -> bool {
- let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id);
+ let loc = db.lookup_intern_macro_call(self.macro_call_id);
matches!(loc.def.kind, MacroDefKind::BuiltInEager(..))
}
+ fn eager_arg(&self, db: &dyn ExpandDatabase) -> Option<MacroCallId> {
+ let loc = db.lookup_intern_macro_call(self.macro_call_id);
+ match &loc.kind {
+ MacroCallKind::FnLike { eager, .. } => eager.as_ref().map(|it| it.arg_id),
+ _ => None,
+ }
+ }
+
fn is_attr_macro(&self, db: &dyn ExpandDatabase) -> bool {
- let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id);
+ let loc = db.lookup_intern_macro_call(self.macro_call_id);
matches!(loc.kind, MacroCallKind::Attr { .. })
}
fn is_derive_attr_pseudo_expansion(&self, db: &dyn ExpandDatabase) -> bool {
- let loc: MacroCallLoc = db.lookup_intern_macro_call(self.macro_call_id);
+ let loc = db.lookup_intern_macro_call(self.macro_call_id);
loc.def.is_attribute_derive()
}
}
@@ -478,6 +497,14 @@ impl MacroDefId {
pub fn is_include(&self) -> bool {
matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include())
}
+
+ pub fn is_include_like(&self) -> bool {
+ matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_include_like())
+ }
+
+ pub fn is_env_or_option_env(&self) -> bool {
+ matches!(self.kind, MacroDefKind::BuiltInEager(expander, ..) if expander.is_env_or_option_env())
+ }
}
impl MacroCallLoc {
@@ -659,7 +686,7 @@ impl MacroCallKind {
/// ExpansionInfo mainly describes how to map text range between src and expanded macro
// FIXME: can be expensive to create, we should check the use sites and maybe replace them with
// simpler function calls if the map is only used once
-#[derive(Debug, Clone, PartialEq, Eq)]
+#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ExpansionInfo {
pub expanded: InMacroFile<SyntaxNode>,
/// The argument TokenTree or item for attributes
@@ -689,6 +716,22 @@ impl ExpansionInfo {
/// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
///
/// Note this does a linear search through the entire backing vector of the spanmap.
+ pub fn map_range_down_exact(
+ &self,
+ span: Span,
+ ) -> Option<InMacroFile<impl Iterator<Item = SyntaxToken> + '_>> {
+ let tokens = self
+ .exp_map
+ .ranges_with_span_exact(span)
+ .flat_map(move |range| self.expanded.value.covering_element(range).into_token());
+
+ Some(InMacroFile::new(self.expanded.file_id, tokens))
+ }
+
+ /// Maps the passed in file range down into a macro expansion if it is the input to a macro call.
+ /// Unlike [`map_range_down_exact`], this will consider spans that contain the given span.
+ ///
+ /// Note this does a linear search through the entire backing vector of the spanmap.
pub fn map_range_down(
&self,
span: Span,
@@ -745,7 +788,7 @@ impl ExpansionInfo {
InFile::new(
self.arg.file_id,
arg_map
- .ranges_with_span(span)
+ .ranges_with_span_exact(span)
.filter(|range| range.intersect(arg_range).is_some())
.collect(),
)
diff --git a/crates/hir-expand/src/quote.rs b/crates/hir-expand/src/quote.rs
index c1930c94f5..a31a111c91 100644
--- a/crates/hir-expand/src/quote.rs
+++ b/crates/hir-expand/src/quote.rs
@@ -266,10 +266,11 @@ mod tests {
let quoted = quote!(DUMMY =>#a);
assert_eq!(quoted.to_string(), "hello");
- let t = format!("{quoted:?}");
+ let t = format!("{quoted:#?}");
expect![[r#"
- SUBTREE $$ SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) } SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }
- IDENT hello SpanData { range: 0..0, anchor: SpanAnchor(FileId(937550), 0), ctx: SyntaxContextId(0) }"#]].assert_eq(&t);
+ SUBTREE $$ 937550:[email protected]#0 937550:[email protected]#0
+ IDENT hello 937550:[email protected]#0"#]]
+ .assert_eq(&t);
}
#[test]
diff --git a/crates/hir-expand/src/span_map.rs b/crates/hir-expand/src/span_map.rs
index 29fec16334..3713578478 100644
--- a/crates/hir-expand/src/span_map.rs
+++ b/crates/hir-expand/src/span_map.rs
@@ -1,4 +1,5 @@
//! Span maps for real files and macro expansions.
+
use span::{FileId, HirFileId, HirFileIdRepr, MacroFileId, Span, SyntaxContextId};
use syntax::{AstNode, TextRange};
use triomphe::Arc;
diff --git a/crates/hir/src/lib.rs b/crates/hir/src/lib.rs
index b16ec5540c..b922aa8e46 100644
--- a/crates/hir/src/lib.rs
+++ b/crates/hir/src/lib.rs
@@ -2617,6 +2617,15 @@ impl Macro {
}
}
+ pub fn is_env_or_option_env(&self, db: &dyn HirDatabase) -> bool {
+ match self.id {
+ MacroId::Macro2Id(it) => {
+ matches!(it.lookup(db.upcast()).expander, MacroExpander::BuiltInEager(eager) if eager.is_env_or_option_env())
+ }
+ MacroId::MacroRulesId(_) | MacroId::ProcMacroId(_) => false,
+ }
+ }
+
pub fn is_attr(&self, db: &dyn HirDatabase) -> bool {
matches!(self.kind(db), MacroKind::Attr)
}
diff --git a/crates/hir/src/semantics.rs b/crates/hir/src/semantics.rs
index ca47b37d68..02f9d5f31a 100644
--- a/crates/hir/src/semantics.rs
+++ b/crates/hir/src/semantics.rs
@@ -681,19 +681,20 @@ impl<'db> SemanticsImpl<'db> {
.filter(|&(_, include_file_id)| include_file_id == file_id)
{
let macro_file = invoc.as_macro_file();
- let expansion_info = cache
- .entry(macro_file)
- .or_insert_with(|| macro_file.expansion_info(self.db.upcast()));
+ let expansion_info = cache.entry(macro_file).or_insert_with(|| {
+ let exp_info = macro_file.expansion_info(self.db.upcast());
+
+ let InMacroFile { file_id, value } = exp_info.expanded();
+ self.cache(value, file_id.into());
+
+ exp_info
+ });
// Create the source analyzer for the macro call scope
let Some(sa) = self.analyze_no_infer(&self.parse_or_expand(expansion_info.call_file()))
else {
continue;
};
- {
- let InMacroFile { file_id: macro_file, value } = expansion_info.expanded();
- self.cache(value, macro_file.into());
- }
// get mapped token in the include! macro file
let span = span::SpanData {
@@ -702,7 +703,7 @@ impl<'db> SemanticsImpl<'db> {
ctx: SyntaxContextId::ROOT,
};
let Some(InMacroFile { file_id, value: mut mapped_tokens }) =
- expansion_info.map_range_down(span)
+ expansion_info.map_range_down_exact(span)
else {
continue;
};
@@ -753,22 +754,20 @@ impl<'db> SemanticsImpl<'db> {
let def_map = sa.resolver.def_map();
let mut stack: Vec<(_, SmallVec<[_; 2]>)> = vec![(file_id, smallvec![token])];
-
let mut process_expansion_for_token = |stack: &mut Vec<_>, macro_file| {
- let expansion_info = cache
- .entry(macro_file)
- .or_insert_with(|| macro_file.expansion_info(self.db.upcast()));
+ let exp_info = cache.entry(macro_file).or_insert_with(|| {
+ let exp_info = macro_file.expansion_info(self.db.upcast());
- {
- let InMacroFile { file_id, value } = expansion_info.expanded();
+ let InMacroFile { file_id, value } = exp_info.expanded();
self.cache(value, file_id.into());
- }
- let InMacroFile { file_id, value: mapped_tokens } =
- expansion_info.map_range_down(span)?;
+ exp_info
+ });
+
+ let InMacroFile { file_id, value: mapped_tokens } = exp_info.map_range_down(span)?;
let mapped_tokens: SmallVec<[_; 2]> = mapped_tokens.collect();
- // if the length changed we have found a mapping for the token
+ // we have found a mapping for the token if the vec is non-empty
let res = mapped_tokens.is_empty().not().then_some(());
// requeue the tokens we got from mapping our current token down
stack.push((HirFileId::from(file_id), mapped_tokens));
@@ -851,7 +850,13 @@ impl<'db> SemanticsImpl<'db> {
// remove any other token in this macro input, all their mappings are the
// same as this one
tokens.retain(|t| !text_range.contains_range(t.text_range()));
- process_expansion_for_token(&mut stack, file_id)
+
+ process_expansion_for_token(&mut stack, file_id).or(file_id
+ .eager_arg(self.db.upcast())
+ .and_then(|arg| {
+ // also descend into eager expansions
+ process_expansion_for_token(&mut stack, arg.as_macro_file())
+ }))
} else if let Some(meta) = ast::Meta::cast(parent) {
// attribute we failed expansion for earlier, this might be a derive invocation
// or derive helper attribute
diff --git a/crates/ide-completion/src/completions/env_vars.rs b/crates/ide-completion/src/completions/env_vars.rs
index 35e6b97eb7..4005753773 100644
--- a/crates/ide-completion/src/completions/env_vars.rs
+++ b/crates/ide-completion/src/completions/env_vars.rs
@@ -1,7 +1,10 @@
//! Completes environment variables defined by Cargo (https://doc.rust-lang.org/cargo/reference/environment-variables.html)
-use hir::Semantics;
-use ide_db::{syntax_helpers::node_ext::macro_call_for_string_token, RootDatabase};
-use syntax::ast::{self, IsString};
+use hir::MacroFileIdExt;
+use ide_db::syntax_helpers::node_ext::macro_call_for_string_token;
+use syntax::{
+ ast::{self, IsString},
+ AstToken,
+};
use crate::{
completions::Completions, context::CompletionContext, CompletionItem, CompletionItemKind,
@@ -32,10 +35,24 @@ const CARGO_DEFINED_VARS: &[(&str, &str)] = &[
pub(crate) fn complete_cargo_env_vars(
acc: &mut Completions,
ctx: &CompletionContext<'_>,
+ original: &ast::String,
expanded: &ast::String,
) -> Option<()> {
- guard_env_macro(expanded, &ctx.sema)?;
- let range = expanded.text_range_between_quotes()?;
+ let is_in_env_expansion = ctx
+ .sema
+ .hir_file_for(&expanded.syntax().parent()?)
+ .macro_file()
+ .map_or(false, |it| it.is_env_or_option_env(ctx.sema.db));
+ if !is_in_env_expansion {
+ let call = macro_call_for_string_token(expanded)?;
+ let makro = ctx.sema.resolve_macro_call(&call)?;
+ // We won't map into `option_env` as that generates `None` for non-existent env vars
+ // so fall back to this lookup
+ if !makro.is_env_or_option_env(ctx.sema.db) {
+ return None;
+ }
+ }
+ let range = original.text_range_between_quotes()?;
CARGO_DEFINED_VARS.iter().for_each(|&(var, detail)| {
let mut item = CompletionItem::new(CompletionItemKind::Keyword, range, var);
@@ -46,18 +63,6 @@ pub(crate) fn complete_cargo_env_vars(
Some(())
}
-fn guard_env_macro(string: &ast::String, semantics: &Semantics<'_, RootDatabase>) -> Option<()> {
- let call = macro_call_for_string_token(string)?;
- let name = call.path()?.segment()?.name_ref()?;
- let makro = semantics.resolve_macro_call(&call)?;
- let db = semantics.db;
-
- match name.text().as_str() {
- "env" | "option_env" if makro.kind(db) == hir::MacroKind::BuiltIn => Some(()),
- _ => None,
- }
-}
-
#[cfg(test)]
mod tests {
use crate::tests::{check_edit, completion_list};
@@ -68,7 +73,7 @@ mod tests {
&format!(
r#"
#[rustc_builtin_macro]
- macro_rules! {macro_name} {{
+ macro {macro_name} {{
($var:literal) => {{ 0 }}
}}
@@ -80,7 +85,7 @@ mod tests {
&format!(
r#"
#[rustc_builtin_macro]
- macro_rules! {macro_name} {{
+ macro {macro_name} {{
($var:literal) => {{ 0 }}
}}
diff --git a/crates/ide-completion/src/lib.rs b/crates/ide-completion/src/lib.rs
index 912f2fba2b..d89cfc8b6c 100644
--- a/crates/ide-completion/src/lib.rs
+++ b/crates/ide-completion/src/lib.rs
@@ -207,7 +207,7 @@ pub fn completions(
CompletionAnalysis::String { original, expanded: Some(expanded) } => {
completions::extern_abi::complete_extern_abi(acc, ctx, expanded);
completions::format_string::format_string(acc, ctx, original, expanded);
- completions::env_vars::complete_cargo_env_vars(acc, ctx, expanded);
+ completions::env_vars::complete_cargo_env_vars(acc, ctx, original, expanded);
}
CompletionAnalysis::UnexpandedAttrTT {
colon_prefix,
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs
index 1bda15255d..ddeeca5f7b 100644
--- a/crates/ide/src/goto_definition.rs
+++ b/crates/ide/src/goto_definition.rs
@@ -1,10 +1,10 @@
-use std::mem::discriminant;
+use std::{iter, mem::discriminant};
use crate::{
doc_links::token_as_doc_comment, navigation_target::ToNav, FilePosition, NavigationTarget,
RangeInfo, TryToNav,
};
-use hir::{AsAssocItem, AssocItem, DescendPreference, ModuleDef, Semantics};
+use hir::{AsAssocItem, AssocItem, DescendPreference, MacroFileIdExt, ModuleDef, Semantics};
use ide_db::{
base_db::{AnchoredPath, FileId, FileLoader},
defs::{Definition, IdentClass},
@@ -74,11 +74,13 @@ pub(crate) fn goto_definition(
.filter_map(|token| {
let parent = token.parent()?;
- if let Some(tt) = ast::TokenTree::cast(parent.clone()) {
- if let Some(x) = try_lookup_include_path(sema, tt, token.clone(), file_id) {
+ if let Some(token) = ast::String::cast(token.clone()) {
+ if let Some(x) = try_lookup_include_path(sema, token, file_id) {
return Some(vec![x]);
}
+ }
+ if ast::TokenTree::can_cast(parent.kind()) {
if let Some(x) = try_lookup_macro_def_in_macro_use(sema, token) {
return Some(vec![x]);
}
@@ -111,24 +113,17 @@ pub(crate) fn goto_definition(
fn try_lookup_include_path(
sema: &Semantics<'_, RootDatabase>,
- tt: ast::TokenTree,
- token: SyntaxToken,
+ token: ast::String,
file_id: FileId,
) -> Option<NavigationTarget> {
- let token = ast::String::cast(token)?;
- let path = token.value()?.into_owned();
- let macro_call = tt.syntax().parent().and_then(ast::MacroCall::cast)?;
- let name = macro_call.path()?.segment()?.name_ref()?;
- if !matches!(&*name.text(), "include" | "include_str" | "include_bytes") {
+ let file = sema.hir_file_for(&token.syntax().parent()?).macro_file()?;
+ if !iter::successors(Some(file), |file| file.parent(sema.db).macro_file())
+ // Check that we are in the eager argument expansion of an include macro
+ .any(|file| file.is_include_like_macro(sema.db) && file.eager_arg(sema.db).is_none())
+ {
return None;
}
-
- // Ignore non-built-in macros to account for shadowing
- if let Some(it) = sema.resolve_macro_call(&macro_call) {
- if !matches!(it.kind(sema.db), hir::MacroKind::BuiltIn) {
- return None;
- }
- }
+ let path = token.value()?;
let file_id = sema.db.resolve_path(AnchoredPath { anchor: file_id, path: &path })?;
let size = sema.db.file_text(file_id).len().try_into().ok()?;
@@ -1532,6 +1527,26 @@ fn main() {
}
#[test]
+ fn goto_include_has_eager_input() {
+ check(
+ r#"
+//- /main.rs
+#[rustc_builtin_macro]
+macro_rules! include_str {}
+#[rustc_builtin_macro]
+macro_rules! concat {}
+
+fn main() {
+ let str = include_str!(concat!("foo", ".tx$0t"));
+}
+//- /foo.txt
+// empty
+//^file
+"#,
+ );
+ }
+
+ #[test]
fn goto_doc_include_str() {
check(
r#"
diff --git a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
index bcfe542410..32ac6a94d8 100644
--- a/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
+++ b/crates/ide/src/syntax_highlighting/test_data/highlight_macros.html
@@ -94,7 +94,7 @@ pre { color: #DCDCCC; background: #3F3F3F; font-size: 22px; padd
<span class="brace">}</span>
-<span class="macro default_library library">include</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="none macro">concat</span><span class="punctuation macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"foo/"</span><span class="comma macro">,</span> <span class="string_literal macro">"foo.rs"</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
+<span class="macro default_library library">include</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="macro default_library library macro">concat</span><span class="macro_bang macro">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"foo/"</span><span class="string_literal macro">,</span> <span class="string_literal macro">"foo.rs"</span><span class="parenthesis macro">)</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
<span class="keyword">fn</span> <span class="function declaration">main</span><span class="parenthesis">(</span><span class="parenthesis">)</span> <span class="brace">{</span>
<span class="macro default_library library">format_args</span><span class="macro_bang">!</span><span class="parenthesis macro">(</span><span class="string_literal macro">"Hello, </span><span class="format_specifier">{</span><span class="format_specifier">}</span><span class="string_literal macro">!"</span><span class="comma macro">,</span> <span class="parenthesis macro">(</span><span class="numeric_literal macro">92</span><span class="comma macro">,</span><span class="parenthesis macro">)</span><span class="operator macro">.</span><span class="field library macro">0</span><span class="parenthesis macro">)</span><span class="semicolon">;</span>
diff --git a/crates/proc-macro-srv/src/tests/mod.rs b/crates/proc-macro-srv/src/tests/mod.rs
index 54a20357d2..11b008fc0b 100644
--- a/crates/proc-macro-srv/src/tests/mod.rs
+++ b/crates/proc-macro-srv/src/tests/mod.rs
@@ -8,7 +8,12 @@ use expect_test::expect;
#[test]
fn test_derive_empty() {
- assert_expand("DeriveEmpty", r#"struct S;"#, expect!["SUBTREE $$ 1 1"], expect!["SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"]);
+ assert_expand(
+ "DeriveEmpty",
+ r#"struct S;"#,
+ expect!["SUBTREE $$ 1 1"],
+ expect!["SUBTREE $$ 42:[email protected]#0 42:[email protected]#0"],
+ );
}
#[test]
@@ -21,15 +26,15 @@ fn test_derive_error() {
IDENT compile_error 1
PUNCH ! [alone] 1
SUBTREE () 1 1
- LITERAL "#[derive(DeriveError)] struct S ;" 1
+ LITERAL "#[derive(DeriveError)] struct S ;"1
PUNCH ; [alone] 1"##]],
expect![[r##"
- SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- IDENT compile_error SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH ! [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- SUBTREE () SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL "#[derive(DeriveError)] struct S ;" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH ; [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"##]],
+ SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
+ IDENT compile_error 42:[email protected]#0
+ PUNCH ! [alone] 42:[email protected]#0
+ SUBTREE () 42:[email protected]#0 42:[email protected]#0
+ LITERAL "#[derive(DeriveError)] struct S ;"42:[email protected]#0
+ PUNCH ; [alone] 42:[email protected]#0"##]],
);
}
@@ -42,20 +47,20 @@ fn test_fn_like_macro_noop() {
SUBTREE $$ 1 1
IDENT ident 1
PUNCH , [alone] 1
- LITERAL 0 1
+ LITERAL 01
PUNCH , [alone] 1
- LITERAL 1 1
+ LITERAL 11
PUNCH , [alone] 1
SUBTREE [] 1 1"#]],
expect![[r#"
- SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- IDENT ident SpanData { range: 0..5, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH , [alone] SpanData { range: 5..6, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL 0 SpanData { range: 7..8, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH , [alone] SpanData { range: 8..9, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL 1 SpanData { range: 10..11, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH , [alone] SpanData { range: 11..12, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- SUBTREE [] SpanData { range: 13..14, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 14..15, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+ SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
+ IDENT ident 42:[email protected]#0
+ PUNCH , [alone] 42:[email protected]#0
+ LITERAL 042:[email protected]#0
+ PUNCH , [alone] 42:[email protected]#0
+ LITERAL 142:[email protected]#0
+ PUNCH , [alone] 42:[email protected]#0
+ SUBTREE [] 42:[email protected]#0 42:[email protected]#0"#]],
);
}
@@ -70,10 +75,10 @@ fn test_fn_like_macro_clone_ident_subtree() {
PUNCH , [alone] 1
SUBTREE [] 1 1"#]],
expect![[r#"
- SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- IDENT ident SpanData { range: 0..5, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH , [alone] SpanData { range: 5..6, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- SUBTREE [] SpanData { range: 7..8, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 7..8, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+ SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
+ IDENT ident 42:[email protected]#0
+ PUNCH , [alone] 42:[email protected]#0
+ SUBTREE [] 42:[email protected]#0 42:[email protected]#0"#]],
);
}
@@ -86,8 +91,8 @@ fn test_fn_like_macro_clone_raw_ident() {
SUBTREE $$ 1 1
IDENT r#async 1"#]],
expect![[r#"
- SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- IDENT r#async SpanData { range: 0..7, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+ SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
+ IDENT r#async 42:[email protected]#0"#]],
);
}
@@ -100,8 +105,8 @@ fn test_fn_like_fn_like_span_join() {
SUBTREE $$ 1 1
IDENT r#joined 1"#]],
expect![[r#"
- SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- IDENT r#joined SpanData { range: 0..11, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+ SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
+ IDENT r#joined 42:[email protected]#0"#]],
);
}
@@ -116,10 +121,10 @@ fn test_fn_like_fn_like_span_ops() {
IDENT resolved_at_def_site 1
IDENT start_span 1"#]],
expect![[r#"
- SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- IDENT set_def_site SpanData { range: 0..150, anchor: SpanAnchor(FileId(41), 1), ctx: SyntaxContextId(0) }
- IDENT resolved_at_def_site SpanData { range: 13..33, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- IDENT start_span SpanData { range: 34..34, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+ SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
+ IDENT set_def_site 41:[email protected]#0
+ IDENT resolved_at_def_site 42:[email protected]#0
+ IDENT start_span 42:[email protected]#0"#]],
);
}
@@ -130,22 +135,22 @@ fn test_fn_like_mk_literals() {
r#""#,
expect![[r#"
SUBTREE $$ 1 1
- LITERAL b"byte_string" 1
- LITERAL 'c' 1
- LITERAL "string" 1
- LITERAL 3.14f64 1
- LITERAL 3.14 1
- LITERAL 123i64 1
- LITERAL 123 1"#]],
+ LITERAL b"byte_string"1
+ LITERAL 'c'1
+ LITERAL "string"1
+ LITERAL 3.14f641
+ LITERAL 3.141
+ LITERAL 123i641
+ LITERAL 1231"#]],
expect![[r#"
- SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL b"byte_string" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL 'c' SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL "string" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL 3.14f64 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL 3.14 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL 123i64 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL 123 SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+ SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
+ LITERAL b"byte_string"42:[email protected]#0
+ LITERAL 'c'42:[email protected]#0
+ LITERAL "string"42:[email protected]#0
+ LITERAL 3.14f6442:[email protected]#0
+ LITERAL 3.1442:[email protected]#0
+ LITERAL 123i6442:[email protected]#0
+ LITERAL 12342:[email protected]#0"#]],
);
}
@@ -159,9 +164,9 @@ fn test_fn_like_mk_idents() {
IDENT standard 1
IDENT r#raw 1"#]],
expect![[r#"
- SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- IDENT standard SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- IDENT r#raw SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"#]],
+ SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
+ IDENT standard 42:[email protected]#0
+ IDENT r#raw 42:[email protected]#0"#]],
);
}
@@ -172,48 +177,48 @@ fn test_fn_like_macro_clone_literals() {
r###"1u16, 2_u32, -4i64, 3.14f32, "hello bridge", "suffixed"suffix, r##"raw"##, 'a', b'b', c"null""###,
expect![[r###"
SUBTREE $$ 1 1
- LITERAL 1u16 1
+ LITERAL 1u161
PUNCH , [alone] 1
- LITERAL 2_u32 1
+ LITERAL 2_u321
PUNCH , [alone] 1
PUNCH - [alone] 1
- LITERAL 4i64 1
+ LITERAL 4i641
PUNCH , [alone] 1
- LITERAL 3.14f32 1
+ LITERAL 3.14f321
PUNCH , [alone] 1
- LITERAL "hello bridge" 1
+ LITERAL "hello bridge"1
PUNCH , [alone] 1
- LITERAL "suffixed"suffix 1
+ LITERAL "suffixed"suffix1
PUNCH , [alone] 1
- LITERAL r##"raw"## 1
+ LITERAL r##"raw"##1
PUNCH , [alone] 1
- LITERAL 'a' 1
+ LITERAL 'a'1
PUNCH , [alone] 1
- LITERAL b'b' 1
+ LITERAL b'b'1
PUNCH , [alone] 1
- LITERAL c"null" 1"###]],
+ LITERAL c"null"1"###]],
expect![[r###"
- SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL 1u16 SpanData { range: 0..4, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH , [alone] SpanData { range: 4..5, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL 2_u32 SpanData { range: 6..11, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH , [alone] SpanData { range: 11..12, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH - [alone] SpanData { range: 13..14, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL 4i64 SpanData { range: 14..18, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH , [alone] SpanData { range: 18..19, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL 3.14f32 SpanData { range: 20..27, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH , [alone] SpanData { range: 27..28, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL "hello bridge" SpanData { range: 29..43, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH , [alone] SpanData { range: 43..44, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL "suffixed"suffix SpanData { range: 45..61, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH , [alone] SpanData { range: 61..62, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL r##"raw"## SpanData { range: 63..73, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH , [alone] SpanData { range: 73..74, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL 'a' SpanData { range: 75..78, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH , [alone] SpanData { range: 78..79, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL b'b' SpanData { range: 80..84, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH , [alone] SpanData { range: 84..85, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL c"null" SpanData { range: 86..93, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"###]],
+ SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
+ LITERAL 1u1642:[email protected]#0
+ PUNCH , [alone] 42:[email protected]#0
+ LITERAL 2_u3242:[email protected]#0
+ PUNCH , [alone] 42:[email protected]#0
+ PUNCH - [alone] 42:[email protected]#0
+ LITERAL 4i6442:[email protected]#0
+ PUNCH , [alone] 42:[email protected]#0
+ LITERAL 3.14f3242:[email protected]#0
+ PUNCH , [alone] 42:[email protected]#0
+ LITERAL "hello bridge"42:[email protected]#0
+ PUNCH , [alone] 42:[email protected]#0
+ LITERAL "suffixed"suffix42:[email protected]#0
+ PUNCH , [alone] 42:[email protected]#0
+ LITERAL r##"raw"##42:[email protected]#0
+ PUNCH , [alone] 42:[email protected]#0
+ LITERAL 'a'42:[email protected]#0
+ PUNCH , [alone] 42:[email protected]#0
+ LITERAL b'b'42:[email protected]#0
+ PUNCH , [alone] 42:[email protected]#0
+ LITERAL c"null"42:[email protected]#0"###]],
);
}
@@ -231,15 +236,15 @@ fn test_attr_macro() {
IDENT compile_error 1
PUNCH ! [alone] 1
SUBTREE () 1 1
- LITERAL "#[attr_error(some arguments)] mod m {}" 1
+ LITERAL "#[attr_error(some arguments)] mod m {}"1
PUNCH ; [alone] 1"##]],
expect![[r##"
- SUBTREE $$ SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- IDENT compile_error SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH ! [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- SUBTREE () SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) } SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- LITERAL "#[attr_error(some arguments)] mod m {}" SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }
- PUNCH ; [alone] SpanData { range: 0..100, anchor: SpanAnchor(FileId(42), 2), ctx: SyntaxContextId(0) }"##]],
+ SUBTREE $$ 42:[email protected]#0 42:[email protected]#0
+ IDENT compile_error 42:[email protected]#0
+ PUNCH ! [alone] 42:[email protected]#0
+ SUBTREE () 42:[email protected]#0 42:[email protected]#0
+ LITERAL "#[attr_error(some arguments)] mod m {}"42:[email protected]#0
+ PUNCH ; [alone] 42:[email protected]#0"##]],
);
}
diff --git a/crates/proc-macro-srv/src/tests/utils.rs b/crates/proc-macro-srv/src/tests/utils.rs
index 9a1311d955..6050bc9e36 100644
--- a/crates/proc-macro-srv/src/tests/utils.rs
+++ b/crates/proc-macro-srv/src/tests/utils.rs
@@ -91,7 +91,7 @@ fn assert_expand_impl(
let res = expander
.expand(macro_name, fixture.into_subtree(call_site), attr, def_site, call_site, mixed_site)
.unwrap();
- expect_s.assert_eq(&format!("{res:?}"));
+ expect_s.assert_eq(&format!("{res:#?}"));
}
pub(crate) fn list() -> Vec<String> {
diff --git a/crates/span/src/hygiene.rs b/crates/span/src/hygiene.rs
index 4f6d792201..e4b0a26a6f 100644
--- a/crates/span/src/hygiene.rs
+++ b/crates/span/src/hygiene.rs
@@ -26,9 +26,19 @@ use salsa::{InternId, InternValue};
use crate::MacroCallId;
/// Interned [`SyntaxContextData`].
-#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
+#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SyntaxContextId(InternId);
+impl fmt::Debug for SyntaxContextId {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if f.alternate() {
+ write!(f, "{}", self.0.as_u32())
+ } else {
+ f.debug_tuple("SyntaxContextId").field(&self.0).finish()
+ }
+ }
+}
+
impl salsa::InternKey for SyntaxContextId {
fn from_intern_id(v: salsa::InternId) -> Self {
SyntaxContextId(v)
diff --git a/crates/span/src/lib.rs b/crates/span/src/lib.rs
index b2624762df..a29e5369a6 100644
--- a/crates/span/src/lib.rs
+++ b/crates/span/src/lib.rs
@@ -44,7 +44,7 @@ pub const FIXUP_ERASED_FILE_AST_ID_MARKER: ErasedFileAstId =
pub type Span = SpanData<SyntaxContextId>;
-#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct SpanData<Ctx> {
/// The text range of this span, relative to the anchor.
/// We need the anchor for incrementality, as storing absolute ranges will require
@@ -56,6 +56,26 @@ pub struct SpanData<Ctx> {
pub ctx: Ctx,
}
+impl<Ctx: fmt::Debug> fmt::Debug for SpanData<Ctx> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ if f.alternate() {
+ fmt::Debug::fmt(&self.anchor.file_id.index(), f)?;
+ f.write_char(':')?;
+ fmt::Debug::fmt(&self.anchor.ast_id.into_raw(), f)?;
+ f.write_char('@')?;
+ fmt::Debug::fmt(&self.range, f)?;
+ f.write_char('#')?;
+ self.ctx.fmt(f)
+ } else {
+ f.debug_struct("SpanData")
+ .field("range", &self.range)
+ .field("anchor", &self.anchor)
+ .field("ctx", &self.ctx)
+ .finish()
+ }
+ }
+}
+
impl<Ctx: Copy> SpanData<Ctx> {
pub fn eq_ignoring_ctx(self, other: Self) -> bool {
self.anchor == other.anchor && self.range == other.range
diff --git a/crates/span/src/map.rs b/crates/span/src/map.rs
index 7b42551099..1f396a1e97 100644
--- a/crates/span/src/map.rs
+++ b/crates/span/src/map.rs
@@ -1,7 +1,7 @@
//! A map that maps a span to every position in a file. Usually maps a span to some range of positions.
//! Allows bidirectional lookup.
-use std::hash::Hash;
+use std::{fmt, hash::Hash};
use stdx::{always, itertools::Itertools};
use syntax::{TextRange, TextSize};
@@ -52,7 +52,7 @@ where
/// Returns all [`TextRange`]s that correspond to the given span.
///
/// Note this does a linear search through the entire backing vector.
- pub fn ranges_with_span(&self, span: SpanData<S>) -> impl Iterator<Item = TextRange> + '_
+ pub fn ranges_with_span_exact(&self, span: SpanData<S>) -> impl Iterator<Item = TextRange> + '_
where
S: Copy,
{
@@ -65,6 +65,25 @@ where
})
}
+ /// Returns all [`TextRange`]s whose spans contain the given span.
+ ///
+ /// Note this does a linear search through the entire backing vector.
+ pub fn ranges_with_span(&self, span: SpanData<S>) -> impl Iterator<Item = TextRange> + '_
+ where
+ S: Copy,
+ {
+ self.spans.iter().enumerate().filter_map(move |(idx, &(end, s))| {
+ if s.anchor != span.anchor {
+ return None;
+ }
+ if !s.range.contains_range(span.range) {
+ return None;
+ }
+ let start = idx.checked_sub(1).map_or(TextSize::new(0), |prev| self.spans[prev].0);
+ Some(TextRange::new(start, end))
+ })
+ }
+
/// Returns the span at the given position.
pub fn span_at(&self, offset: TextSize) -> SpanData<S> {
let entry = self.spans.partition_point(|&(it, _)| it <= offset);
@@ -94,6 +113,16 @@ pub struct RealSpanMap {
end: TextSize,
}
+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().into_u32())?;
+ }
+ Ok(())
+ }
+}
+
impl RealSpanMap {
/// Creates a real file span map that returns absolute ranges (relative ranges to the root ast id).
pub fn absolute(file_id: FileId) -> Self {
diff --git a/crates/stdx/src/anymap.rs b/crates/stdx/src/anymap.rs
index 899cd8ac6b..d47b3d1647 100644
--- a/crates/stdx/src/anymap.rs
+++ b/crates/stdx/src/anymap.rs
@@ -194,21 +194,6 @@ impl<'a, A: ?Sized + Downcast, V: IntoBox<A>> VacantEntry<'a, A, V> {
mod tests {
use super::*;
- #[derive(Clone, Debug, PartialEq)]
- struct A(i32);
- #[derive(Clone, Debug, PartialEq)]
- struct B(i32);
- #[derive(Clone, Debug, PartialEq)]
- struct C(i32);
- #[derive(Clone, Debug, PartialEq)]
- struct D(i32);
- #[derive(Clone, Debug, PartialEq)]
- struct E(i32);
- #[derive(Clone, Debug, PartialEq)]
- struct F(i32);
- #[derive(Clone, Debug, PartialEq)]
- struct J(i32);
-
#[test]
fn test_varieties() {
fn assert_send<T: Send>() {}
diff --git a/crates/tt/src/lib.rs b/crates/tt/src/lib.rs
index eec88f8068..28289a6431 100644
--- a/crates/tt/src/lib.rs
+++ b/crates/tt/src/lib.rs
@@ -177,17 +177,19 @@ fn print_debug_subtree<S: fmt::Debug>(
let align = " ".repeat(level);
let Delimiter { kind, open, close } = &subtree.delimiter;
- let aux = match kind {
- DelimiterKind::Invisible => format!("$$ {:?} {:?}", open, close),
- DelimiterKind::Parenthesis => format!("() {:?} {:?}", open, close),
- DelimiterKind::Brace => format!("{{}} {:?} {:?}", open, close),
- DelimiterKind::Bracket => format!("[] {:?} {:?}", open, close),
+ let delim = match kind {
+ DelimiterKind::Invisible => "$$",
+ DelimiterKind::Parenthesis => "()",
+ DelimiterKind::Brace => "{}",
+ DelimiterKind::Bracket => "[]",
};
- if subtree.token_trees.is_empty() {
- write!(f, "{align}SUBTREE {aux}")?;
- } else {
- writeln!(f, "{align}SUBTREE {aux}")?;
+ write!(f, "{align}SUBTREE {delim} ",)?;
+ fmt::Debug::fmt(&open, f)?;
+ write!(f, " ")?;
+ fmt::Debug::fmt(&close, f)?;
+ if !subtree.token_trees.is_empty() {
+ writeln!(f)?;
for (idx, child) in subtree.token_trees.iter().enumerate() {
print_debug_token(f, child, level + 1)?;
if idx != subtree.token_trees.len() - 1 {
@@ -208,16 +210,24 @@ fn print_debug_token<S: fmt::Debug>(
match tkn {
TokenTree::Leaf(leaf) => match leaf {
- Leaf::Literal(lit) => write!(f, "{}LITERAL {} {:?}", align, lit.text, lit.span)?,
- Leaf::Punct(punct) => write!(
- f,
- "{}PUNCH {} [{}] {:?}",
- align,
- punct.char,
- if punct.spacing == Spacing::Alone { "alone" } else { "joint" },
- punct.span
- )?,
- Leaf::Ident(ident) => write!(f, "{}IDENT {} {:?}", align, ident.text, ident.span)?,
+ Leaf::Literal(lit) => {
+ write!(f, "{}LITERAL {}", align, lit.text)?;
+ fmt::Debug::fmt(&lit.span, f)?;
+ }
+ Leaf::Punct(punct) => {
+ write!(
+ f,
+ "{}PUNCH {} [{}] ",
+ align,
+ punct.char,
+ if punct.spacing == Spacing::Alone { "alone" } else { "joint" },
+ )?;
+ fmt::Debug::fmt(&punct.span, f)?;
+ }
+ Leaf::Ident(ident) => {
+ write!(f, "{}IDENT {} ", align, ident.text)?;
+ fmt::Debug::fmt(&ident.span, f)?;
+ }
},
TokenTree::Subtree(subtree) => {
print_debug_subtree(f, subtree, level)?;