refactor(Rome_js_syntax): Syntax `utils` improvements
* Remove unused methods
* Avoid allocating a vector for `first_comment`, `last_comment`, and `has_comments`
Random:
* Use 2021 Rust Edition for xtask package
MODIFY
crates/rome_formatter/src/formatter.rs
crates/rome_formatter/src/formatter.rs
@@ -15,8 +15,6 @@ use crate::{
soft_line_break_or_space, space_token, FormatElement, FormatOptions, FormatResult,
ToFormatElement,
};
-#[cfg(debug_assertions)]
-use rome_js_syntax::SyntaxNodeExt;
use rome_js_syntax::{AstNode, AstNodeList, AstSeparatedList, SyntaxNode, SyntaxToken};
use rome_rowan::api::SyntaxTriviaPiece;
use rome_rowan::Language;
@@ -75,7 +73,7 @@ impl Formatter {
cfg_if::cfg_if! {
if #[cfg(debug_assertions)] {
let printed_tokens = self.printed_tokens.into_inner();
- for token in root.tokens() {
+ for token in root.descendants_tokens() {
assert!(
printed_tokens.contains(&token),
"token was not seen by the formatter: {:?}",
@@ -377,7 +375,7 @@ impl Formatter {
// towards the first (that was not consumed by the previous loop)
for (index, piece) in pieces.rev() {
if let Some(comment) = piece.as_comments() {
- let is_single_line = comment.text().trim_start().starts_with("//");
+ let is_single_line = comment.text().starts_with("//");
let comment = Token::from(comment);
@@ -483,7 +481,7 @@ impl Formatter {
fn format_verbatim_node_or_token(&self, node: &SyntaxNode) -> FormatElement {
cfg_if::cfg_if! {
if #[cfg(debug_assertions)] {
- for token in node.tokens() {
+ for token in node.descendants_tokens() {
assert!(self.printed_tokens.borrow_mut().insert(token.clone()));
}
}

MODIFY
crates/rome_formatter/src/utils/mod.rs
crates/rome_formatter/src/utils/mod.rs
@@ -65,7 +65,7 @@ pub(crate) fn format_interpreter(
pub(crate) fn has_formatter_trivia(node: &SyntaxNode) -> bool {
let mut line_count = 0;
- for token in node.tokens() {
+ for token in node.descendants_tokens() {
for trivia in token.leading_trivia().pieces() {
if trivia.is_comments() {
return true;

MODIFY
crates/rome_js_syntax/src/lib.rs
crates/rome_js_syntax/src/lib.rs
@@ -20,7 +20,7 @@ pub use modifier_ext::*;
pub use rome_rowan::{SyntaxText, TextLen, TextRange, TextSize, TokenAtOffset, WalkEvent};
pub use stmt_ext::*;
pub use syntax_node::*;
-pub use util::{SyntaxNodeExt, SyntaxTokenExt};
+pub use util::SyntaxNodeExt;
use crate::JsSyntaxKind::*;
use rome_rowan::RawSyntaxKind;

MODIFY
crates/rome_js_syntax/src/util.rs
crates/rome_js_syntax/src/util.rs
-//! Extra utlities for untyped syntax nodes, syntax tokens, and AST nodes.
+//! Extra utilities for untyped syntax nodes, syntax tokens, and AST nodes.
-use crate::{AstNode, JsSyntaxKind, NodeOrToken, SyntaxElement, SyntaxNode, SyntaxToken};
+use crate::{AstNode, SyntaxNode, SyntaxToken};
/// Extensions to rowan's SyntaxNode
pub trait SyntaxNodeExt {
#[doc(hidden)]
fn to_node(&self) -> &SyntaxNode;
- /// Get all of the tokens of this node, recursively, including whitespace and comments.
- fn tokens(&self) -> Vec<SyntaxToken> {
- self.to_node()
- .descendants_with_tokens()
- .filter_map(|x| x.into_token())
- .collect()
- }
-
/// Check if the node is a certain AST node and that it can be casted to it.
fn is<T: AstNode>(&self) -> bool {
T::can_cast(self.to_node().kind())
@@ -39,98 +31,25 @@ pub trait SyntaxNodeExt {
T::cast(self.to_node().to_owned())
}
- /// Go over the descendants of this node, at every descendant call `func`, and keep traversing
- /// the descendants of that node if the function's return is `true`. If the function returns false
- /// then stop traversing the descendants of that node go on to the next child.
- ///
- /// For example:
- /// ```ignore
- /// ROOT
- /// CHILD // <-- Call `F` with CHILD, `F` returns `false` so go on to the next child...
- /// SUBCHILD
- /// CHILD // <-- Call `F` with CHILD, `F` return `true` so go on to the children of CHILD
- /// SUBCHILD // <-- Same thing
- /// SUBSUBCHILD
- /// CHILD // Go on to the next child and do the same thing
- /// ```
- fn descendants_with<F>(&self, func: &mut F)
- where
- F: FnMut(&SyntaxNode) -> bool,
- {
- for node in self.to_node().children() {
- if func(&node) {
- node.descendants_with(func);
- }
- }
- }
-
/// Whether the node contains any comments.
fn contains_comments(&self) -> bool {
- self.tokens()
- .iter()
+ self.to_node()
+ .descendants_tokens()
.any(|tok| tok.has_trailing_comments() || tok.has_leading_comments())
}
/// Whether the node contains trailing comments.
fn has_trailing_comments(&self) -> bool {
- self.tokens()
- .last()
+ self.to_node()
+ .last_token()
.map_or(false, |tok| tok.has_trailing_comments())
}
/// Whether the node contains leading comments.
fn has_leading_comments(&self) -> bool {
- self.tokens()
- .first()
- .map_or(false, |tok| tok.has_leading_comments())
- }
-
- /// Get the first child with a specific kind.
- fn child_with_kind(&self, kind: JsSyntaxKind) -> Option<SyntaxNode> {
- self.to_node().children().find(|child| child.kind() == kind)
- }
-
- /// Get the parent of this node, recursing through any grouping expressions
- fn expr_parent(&self) -> Option<SyntaxNode> {
- let parent = self.to_node().parent()?;
- if parent.kind() == JsSyntaxKind::JS_PARENTHESIZED_EXPRESSION {
- parent.parent()
- } else {
- Some(parent)
- }
- }
-
- /// Get the first direct-child in this node that can be casted to an AST node
- fn child_with_ast<T: AstNode>(&self) -> Option<T> {
- self.to_node().children().find_map(|child| child.try_to())
- }
-
- /// Same as [`descendants_with`](Self::descendants_with) but considers tokens too.
- fn descendants_with_tokens_with<F>(&self, func: &mut F)
- where
- F: FnMut(&SyntaxElement) -> bool,
- {
- for elem in self.to_node().children_with_tokens() {
- match &elem {
- NodeOrToken::Node(node) => {
- if func(&elem) {
- node.descendants_with_tokens_with(func)
- }
- }
- NodeOrToken::Token(_) => {
- let _ = func(&elem);
- }
- }
- }
- }
-
- /// Get a specific token in the node which matches a syntax kind.
- ///
- /// This does not consider tokens in descendant nodes
- fn token_with_kind(&self, kind: JsSyntaxKind) -> Option<SyntaxToken> {
self.to_node()
- .children_with_tokens()
- .find_map(|t| t.into_token().filter(|it| it.kind() == kind))
+ .first_token()
+ .map_or(false, |tok| tok.has_leading_comments())
}
}
@@ -140,90 +59,6 @@ impl SyntaxNodeExt for SyntaxNode {
}
}
-pub trait SyntaxTokenExt {
- fn to_token(&self) -> &SyntaxToken;
-
- /// Convert a comment to a more detailed representation.
- fn comment(&self) -> Option<Comment> {
- if self.to_token().kind() != JsSyntaxKind::COMMENT {
- return None;
- }
-
- let token = self.to_token();
- let (kind, content) = match &token.text()[0..2] {
- "//" => (
- CommentKind::Inline,
- token
- .text()
- .get(2..)
- .map(|x| x.to_string())
- .unwrap_or_default(),
- ),
- "/*" if token.text().chars().nth(2) == Some('*') => {
- let len = token.text().len();
- let end = if token.text().get(len - 2..len) == Some("*/") {
- len - 2
- } else {
- len
- };
- (
- CommentKind::JsDoc,
- token
- .text()
- .get(3..end)
- .map(|x| x.to_string())
- .unwrap_or_default(),
- )
- }
- "/*" => {
- let len = token.text().len();
- let end = if token.text().get(len - 2..len) == Some("*/") {
- len - 2
- } else {
- len
- };
- (
- CommentKind::JsDoc,
- token
- .text()
- .get(3..end)
- .map(|x| x.to_string())
- .unwrap_or_default(),
- )
- }
- _ => return None,
- };
- Some(Comment {
- kind,
- content,
- token: self.to_token().clone(),
- })
- }
-}
-
-impl SyntaxTokenExt for SyntaxToken {
- fn to_token(&self) -> &SyntaxToken {
- self
- }
-}
-
-#[derive(Debug, Clone, PartialEq, Eq, Hash)]
-pub struct Comment {
- pub kind: CommentKind,
- pub content: String,
- pub token: SyntaxToken,
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-pub enum CommentKind {
- /// A block comment which starts with `/**`
- JsDoc,
- /// A block comment which starts with `/*`
- Multiline,
- /// An inline comment which starts with `//`
- Inline,
-}
-
/// Concatenate tokens into a string
pub fn concat_tokens(tokens: &[SyntaxToken]) -> String {
tokens
@@ -231,25 +66,3 @@ pub fn concat_tokens(tokens: &[SyntaxToken]) -> String {
.map(|token| token.text().to_string())
.collect()
}
-
-/// Check whether a string contains a valid js linebreak consisting of any of these characters:
-/// `\n`, `\r`, `\u{2028}`, or `\u{2029}`
-pub fn contains_js_linebreak(string: impl AsRef<str>) -> bool {
- let text = string.as_ref();
- text.contains('\n')
- || text.contains('\r')
- || text.contains('\u{2028}')
- || text.contains('\u{2029}')
-}
-
-/// Check whether a string contains a valid js whitespace character
-// FIXME: this should account for stuff in the Zs unicode category
-pub fn contains_js_whitespace(string: impl AsRef<str>) -> bool {
- let text = string.as_ref();
- text.contains(' ')
- || text.contains('\u{000B}')
- || text.contains('\u{000C}')
- || text.contains('\u{0020}')
- || text.contains('\u{00A0}')
- || text.contains('\u{FEFF}')
-}

MODIFY
crates/rome_rowan/src/cursor.rs
crates/rome_rowan/src/cursor.rs
@@ -914,8 +914,7 @@ impl SyntaxNode {
pub fn first_token(&self) -> Option<SyntaxToken> {
self.descendants_with_tokens()
- .filter_map(|x| x.as_token().cloned())
- .next()
+ .find_map(|x| x.as_token().cloned())
}
pub fn last_token(&self) -> Option<SyntaxToken> {

MODIFY
crates/rslint_parser/src/parse.rs
crates/rslint_parser/src/parse.rs
@@ -146,7 +146,7 @@ fn parse_common(
/// assert_eq!(prop.syntax().text(), "2");
///
/// // Util has a function for yielding all tokens of a node.
-/// let tokens = untyped_expr_node.tokens();
+/// let tokens = untyped_expr_node.descendants_tokens().collect::<Vec<_>>();
///
/// assert_eq!(&util::concat_tokens(&tokens), "foo.bar[2]")
/// ```

MODIFY
crates/rslint_parser/src/tests.rs
crates/rslint_parser/src/tests.rs
@@ -185,14 +185,12 @@ fn assert_errors_are_absent<T>(program: &Parse<T>, path: &Path) {
#[test]
pub fn test_trivia_attached_to_tokens() {
- use rome_js_syntax::SyntaxNodeExt;
-
let text = "/**/let a = 1; // nice variable \n /*hey*/ let \t b = 2; // another nice variable";
let m = parse_module(text, 0);
- let tokens = m.syntax().tokens();
+ let mut tokens = m.syntax().descendants_tokens();
- let is_let = |x: &&SyntaxToken| x.text_trimmed() == "let";
- let first_let = tokens.iter().find(is_let).unwrap();
+ let is_let = |x: &SyntaxToken| x.text_trimmed() == "let";
+ let first_let = tokens.find(is_let).unwrap();
// first let leading trivia asserts
let pieces: Vec<_> = first_let.leading_trivia().pieces().collect();
@@ -205,7 +203,7 @@ pub fn test_trivia_attached_to_tokens() {
assert!(matches!(pieces.get(1), None));
// second let leading trivia asserts
- let second_let = tokens.iter().filter(is_let).nth(1).unwrap();
+ let second_let = tokens.find(is_let).unwrap();
let pieces: Vec<_> = second_let.leading_trivia().pieces().collect();
assert_eq!(4, pieces.len());
assert!(matches!(pieces.get(0).map(|x| x.text()), Some("\n")));

MODIFY
crates/rslint_parser/test_data/inline/err/jsx_namespace_member_element_name.rast
crates/rslint_parser/test_data/inline/err/jsx_namespace_member_element_name.rast
@@ -8,7 +8,7 @@ JsModule {
opening_element: JsxOpeningElement {
l_angle_token: L_ANGLE@0..1 "<" [] [],
name: JsxNamespaceName {
- namespace: JsxReferenceIdentifier {
+ namespace: JsxName {
value_token: JSX_IDENT@1..10 "namespace" [] [],
},
colon_token: COLON@10..11 ":" [] [],
@@ -24,7 +24,7 @@ JsModule {
l_angle_token: L_ANGLE@13..14 "<" [] [],
slash_token: SLASH@14..15 "/" [] [],
name: JsxNamespaceName {
- namespace: JsxReferenceIdentifier {
+ namespace: JsxName {
value_token: JSX_IDENT@15..24 "namespace" [] [],
},
colon_token: COLON@24..25 ":" [] [],
@@ -39,50 +39,65 @@ JsModule {
semicolon_token: SEMICOLON@27..28 ";" [] [],
},
JsExpressionStatement {
- expression: JsxTagExpression {
- tag: JsxElement {
- opening_element: JsxOpeningElement {
- l_angle_token: L_ANGLE@28..30 "<" [Newline("\n")] [],
- name: JsxMemberName {
- object: JsxNamespaceName {
- namespace: JsxReferenceIdentifier {
+ expression: JsUnknownExpression {
+ items: [
+ JsUnknown {
+ items: [
+ JsUnknown {
+ items: [
+ L_ANGLE@28..30 "<" [Newline("\n")] [],
+ JsUnknown {
+ items: [
+ JsUnknown {
+ items: [
+ JsxName {
value_token: JSX_IDENT@30..39 "namespace" [] [],
},
- colon_token: COLON@39..40 ":" [] [],
- name: JsxName {
+ COLON@39..40 ":" [] [],
+ JsxName {
value_token: JSX_IDENT@40..41 "a" [] [],
},
+ ],
},
- dot_token: DOT@41..42 "." [] [],
- member: JsName {
+ DOT@41..42 "." [] [],
+ JsName {
value_token: IDENT@42..43 "b" [] [],
},
+ ],
},
- attributes: JsxAttributeList [],
- r_angle_token: R_ANGLE@43..44 ">" [] [],
+ JsxAttributeList [],
+ R_ANGLE@43..44 ">" [] [],
+ ],
},
- children: missing (optional),
- closing_element: JsxClosingElement {
- l_angle_token: L_ANGLE@44..45 "<" [] [],
- slash_token: SLASH@45..46 "/" [] [],
- name: JsxMemberName {
- object: JsxNamespaceName {
- namespace: JsxReferenceIdentifier {
+ JsUnknown {
+ items: [
+ L_ANGLE@44..45 "<" [] [],
+ SLASH@45..46 "/" [] [],
+ JsUnknown {
+ items: [
+ JsUnknown {
+ items: [
+ JsxName {
value_token: JSX_IDENT@46..55 "namespace" [] [],
},
- colon_token: COLON@55..56 ":" [] [],
- name: JsxName {
+ COLON@55..56 ":" [] [],
+ JsxName {
value_token: JSX_IDENT@56..57 "a" [] [],
},
+ ],
},
- dot_token: DOT@57..58 "." [] [],
- member: JsName {
+ DOT@57..58 "." [] [],
+ JsName {
value_token: IDENT@58..59 "b" [] [],
},
+ ],
},
- r_angle_token: R_ANGLE@59..60 ">" [] [],
+ R_ANGLE@59..60 ">" [] [],
+ ],
},
+ ],
},
+ ],
},
semicolon_token: SEMICOLON@60..61 ";" [] [],
},
@@ -100,7 +115,7 @@ JsModule {
0: JSX_OPENING_ELEMENT@0..13
0: L_ANGLE@0..1 "<" [] []
1: JSX_NAMESPACE_NAME@1..12
- 0: JSX_REFERENCE_IDENTIFIER@1..10
+ 0: JSX_NAME@1..10
0: JSX_IDENT@1..10 "namespace" [] []
1: COLON@10..11 ":" [] []
2: JSX_NAME@11..12
@@ -112,7 +127,7 @@ JsModule {
0: L_ANGLE@13..14 "<" [] []
1: SLASH@14..15 "/" [] []
2: JSX_NAMESPACE_NAME@15..26
- 0: JSX_REFERENCE_IDENTIFIER@15..24
+ 0: JSX_NAME@15..24
0: JSX_IDENT@15..24 "namespace" [] []
1: COLON@24..25 ":" [] []
2: JSX_NAME@25..26
@@ -120,13 +135,13 @@ JsModule {
3: R_ANGLE@26..27 ">" [] []
1: SEMICOLON@27..28 ";" [] []
1: JS_EXPRESSION_STATEMENT@28..61
- 0: JSX_TAG_EXPRESSION@28..60
- 0: JSX_ELEMENT@28..60
- 0: JSX_OPENING_ELEMENT@28..44
+ 0: JS_UNKNOWN_EXPRESSION@28..60
+ 0: JS_UNKNOWN@28..60
+ 0: JS_UNKNOWN@28..44
0: L_ANGLE@28..30 "<" [Newline("\n")] []
- 1: JSX_MEMBER_NAME@30..43
- 0: JSX_NAMESPACE_NAME@30..41
- 0: JSX_REFERENCE_IDENTIFIER@30..39
+ 1: JS_UNKNOWN@30..43
+ 0: JS_UNKNOWN@30..41
+ 0: JSX_NAME@30..39
0: JSX_IDENT@30..39 "namespace" [] []
1: COLON@39..40 ":" [] []
2: JSX_NAME@40..41
@@ -136,13 +151,12 @@ JsModule {
0: IDENT@42..43 "b" [] []
2: JSX_ATTRIBUTE_LIST@43..43
3: R_ANGLE@43..44 ">" [] []
- 1: (empty)
- 2: JSX_CLOSING_ELEMENT@44..60
+ 1: JS_UNKNOWN@44..60
0: L_ANGLE@44..45 "<" [] []
1: SLASH@45..46 "/" [] []
- 2: JSX_MEMBER_NAME@46..59
- 0: JSX_NAMESPACE_NAME@46..57
- 0: JSX_REFERENCE_IDENTIFIER@46..55
+ 2: JS_UNKNOWN@46..59
+ 0: JS_UNKNOWN@46..57
+ 0: JSX_NAME@46..55
0: JSX_IDENT@46..55 "namespace" [] []
1: COLON@55..56 ":" [] []
2: JSX_NAME@56..57

MODIFY
crates/rslint_parser/test_data/inline/err/spread_attribute_error.rast
crates/rslint_parser/test_data/inline/err/spread_attribute_error.rast
@@ -25,8 +25,8 @@ JsModule {
semicolon_token: SEMICOLON@12..13 ";" [] [],
},
JsExpressionStatement {
- expression: JsxElementExpression {
- element: JsxSelfClosingElement {
+ expression: JsxTagExpression {
+ tag: JsxSelfClosingElement {
l_angle_token: L_ANGLE@13..15 "<" [Newline("\n")] [],
name: JsxName {
value_token: JSX_IDENT@15..17 "a" [] [Whitespace(" ")],
@@ -60,8 +60,8 @@ JsModule {
semicolon_token: SEMICOLON@35..36 ";" [] [],
},
JsExpressionStatement {
- expression: JsxElementExpression {
- element: JsxSelfClosingElement {
+ expression: JsxTagExpression {
+ tag: JsxSelfClosingElement {
l_angle_token: L_ANGLE@36..38 "<" [Newline("\n")] [],
name: JsxName {
value_token: JSX_IDENT@38..40 "a" [] [Whitespace(" ")],
@@ -85,8 +85,8 @@ JsModule {
semicolon_token: SEMICOLON@50..51 ";" [] [],
},
JsExpressionStatement {
- expression: JsxElementExpression {
- element: JsxSelfClosingElement {
+ expression: JsxTagExpression {
+ tag: JsxSelfClosingElement {
l_angle_token: L_ANGLE@51..53 "<" [Newline("\n")] [],
name: JsxName {
value_token: JSX_IDENT@53..55 "a" [] [Whitespace(" ")],
@@ -110,8 +110,8 @@ JsModule {
semicolon_token: SEMICOLON@63..64 ";" [] [],
},
JsExpressionStatement {
- expression: JsxElementExpression {
- element: JsxSelfClosingElement {
+ expression: JsxTagExpression {
+ tag: JsxSelfClosingElement {
l_angle_token: L_ANGLE@64..66 "<" [Newline("\n")] [],
name: JsxName {
value_token: JSX_IDENT@66..69 "div" [] [],
@@ -169,7 +169,7 @@ JsModule {
2: R_CURLY@11..12 "}" [] []
1: SEMICOLON@12..13 ";" [] []
1: JS_EXPRESSION_STATEMENT@13..36
- 0: JSX_ELEMENT_EXPRESSION@13..35
+ 0: JSX_TAG_EXPRESSION@13..35
0: JSX_SELF_CLOSING_ELEMENT@13..35
0: L_ANGLE@13..15 "<" [Newline("\n")] []
1: JSX_NAME@15..17
@@ -191,7 +191,7 @@ JsModule {
4: R_ANGLE@34..35 ">" [] []
1: SEMICOLON@35..36 ";" [] []
2: JS_EXPRESSION_STATEMENT@36..51
- 0: JSX_ELEMENT_EXPRESSION@36..50
+ 0: JSX_TAG_EXPRESSION@36..50
0: JSX_SELF_CLOSING_ELEMENT@36..50
0: L_ANGLE@36..38 "<" [Newline("\n")] []
1: JSX_NAME@38..40
@@ -208,7 +208,7 @@ JsModule {
4: R_ANGLE@49..50 ">" [] []
1: SEMICOLON@50..51 ";" [] []
3: JS_EXPRESSION_STATEMENT@51..64
- 0: JSX_ELEMENT_EXPRESSION@51..63
+ 0: JSX_TAG_EXPRESSION@51..63
0: JSX_SELF_CLOSING_ELEMENT@51..63
0: L_ANGLE@51..53 "<" [Newline("\n")] []
1: JSX_NAME@53..55
@@ -225,7 +225,7 @@ JsModule {
4: R_ANGLE@62..63 ">" [] []
1: SEMICOLON@63..64 ";" [] []
4: JS_EXPRESSION_STATEMENT@64..169
- 0: JSX_ELEMENT_EXPRESSION@64..168
+ 0: JSX_TAG_EXPRESSION@64..168
0: JSX_SELF_CLOSING_ELEMENT@64..168
0: L_ANGLE@64..66 "<" [Newline("\n")] []
1: JSX_NAME@66..69

MODIFY
xtask/Cargo.toml
xtask/Cargo.toml
[package]
name = "xtask"
version = "0.0.0"
-edition = "2018"
+edition = "2021"
publish = false
[dependencies]

MODIFY
xtask/bench/Cargo.toml
xtask/bench/Cargo.toml
[package]
name = "xtask_bench"
version = "0.0.0"
-edition = "2018"
+edition = "2021"
publish = false
[dependencies]

MODIFY
xtask/codegen/Cargo.toml
xtask/codegen/Cargo.toml
[package]
name = "xtask_codegen"
version = "0.0.0"
-edition = "2018"
+edition = "2021"
publish = false
[dependencies]

MODIFY
xtask/coverage/Cargo.toml
xtask/coverage/Cargo.toml
[package]
name = "xtask_coverage"
version = "0.0.0"
-edition = "2018"
+edition = "2021"
publish = false
[dependencies]

refactor(Rome_js_syntax): Syntax `utils` improvements (#2254)

* Remove unused methods
* Avoid allocating a vector for `first_comment`, `last_comment`, and `has_comments`

Random:
* Use 2021 Rust Edition for xtask package