From a6dde501dfe209f0734d6175fb95978ef80b4fd3 Mon Sep 17 00:00:00 2001 From: Lukas Wirth Date: Sun, 19 Sep 2021 18:30:29 +0200 Subject: [PATCH] Only strip derive attributes when preparing macro input --- Cargo.lock | 4 +- crates/hir_expand/src/db.rs | 42 ++++++++++---------- crates/mbe/src/syntax_bridge.rs | 68 +++++++++++++++++++------------- crates/mbe/src/tests.rs | 27 ++++++++----- crates/syntax/Cargo.toml | 2 +- crates/syntax/src/lib.rs | 4 +- crates/syntax/src/syntax_node.rs | 1 + 7 files changed, 87 insertions(+), 61 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a7e6ea676..47e88e877d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1286,9 +1286,9 @@ checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b" [[package]] name = "rowan" -version = "0.13.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a938f42b9c73aeece236481f37adb3debb7dfe3ae347cd6a45b5797d9ce4250" +checksum = "86f050538a65de83ae021294fb50d57f71fb4530fe79af755fc4d4cd61082c01" dependencies = [ "countme", "hashbrown", diff --git a/crates/hir_expand/src/db.rs b/crates/hir_expand/src/db.rs index e1ff646b8b..09ff972960 100644 --- a/crates/hir_expand/src/db.rs +++ b/crates/hir_expand/src/db.rs @@ -3,13 +3,13 @@ use std::sync::Arc; use base_db::{salsa, SourceDatabase}; -use itertools::Itertools; use limit::Limit; use mbe::{syntax_node_to_token_tree, ExpandError, ExpandResult}; +use rustc_hash::FxHashSet; use syntax::{ algo::diff, ast::{self, AttrsOwner, NameOwner}, - AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken, TextRange, T, + AstNode, GreenNode, Parse, SyntaxNode, SyntaxToken, T, }; use crate::{ @@ -151,7 +151,7 @@ pub fn expand_speculative( // Build the subtree and token mapping for the speculative args let censor = censor_for_macro_input(&loc, &speculative_args); let (mut tt, spec_args_tmap) = - mbe::syntax_node_to_token_tree_censored(&speculative_args, censor); + mbe::syntax_node_to_token_tree_censored(&speculative_args, &censor); let (attr_arg, token_id) = match loc.kind { MacroCallKind::Attr { invoc_attr_index, .. } => { @@ -305,7 +305,7 @@ fn macro_arg(db: &dyn AstDatabase, id: MacroCallId) -> Option Option Option { - match loc.kind { - MacroCallKind::FnLike { .. } => None, - MacroCallKind::Derive { derive_attr_index, .. } => match ast::Item::cast(node.clone()) { - Some(item) => item +fn censor_for_macro_input(loc: &MacroCallLoc, node: &SyntaxNode) -> FxHashSet { + (|| { + let censor = match loc.kind { + MacroCallKind::FnLike { .. } => return None, + MacroCallKind::Derive { derive_attr_index, .. } => ast::Item::cast(node.clone())? .attrs() - .map(|attr| attr.syntax().text_range()) .take(derive_attr_index as usize + 1) - .fold1(TextRange::cover), - None => None, - }, - MacroCallKind::Attr { invoc_attr_index, .. } => match ast::Item::cast(node.clone()) { - Some(item) => { - item.attrs().nth(invoc_attr_index as usize).map(|attr| attr.syntax().text_range()) - } - None => None, - }, - } + .filter(|attr| attr.simple_name().as_deref() == Some("derive")) + .map(|it| it.syntax().clone()) + .collect(), + MacroCallKind::Attr { invoc_attr_index, .. } => ast::Item::cast(node.clone())? + .attrs() + .nth(invoc_attr_index as usize) + .map(|attr| attr.syntax().clone()) + .into_iter() + .collect(), + }; + Some(censor) + })() + .unwrap_or_default() } fn macro_arg_text(db: &dyn AstDatabase, id: MacroCallId) -> Option { diff --git a/crates/mbe/src/syntax_bridge.rs b/crates/mbe/src/syntax_bridge.rs index fb082a4a05..41d0ceb85a 100644 --- a/crates/mbe/src/syntax_bridge.rs +++ b/crates/mbe/src/syntax_bridge.rs @@ -1,14 +1,13 @@ //! Conversions between [`SyntaxNode`] and [`tt::TokenTree`]. -use std::iter; - use parser::{ParseError, TreeSink}; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use syntax::{ ast::{self, make::tokens::doc_comment}, - tokenize, AstToken, Parse, SmolStr, SyntaxKind, + tokenize, AstToken, Parse, PreorderWithTokens, SmolStr, SyntaxElement, SyntaxKind, SyntaxKind::*, - SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, Token as RawToken, T, + SyntaxNode, SyntaxToken, SyntaxTreeBuilder, TextRange, TextSize, Token as RawToken, WalkEvent, + T, }; use tt::buffer::{Cursor, TokenBuffer}; @@ -19,14 +18,14 @@ use crate::{ /// Convert the syntax node to a `TokenTree` (what macro /// will consume). pub fn syntax_node_to_token_tree(node: &SyntaxNode) -> (tt::Subtree, TokenMap) { - syntax_node_to_token_tree_censored(node, None) + syntax_node_to_token_tree_censored(node, &Default::default()) } /// Convert the syntax node to a `TokenTree` (what macro will consume) /// with the censored range excluded. pub fn syntax_node_to_token_tree_censored( node: &SyntaxNode, - censor: Option, + censor: &FxHashSet, ) -> (tt::Subtree, TokenMap) { let global_offset = node.text_range().start(); let mut c = Convertor::new(node, global_offset, censor); @@ -424,8 +423,6 @@ impl<'a> SrcToken for (&'a RawToken, &'a str) { } } -impl RawConvertor<'_> {} - impl<'a> TokenConvertor for RawConvertor<'a> { type Token = (&'a RawToken, &'a str); @@ -455,30 +452,51 @@ impl<'a> TokenConvertor for RawConvertor<'a> { } } -struct Convertor { +struct Convertor<'c> { id_alloc: TokenIdAlloc, current: Option, - censor: Option, + preorder: PreorderWithTokens, + censor: &'c FxHashSet, range: TextRange, punct_offset: Option<(SyntaxToken, TextSize)>, } -impl Convertor { - fn new(node: &SyntaxNode, global_offset: TextSize, censor: Option) -> Convertor { - let first = node.first_token(); - let current = match censor { - Some(censor) => iter::successors(first, |token| token.next_token()) - .find(|token| !censor.contains_range(token.text_range())), - None => first, - }; +impl<'c> Convertor<'c> { + fn new( + node: &SyntaxNode, + global_offset: TextSize, + censor: &'c FxHashSet, + ) -> Convertor<'c> { + let range = node.text_range(); + let mut preorder = node.preorder_with_tokens(); + let first = Self::next_token(&mut preorder, censor); Convertor { id_alloc: { TokenIdAlloc { map: TokenMap::default(), global_offset, next_id: 0 } }, - current, - range: node.text_range(), + current: first, + preorder, + range, censor, punct_offset: None, } } + + fn next_token( + preorder: &mut PreorderWithTokens, + censor: &FxHashSet, + ) -> Option { + while let Some(ev) = preorder.next() { + let ele = match ev { + WalkEvent::Enter(ele) => ele, + _ => continue, + }; + match ele { + SyntaxElement::Token(t) => return Some(t), + SyntaxElement::Node(node) if censor.contains(&node) => preorder.skip_subtree(), + SyntaxElement::Node(_) => (), + } + } + None + } } #[derive(Debug)] @@ -511,7 +529,7 @@ impl SrcToken for SynToken { } } -impl TokenConvertor for Convertor { +impl TokenConvertor for Convertor<'_> { type Token = SynToken; fn convert_doc_comment(&self, token: &Self::Token) -> Option> { convert_doc_comment(token.token()) @@ -532,11 +550,7 @@ impl TokenConvertor for Convertor { if !&self.range.contains_range(curr.text_range()) { return None; } - self.current = match self.censor { - Some(censor) => iter::successors(curr.next_token(), |token| token.next_token()) - .find(|token| !censor.contains_range(token.text_range())), - None => curr.next_token(), - }; + self.current = Self::next_token(&mut self.preorder, self.censor); let token = if curr.kind().is_punct() { let range = curr.text_range(); let range = TextRange::at(range.start(), TextSize::of('.')); diff --git a/crates/mbe/src/tests.rs b/crates/mbe/src/tests.rs index 0b80c8f3ae..e6ac9754da 100644 --- a/crates/mbe/src/tests.rs +++ b/crates/mbe/src/tests.rs @@ -1,7 +1,7 @@ mod expand; mod rule; -use std::fmt::Write; +use std::{fmt::Write, iter}; use syntax::{ast, AstNode, NodeOrToken, SyntaxNode, WalkEvent}; use test_utils::assert_eq_text; @@ -252,27 +252,36 @@ struct Struct { let item = source_file.items().next().unwrap(); let attr = item.attrs().nth(1).unwrap(); - let (tt, _) = - syntax_node_to_token_tree_censored(item.syntax(), Some(attr.syntax().text_range())); + let (tt, _) = syntax_node_to_token_tree_censored( + item.syntax(), + &iter::once(attr.syntax().clone()).collect(), + ); expect_test::expect![[r##"# [attr0] # [attr2] struct Struct {field : ()}"##]] .assert_eq(&tt.to_string()); let source = r##" +#[attr0] #[derive(Derive0)] +#[attr1] #[derive(Derive1)] +#[attr2] #[derive(Derive2)] +#[attr3] struct Struct { field: () } "##; let source_file = ast::SourceFile::parse(source).ok().unwrap(); let item = source_file.items().next().unwrap(); - let attr = item.attrs().nth(1).unwrap(); + let derive_attr_index = 3; + let censor = item + .attrs() + .take(derive_attr_index as usize + 1) + .filter(|attr| attr.simple_name().as_deref() == Some("derive")) + .map(|it| it.syntax().clone()) + .collect(); - let (tt, _) = syntax_node_to_token_tree_censored( - item.syntax(), - Some(attr.syntax().text_range().cover_offset(0.into())), - ); - expect_test::expect![[r##"# [derive (Derive2)] struct Struct {field : ()}"##]] + let (tt, _) = syntax_node_to_token_tree_censored(item.syntax(), &censor); + expect_test::expect![[r##"# [attr0] # [attr1] # [attr2] # [derive (Derive2)] # [attr3] struct Struct {field : ()}"##]] .assert_eq(&tt.to_string()); } diff --git a/crates/syntax/Cargo.toml b/crates/syntax/Cargo.toml index 32c765191c..6a48a322a6 100644 --- a/crates/syntax/Cargo.toml +++ b/crates/syntax/Cargo.toml @@ -12,7 +12,7 @@ doctest = false [dependencies] cov-mark = "2.0.0-pre.1" itertools = "0.10.0" -rowan = "0.13.0" +rowan = "0.14.0" rustc_lexer = { version = "725.0.0", package = "rustc-ap-rustc_lexer" } rustc-hash = "1.1.0" once_cell = "1.3.1" diff --git a/crates/syntax/src/lib.rs b/crates/syntax/src/lib.rs index c79742d8ef..ea0de4f28d 100644 --- a/crates/syntax/src/lib.rs +++ b/crates/syntax/src/lib.rs @@ -52,8 +52,8 @@ pub use crate::{ ptr::{AstPtr, SyntaxNodePtr}, syntax_error::SyntaxError, syntax_node::{ - SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren, SyntaxToken, - SyntaxTreeBuilder, + PreorderWithTokens, SyntaxElement, SyntaxElementChildren, SyntaxNode, SyntaxNodeChildren, + SyntaxToken, SyntaxTreeBuilder, }, token_text::TokenText, }; diff --git a/crates/syntax/src/syntax_node.rs b/crates/syntax/src/syntax_node.rs index 8f643b2284..6e838dd229 100644 --- a/crates/syntax/src/syntax_node.rs +++ b/crates/syntax/src/syntax_node.rs @@ -31,6 +31,7 @@ pub type SyntaxToken = rowan::SyntaxToken; pub type SyntaxElement = rowan::SyntaxElement; pub type SyntaxNodeChildren = rowan::SyntaxNodeChildren; pub type SyntaxElementChildren = rowan::SyntaxElementChildren; +pub type PreorderWithTokens = rowan::api::PreorderWithTokens; #[derive(Default)] pub struct SyntaxTreeBuilder {