From 1e82e4f20fb1ac42607d9df1fa1ddd911a9ffc10 Mon Sep 17 00:00:00 2001 From: Micha Reiser Date: Fri, 12 Nov 2021 13:05:54 +0100 Subject: [PATCH] refactor(rslint_parser): Update Literals AST Facade Refines the AST Facade for literals to match the grammar proposed in #1719. This change is part of #1725. * Introduce new `JsAnyLiteral` * Split `Literal` into `JsStringLiteral`, `JsBooleanLiteral`, `JsNullLiteral`, `JsRegexLiteral`, and `JsBigIntLiteral`. This allows to implement custom methods on the corresponding literal nodes. * Renames the `number` and kinds to `JS_NUMBER_LITERAL_TOKEN` `JS_STRING_LITERAL_TOKEN` to avoid conflicts with the TS `number` keyword (and keep symmetry). * Removed some unused keywords and special handling inside of the code gen. --- crates/rome_formatter/src/cst.rs | 37 ++- crates/rome_formatter/src/format_json.rs | 23 +- crates/rome_formatter/src/formatter.rs | 8 +- .../src/ts/expressions/expression.rs | 2 +- .../src/ts/expressions/literal.rs | 60 +++- crates/rome_formatter/src/ts/mod.rs | 1 - .../rome_formatter/src/ts/props/prop_name.rs | 3 +- crates/rome_formatter/src/ts/tokens/mod.rs | 1 - .../src/ts/tokens/string_token.rs | 16 - crates/rslint_lexer/src/highlight.rs | 8 +- crates/rslint_lexer/src/lib.rs | 10 +- crates/rslint_lexer/src/tests.rs | 80 ++--- crates/rslint_parser/src/ast.rs | 15 +- crates/rslint_parser/src/ast/expr_ext.rs | 85 +----- .../rslint_parser/src/ast/generated/nodes.rs | 280 +++++++++++++----- .../rslint_parser/src/ast/generated/tokens.rs | 42 --- crates/rslint_parser/src/ast/stmt_ext.rs | 21 +- crates/rslint_parser/src/ast/ts_ext.rs | 2 +- crates/rslint_parser/src/lib.rs | 2 +- crates/rslint_parser/src/numbers.rs | 67 +++-- crates/rslint_parser/src/syntax/decl.rs | 12 +- crates/rslint_parser/src/syntax/expr.rs | 46 ++- crates/rslint_parser/src/syntax/program.rs | 6 +- crates/rslint_parser/src/syntax/stmt.rs | 24 +- crates/rslint_parser/src/syntax/typescript.rs | 32 +- crates/rslint_parser/src/syntax/util.rs | 2 +- .../inline/err/binary_expressions_err.rast | 4 +- .../err/binding_identifier_invalid.rast | 12 +- .../inline/err/do_while_stmt_err.rast | 4 +- .../inline/err/export_decl_not_top_level.rast | 4 +- .../test_data/inline/err/for_stmt_err.rast | 8 +- .../test_data/inline/err/if_stmt_err.rast | 4 +- .../inline/err/import_decl_not_top_level.rast | 4 +- .../inline/err/invalid_method_recover.rast | 8 +- .../err/object_expr_error_prop_name.rast | 6 +- .../object_expr_non_ident_literal_prop.rast | 4 +- .../paren_or_arrow_expr_invalid_params.rast | 4 +- .../test_data/inline/err/throw_stmt_err.rast | 4 +- .../test_data/inline/err/var_decl_err.rast | 8 +- .../test_data/inline/err/while_stmt_err.rast | 6 +- .../test_data/inline/ok/assign_expr.rast | 4 +- .../inline/ok/binary_expressions.rast | 96 +++--- .../test_data/inline/ok/bracket_expr.rast | 12 +- .../test_data/inline/ok/continue_stmt.rast | 2 +- .../inline/ok/do_while_statement.rast | 28 +- .../test_data/inline/ok/export.rast | 4 +- .../test_data/inline/ok/for_stmt.rast | 8 +- .../test_data/inline/ok/if_stmt.rast | 10 +- .../test_data/inline/ok/import_call.rast | 4 +- .../test_data/inline/ok/import_decl.rast | 4 +- .../test_data/inline/ok/literals.rast | 22 +- .../test_data/inline/ok/new_exprs.rast | 8 +- .../inline/ok/object_expr_assign_prop.rast | 4 +- .../ok/object_expr_ident_literal_prop.rast | 2 +- .../test_data/inline/ok/object_prop_name.rast | 16 +- .../inline/ok/paren_or_arrow_expr.rast | 8 +- .../test_data/inline/ok/semicolons.rast | 2 +- .../test_data/inline/ok/sequence_expr.rast | 20 +- .../test_data/inline/ok/throw_stmt.rast | 8 +- .../test_data/inline/ok/var_decl.rast | 12 +- .../test_data/inline/ok/while_stmt.rast | 6 +- crates/rslint_syntax/src/generated.rs | 30 +- xtask/js.ungram | 61 ++-- xtask/src/codegen/ast.rs | 16 +- xtask/src/codegen/generate_syntax_kinds.rs | 4 + xtask/src/codegen/kinds_src.rs | 17 +- 66 files changed, 758 insertions(+), 615 deletions(-) delete mode 100644 crates/rome_formatter/src/ts/tokens/mod.rs delete mode 100644 crates/rome_formatter/src/ts/tokens/string_token.rs diff --git a/crates/rome_formatter/src/cst.rs b/crates/rome_formatter/src/cst.rs index 7197586b35fb..364fc738fe17 100644 --- a/crates/rome_formatter/src/cst.rs +++ b/crates/rome_formatter/src/cst.rs @@ -1,15 +1,16 @@ -use crate::{token, FormatElement, FormatResult, Formatter, ToFormatElement}; +use crate::{FormatElement, FormatResult, Formatter, ToFormatElement}; use rslint_parser::ast::{ ArgList, ArrayExpr, ArrayPattern, ArrowExpr, AssignPattern, CallExpr, ClassBody, ClassDecl, ClassProp, Condition, ConstructorParameters, FnDecl, ForInStmt, ForStmt, ForStmtInit, - ForStmtTest, ForStmtUpdate, Getter, IdentProp, JsBlockStatement, JsCaseClause, JsCatchClause, - JsContinueStatement, JsDebuggerStatement, JsDefaultClause, JsDoWhileStatement, - JsEmptyStatement, JsExpressionStatement, JsFinallyClause, JsIfStatement, JsLabeledStatement, - JsReturnStatement, JsScript, JsSwitchStatement, JsTryStatement, JsVariableDeclarationStatement, - JsVariableDeclarator, JsWhileStatement, JsWithStatement, Literal, LiteralProp, Name, NameRef, + ForStmtTest, ForStmtUpdate, Getter, IdentProp, JsBlockStatement, JsBooleanLiteral, + JsCaseClause, JsCatchClause, JsContinueStatement, JsDebuggerStatement, JsDefaultClause, + JsDoWhileStatement, JsEmptyStatement, JsExpressionStatement, JsFinallyClause, JsIfStatement, + JsLabeledStatement, JsNullLiteral, JsNumberLiteral, JsReturnStatement, JsScript, + JsStringLiteral, JsSwitchStatement, JsTryStatement, JsVariableDeclarationStatement, + JsVariableDeclarator, JsWhileStatement, JsWithStatement, LiteralProp, Name, NameRef, ObjectExpr, ParameterList, SequenceExpr, Setter, SinglePattern, }; -use rslint_parser::{AstNode, AstToken, SyntaxKind, SyntaxNode, SyntaxToken}; +use rslint_parser::{AstNode, SyntaxKind, SyntaxNode}; impl ToFormatElement for SyntaxNode { fn to_format_element(&self, formatter: &Formatter) -> FormatResult { @@ -23,7 +24,16 @@ impl ToFormatElement for SyntaxNode { SyntaxKind::ASSIGN_PATTERN => AssignPattern::cast(self.clone()) .unwrap() .to_format_element(formatter), - SyntaxKind::LITERAL => Literal::cast(self.clone()) + SyntaxKind::JS_BOOLEAN_LITERAL => JsBooleanLiteral::cast(self.clone()) + .unwrap() + .to_format_element(formatter), + SyntaxKind::JS_STRING_LITERAL => JsStringLiteral::cast(self.clone()) + .unwrap() + .to_format_element(formatter), + SyntaxKind::JS_NULL_LITERAL => JsNullLiteral::cast(self.clone()) + .unwrap() + .to_format_element(formatter), + SyntaxKind::JS_NUMBER_LITERAL => JsNumberLiteral::cast(self.clone()) .unwrap() .to_format_element(formatter), SyntaxKind::NAME => Name::cast(self.clone()) @@ -171,14 +181,3 @@ impl ToFormatElement for SyntaxNode { } } } - -impl ToFormatElement for SyntaxToken { - fn to_format_element(&self, formatter: &Formatter) -> FormatResult { - match self.kind() { - SyntaxKind::STRING => rslint_parser::ast::String::cast(self.clone()) - .unwrap() - .to_format_element(formatter), - _ => Ok(token(self.text())), - } - } -} diff --git a/crates/rome_formatter/src/format_json.rs b/crates/rome_formatter/src/format_json.rs index 5d159d17059e..334412156dab 100644 --- a/crates/rome_formatter/src/format_json.rs +++ b/crates/rome_formatter/src/format_json.rs @@ -4,17 +4,18 @@ use crate::{ space_token, token, }; use rslint_parser::ast::{ - ArrayExpr, GroupingExpr, Literal, LiteralProp, ObjectExpr, ObjectProp, UnaryExpr, + ArrayExpr, GroupingExpr, JsBooleanLiteral, JsNullLiteral, JsNumberLiteral, JsStringLiteral, + LiteralProp, ObjectExpr, ObjectProp, UnaryExpr, }; -use rslint_parser::{parse_text, AstNode, SyntaxKind, SyntaxNode, SyntaxToken}; +use rslint_parser::{parse_text, AstNode, SyntaxKind, SyntaxNode, SyntaxNodeExt, SyntaxToken}; fn tokenize_token(syntax_token: SyntaxToken) -> FormatElement { match syntax_token.kind() { SyntaxKind::NULL_KW => token("null"), SyntaxKind::TRUE_KW => token("true"), SyntaxKind::FALSE_KW => token("false"), - SyntaxKind::STRING => token(syntax_token.text()), - SyntaxKind::NUMBER => token(syntax_token.text()), + SyntaxKind::JS_STRING_LITERAL_TOKEN => token(syntax_token.text()), + SyntaxKind::JS_NUMBER_LITERAL_TOKEN => token(syntax_token.text()), SyntaxKind::MINUS => token("-"), _ => panic!("Unsupported JSON token {:?}", syntax_token), } @@ -22,9 +23,17 @@ fn tokenize_token(syntax_token: SyntaxToken) -> FormatElement { fn tokenize_node(node: SyntaxNode) -> FormatElement { match node.kind() { - SyntaxKind::LITERAL => { - let literal = Literal::cast(node).unwrap(); - tokenize_token(literal.token()) + SyntaxKind::JS_STRING_LITERAL => { + tokenize_token(node.to::().value_token().unwrap()) + } + SyntaxKind::JS_BOOLEAN_LITERAL => { + tokenize_token(node.to::().value_token().unwrap()) + } + SyntaxKind::JS_NULL_LITERAL => { + tokenize_token(node.to::().value_token().unwrap()) + } + SyntaxKind::JS_NUMBER_LITERAL => { + tokenize_token(node.to::().value_token().unwrap()) } SyntaxKind::UNARY_EXPR => { let expr = UnaryExpr::cast(node).unwrap(); diff --git a/crates/rome_formatter/src/formatter.rs b/crates/rome_formatter/src/formatter.rs index 1217fe8767a2..a48d018b2794 100644 --- a/crates/rome_formatter/src/formatter.rs +++ b/crates/rome_formatter/src/formatter.rs @@ -87,8 +87,8 @@ impl Formatter { /// use rome_rowan::{NodeOrToken, TreeBuilder}; /// /// let mut builder = TreeBuilder::<'_, JsLanguage>::new(); - /// builder.start_node(SyntaxKind::LITERAL); - /// builder.token(SyntaxKind::STRING, "'abc'"); + /// builder.start_node(SyntaxKind::JS_STRING_LITERAL); + /// builder.token(SyntaxKind::JS_STRING_LITERAL_TOKEN, "'abc'"); /// builder.finish_node(); /// let node = builder.finish(); /// @@ -97,10 +97,10 @@ impl Formatter { /// let formatter = Formatter::default(); /// let result = formatter.format_token(&syntax_token); /// - /// assert_eq!(Ok(token("\"abc\"")), result) + /// assert_eq!(Ok(token("'abc'")), result) /// ``` pub fn format_token(&self, syntax_token: &SyntaxToken) -> FormatResult { - syntax_token.to_format_element(self) + Ok(token(syntax_token.text())) } /// Formats each child and returns the result as a list. diff --git a/crates/rome_formatter/src/ts/expressions/expression.rs b/crates/rome_formatter/src/ts/expressions/expression.rs index 91d22b89bcd7..fd0f4231e984 100644 --- a/crates/rome_formatter/src/ts/expressions/expression.rs +++ b/crates/rome_formatter/src/ts/expressions/expression.rs @@ -5,7 +5,7 @@ impl ToFormatElement for JsAnyExpression { fn to_format_element(&self, formatter: &Formatter) -> FormatResult { match self { JsAnyExpression::ArrowExpr(arrow) => arrow.to_format_element(formatter), - JsAnyExpression::Literal(literal) => literal.to_format_element(formatter), + JsAnyExpression::JsAnyLiteral(literal) => literal.to_format_element(formatter), JsAnyExpression::Template(_) => todo!(), JsAnyExpression::NameRef(name_ref) => name_ref.to_format_element(formatter), JsAnyExpression::ThisExpr(_) => todo!(), diff --git a/crates/rome_formatter/src/ts/expressions/literal.rs b/crates/rome_formatter/src/ts/expressions/literal.rs index 418eb619e4e8..f73702424a58 100644 --- a/crates/rome_formatter/src/ts/expressions/literal.rs +++ b/crates/rome_formatter/src/ts/expressions/literal.rs @@ -1,9 +1,61 @@ -use rslint_parser::ast::Literal; +use crate::{token, FormatElement, FormatResult, Formatter, ToFormatElement}; +use rslint_parser::ast::{ + JsAnyLiteral, JsBigIntLiteral, JsBooleanLiteral, JsNullLiteral, JsNumberLiteral, + JsStringLiteral, +}; -use crate::{FormatElement, FormatResult, Formatter, ToFormatElement}; +impl ToFormatElement for JsStringLiteral { + fn to_format_element(&self, _: &Formatter) -> FormatResult { + let value_token = self.value_token()?; + let quoted = value_token.text(); -impl ToFormatElement for Literal { + // uses single quotes + if quoted.starts_with('\'') { + let mut double_quoted = String::from(quoted); + double_quoted.replace_range(0..1, "\""); + double_quoted.replace_range(double_quoted.len() - 1..double_quoted.len(), "\""); + Ok(token(double_quoted.as_str())) + } else { + Ok(token(quoted)) + } + } +} + +impl ToFormatElement for JsBooleanLiteral { + fn to_format_element(&self, formatter: &Formatter) -> FormatResult { + formatter.format_token(&self.value_token()?) + } +} + +impl ToFormatElement for JsNullLiteral { + fn to_format_element(&self, formatter: &Formatter) -> FormatResult { + formatter.format_token(&self.value_token()?) + } +} + +impl ToFormatElement for JsNumberLiteral { + fn to_format_element(&self, formatter: &Formatter) -> FormatResult { + formatter.format_token(&self.value_token()?) + } +} + +impl ToFormatElement for JsBigIntLiteral { + fn to_format_element(&self, formatter: &Formatter) -> FormatResult { + formatter.format_token(&self.value_token()?) + } +} + +impl ToFormatElement for JsAnyLiteral { fn to_format_element(&self, formatter: &Formatter) -> FormatResult { - formatter.format_token(&self.token()) + match self { + JsAnyLiteral::JsBooleanLiteral(boolean) => boolean.to_format_element(formatter), + JsAnyLiteral::JsStringLiteral(string_literal) => { + string_literal.to_format_element(formatter) + } + JsAnyLiteral::JsNumberLiteral(number) => number.to_format_element(formatter), + JsAnyLiteral::JsBigIntLiteral(big_int) => big_int.to_format_element(formatter), + JsAnyLiteral::JsNullLiteral(null_literal) => null_literal.to_format_element(formatter), + JsAnyLiteral::JsRegexLiteral(_) => todo!(), + } } } diff --git a/crates/rome_formatter/src/ts/mod.rs b/crates/rome_formatter/src/ts/mod.rs index ad3a3aa77aaa..cccc3534a5d5 100644 --- a/crates/rome_formatter/src/ts/mod.rs +++ b/crates/rome_formatter/src/ts/mod.rs @@ -17,7 +17,6 @@ mod script; mod setter; mod spread; mod statements; -mod tokens; #[cfg(test)] mod test { diff --git a/crates/rome_formatter/src/ts/props/prop_name.rs b/crates/rome_formatter/src/ts/props/prop_name.rs index d84d656ac629..7524118e521c 100644 --- a/crates/rome_formatter/src/ts/props/prop_name.rs +++ b/crates/rome_formatter/src/ts/props/prop_name.rs @@ -6,7 +6,8 @@ impl ToFormatElement for PropName { fn to_format_element(&self, formatter: &Formatter) -> FormatResult { match self { PropName::ComputedPropertyName(_) => todo!(), - PropName::Literal(literal) => literal.to_format_element(formatter), + PropName::JsStringLiteral(literal) => literal.to_format_element(formatter), + PropName::JsNumberLiteral(literal) => literal.to_format_element(formatter), PropName::Ident(ident) => ident.to_format_element(formatter), PropName::Name(name) => name.to_format_element(formatter), PropName::JsUnknownBinding(_) => todo!(), diff --git a/crates/rome_formatter/src/ts/tokens/mod.rs b/crates/rome_formatter/src/ts/tokens/mod.rs deleted file mode 100644 index 6026f19c2c15..000000000000 --- a/crates/rome_formatter/src/ts/tokens/mod.rs +++ /dev/null @@ -1 +0,0 @@ -mod string_token; diff --git a/crates/rome_formatter/src/ts/tokens/string_token.rs b/crates/rome_formatter/src/ts/tokens/string_token.rs deleted file mode 100644 index f72a666cfa3d..000000000000 --- a/crates/rome_formatter/src/ts/tokens/string_token.rs +++ /dev/null @@ -1,16 +0,0 @@ -use crate::{token, FormatElement, FormatResult, Formatter, ToFormatElement}; -use rslint_parser::ast::String as JsString; - -impl ToFormatElement for JsString { - fn to_format_element(&self, _formatter: &Formatter) -> FormatResult { - let mut content = self.to_string(); - - // uses single quotes - if content.starts_with('\'') { - content.replace_range(0..1, "\""); - content.replace_range(content.len() - 1..content.len(), "\""); - } - - Ok(token(content.as_str())) - } -} diff --git a/crates/rslint_lexer/src/highlight.rs b/crates/rslint_lexer/src/highlight.rs index a652787f94dd..c21407e988d3 100644 --- a/crates/rslint_lexer/src/highlight.rs +++ b/crates/rslint_lexer/src/highlight.rs @@ -120,10 +120,12 @@ impl<'s> Iterator for Highlighter<'s> { } t if t.is_punct() => rgb![86, 182, 194], t if t.is_keyword() => rgb![198, 120, 221], - SyntaxKind::STRING | SyntaxKind::BACKTICK | SyntaxKind::TEMPLATE_CHUNK => { + SyntaxKind::JS_STRING_LITERAL_TOKEN + | SyntaxKind::BACKTICK + | SyntaxKind::TEMPLATE_CHUNK => { rgb![152, 195, 121] } - SyntaxKind::NUMBER => rgb![209, 154, 102], + SyntaxKind::JS_NUMBER_LITERAL_TOKEN => rgb![209, 154, 102], SyntaxKind::DOLLARCURLY => rgb![198, 120, 221], SyntaxKind::ERROR_TOKEN => rgb![244, 71, 71], SyntaxKind::COMMENT => rgb![127, 132, 142], @@ -137,7 +139,7 @@ impl<'s> Iterator for Highlighter<'s> { } } -/// Colors a piece of source code using ANSI. +/// Colors a piece of source code using ANSI. /// The string returned will be unaltered if stdout and stderr are not terminals. pub fn color(source: &str) -> String { Highlighter::new(source).color() diff --git a/crates/rslint_lexer/src/lib.rs b/crates/rslint_lexer/src/lib.rs index c89410ee230c..1db1d3a936dc 100644 --- a/crates/rslint_lexer/src/lib.rs +++ b/crates/rslint_lexer/src/lib.rs @@ -862,7 +862,7 @@ impl<'src> Lexer<'src> { Some(err), ) } else { - tok!(NUMBER, self.cur - start) + tok!(JS_NUMBER_LITERAL_TOKEN, self.cur - start) } } @@ -1009,7 +1009,7 @@ impl<'src> Lexer<'src> { } }, _ => { - return (Token::new(SyntaxKind::REGEX, self.cur - start), diagnostic) + return (Token::new(SyntaxKind::JS_REGEX_LITERAL_TOKEN, self.cur - start), diagnostic) } } } @@ -1020,7 +1020,7 @@ impl<'src> Lexer<'src> { let err = Diagnostic::error(self.file_id, "", "expected a character after a regex escape, but found none") .primary(self.cur..self.cur + 1, "expected a character following this"); - return (Token::new(SyntaxKind::REGEX, self.cur - start), Some(err)); + return (Token::new(SyntaxKind::JS_REGEX_LITERAL_TOKEN, self.cur - start), Some(err)); } }, None => { @@ -1028,7 +1028,7 @@ impl<'src> Lexer<'src> { .primary(self.cur..self.cur, "...but the file ends here") .secondary(start..start + 1, "a regex literal starts there..."); - return (Token::new(SyntaxKind::REGEX, self.cur - start), Some(err)); + return (Token::new(SyntaxKind::JS_REGEX_LITERAL_TOKEN, self.cur - start), Some(err)); }, _ => {}, } @@ -1333,7 +1333,7 @@ impl<'src> Lexer<'src> { Some(err), ) } else { - tok!(STRING, self.cur - start) + tok!(JS_STRING_LITERAL_TOKEN, self.cur - start) } } IDT => self.resolve_identifier((byte as char, start)), diff --git a/crates/rslint_lexer/src/tests.rs b/crates/rslint_lexer/src/tests.rs index bcc84d17c302..14fb39f9f42d 100644 --- a/crates/rslint_lexer/src/tests.rs +++ b/crates/rslint_lexer/src/tests.rs @@ -170,12 +170,12 @@ fn all_whitespace() { fn empty_string() { assert_lex! { r#""""#, - STRING:2 + JS_STRING_LITERAL_TOKEN:2 } assert_lex! { "''", - STRING:2 + JS_STRING_LITERAL_TOKEN:2 } } @@ -219,12 +219,12 @@ fn template_literals() { fn simple_string() { assert_lex! { r#"'abcdefghijklmnopqrstuvwxyz123456789\'10🦀'"#, - STRING:45 + JS_STRING_LITERAL_TOKEN:45 } assert_lex! { r#""abcdefghijklmnopqrstuvwxyz123456789\"10🦀""#, - STRING:45 + JS_STRING_LITERAL_TOKEN:45 } } @@ -245,12 +245,12 @@ fn string_unicode_escape_invalid() { fn string_unicode_escape_valid() { assert_lex! { r#""abcd\u2000a""#, - STRING:13 + JS_STRING_LITERAL_TOKEN:13 } assert_lex! { r#"'abcd\u2000a'"#, - STRING:13 + JS_STRING_LITERAL_TOKEN:13 } } @@ -258,12 +258,12 @@ fn string_unicode_escape_valid() { fn string_unicode_escape_valid_resolving_to_endquote() { assert_lex! { r#""abcd\u0022a""#, - STRING:13 + JS_STRING_LITERAL_TOKEN:13 } assert_lex! { r#"'abcd\u0027a'"#, - STRING:13 + JS_STRING_LITERAL_TOKEN:13 } } @@ -284,12 +284,12 @@ fn string_hex_escape_invalid() { fn string_hex_escape_valid() { assert_lex! { r#""abcd \x00 \xAB""#, - STRING:16 + JS_STRING_LITERAL_TOKEN:16 } assert_lex! { r#"'abcd \x00 \xAB'"#, - STRING:16 + JS_STRING_LITERAL_TOKEN:16 } } @@ -328,7 +328,7 @@ fn complex_string_1() { WHITESPACE:1, PLUSEQ:2, WHITESPACE:1, - STRING:14, + JS_STRING_LITERAL_TOKEN:14, SEMICOLON:1 } @@ -339,7 +339,7 @@ fn complex_string_1() { WHITESPACE:1, PLUSEQ:2, WHITESPACE:1, - STRING:14, + JS_STRING_LITERAL_TOKEN:14, SEMICOLON:1 } } @@ -677,23 +677,23 @@ fn labels_y() { fn number_basic() { assert_lex! { "1", - NUMBER:1 + JS_NUMBER_LITERAL_TOKEN:1 } assert_lex! { "123456 ", - NUMBER:6, + JS_NUMBER_LITERAL_TOKEN:6, WHITESPACE:1 } assert_lex! { "90", - NUMBER:2 + JS_NUMBER_LITERAL_TOKEN:2 } assert_lex! { ".13", - NUMBER:3 + JS_NUMBER_LITERAL_TOKEN:3 } } @@ -701,7 +701,7 @@ fn number_basic() { fn number_basic_err() { assert_lex! { "2_?", - NUMBER:2, // numeric separator error + JS_NUMBER_LITERAL_TOKEN:2, // numeric separator error QUESTION:1 } @@ -712,7 +712,7 @@ fn number_basic_err() { assert_lex! { r#"25\uFEFFb"#, - NUMBER:2, + JS_NUMBER_LITERAL_TOKEN:2, ERROR_TOKEN:6, IDENT:1 } @@ -727,19 +727,19 @@ fn number_basic_err() { fn number_complex() { assert_lex! { "3e-5 123e+56", - NUMBER:4, + JS_NUMBER_LITERAL_TOKEN:4, WHITESPACE:1, - NUMBER:7 + JS_NUMBER_LITERAL_TOKEN:7 } assert_lex! { "3.14159e+1", - NUMBER:10 + JS_NUMBER_LITERAL_TOKEN:10 } assert_lex! { ".0e34", - NUMBER:5 + JS_NUMBER_LITERAL_TOKEN:5 } } @@ -750,12 +750,12 @@ fn dot_number_disambiguation() { DOT:1, IDENT:1, PLUS:1, - NUMBER:1 + JS_NUMBER_LITERAL_TOKEN:1 } assert_lex! { ".0e+5", - NUMBER:5 + JS_NUMBER_LITERAL_TOKEN:5 } } @@ -763,13 +763,13 @@ fn dot_number_disambiguation() { fn binary_literals() { assert_lex! { "0b10101010, 0B10101010, 0b10101010n", - NUMBER:10, + JS_NUMBER_LITERAL_TOKEN:10, COMMA:1, WHITESPACE:1, - NUMBER:10, + JS_NUMBER_LITERAL_TOKEN:10, COMMA:1, WHITESPACE:1, - NUMBER:11 + JS_NUMBER_LITERAL_TOKEN:11 } } @@ -777,13 +777,13 @@ fn binary_literals() { fn octal_literals() { assert_lex! { "0o01742242, 0B10101010, 0b10101010n", - NUMBER:10, + JS_NUMBER_LITERAL_TOKEN:10, COMMA:1, WHITESPACE:1, - NUMBER:10, + JS_NUMBER_LITERAL_TOKEN:10, COMMA:1, WHITESPACE:1, - NUMBER:11 + JS_NUMBER_LITERAL_TOKEN:11 } } @@ -791,11 +791,11 @@ fn octal_literals() { fn bigint_literals() { assert_lex! { "0n 1743642n 1n", - NUMBER:2, + JS_NUMBER_LITERAL_TOKEN:2, WHITESPACE:1, - NUMBER:8, + JS_NUMBER_LITERAL_TOKEN:8, WHITESPACE:1, - NUMBER:2 + JS_NUMBER_LITERAL_TOKEN:2 } } @@ -821,15 +821,15 @@ fn shebang() { assert_lex! { "#0", ERROR_TOKEN:1, - NUMBER:1 + JS_NUMBER_LITERAL_TOKEN:1 } assert_lex! { "0#!/bin/deno", - NUMBER:1, + JS_NUMBER_LITERAL_TOKEN:1, HASH:1, BANG:1, - REGEX:9 + JS_REGEX_LITERAL_TOKEN:9 } } @@ -877,7 +877,7 @@ fn regex() { WHITESPACE:1, EQ:1, WHITESPACE:1, - REGEX:7 + JS_REGEX_LITERAL_TOKEN:7 } } @@ -891,11 +891,11 @@ fn division() { WHITESPACE:1, EQ:1, WHITESPACE:1, - NUMBER:1, + JS_NUMBER_LITERAL_TOKEN:1, WHITESPACE:1, SLASH:1, WHITESPACE:1, - NUMBER:1 + JS_NUMBER_LITERAL_TOKEN:1 } } @@ -1007,7 +1007,7 @@ fn issue_30() { fn fuzz_fail_7() { assert_lex! { "/\u{0}/ª\u{80}", - REGEX:5, + JS_REGEX_LITERAL_TOKEN:5, ERROR_TOKEN:2 } } diff --git a/crates/rslint_parser/src/ast.rs b/crates/rslint_parser/src/ast.rs index 62dbe4ba041f..13eabb805765 100644 --- a/crates/rslint_parser/src/ast.rs +++ b/crates/rslint_parser/src/ast.rs @@ -439,7 +439,7 @@ mod support { #[cfg(test)] mod tests { - use crate::ast::{AstSeparatedElement, AstSeparatedList, Literal}; + use crate::ast::{AstSeparatedElement, AstSeparatedList, JsNumberLiteral}; use crate::{JsLanguage, SyntaxKind}; use rome_rowan::TreeBuilder; @@ -447,15 +447,18 @@ mod tests { /// The elements are pairs of: (value, separator). fn build_list<'a>( elements: impl IntoIterator, Option<&'a str>)>, - ) -> AstSeparatedList { + ) -> AstSeparatedList { let mut builder: TreeBuilder = TreeBuilder::new(); builder.start_node(SyntaxKind::LIST); for (node, separator) in elements.into_iter() { if let Some(node) = node { - builder.start_node(SyntaxKind::LITERAL); - builder.token(SyntaxKind::NUMBER, node.to_string().as_str()); + builder.start_node(SyntaxKind::JS_NUMBER_LITERAL); + builder.token( + SyntaxKind::JS_NUMBER_LITERAL_TOKEN, + node.to_string().as_str(), + ); builder.finish_node(); } @@ -472,7 +475,7 @@ mod tests { } fn assert_elements<'a>( - actual: impl Iterator>, + actual: impl Iterator>, expected: impl IntoIterator)>, ) { let actual = actual.map(|element| { @@ -493,7 +496,7 @@ mod tests { } fn assert_nodes( - actual: impl Iterator, + actual: impl Iterator, expected: impl IntoIterator, ) { assert_eq!( diff --git a/crates/rslint_parser/src/ast/expr_ext.rs b/crates/rslint_parser/src/ast/expr_ext.rs index 38a97b62c692..8321d46e64d3 100644 --- a/crates/rslint_parser/src/ast/expr_ext.rs +++ b/crates/rslint_parser/src/ast/expr_ext.rs @@ -1,6 +1,7 @@ //! Extensions for things which are not easily generated in ast expr nodes -use crate::{ast::*, numbers::*, util::*, SyntaxText, TextRange, TextSize, TokenSet, T}; +use crate::{ast::*, numbers::*, util::*, TextRange, TokenSet, T}; +use rome_rowan::{SyntaxText, TextSize}; use SyntaxKind::*; impl BracketExpr { @@ -333,74 +334,21 @@ impl ObjectExpr { } } -#[derive(Debug, Clone, PartialEq, PartialOrd)] -pub enum LiteralKind { - Number(f64), - BigInt(BigInt), - String, - Null, - Bool(bool), - Regex, -} - -impl Literal { - pub fn token(&self) -> SyntaxToken { - self.syntax() - .children_with_tokens() - .find(|e| !e.kind().is_trivia()) - .and_then(|e| e.into_token()) - .unwrap() - } - - pub fn kind(&self) -> LiteralKind { - match self.token().kind() { - T![null] => LiteralKind::Null, - NUMBER => match parse_js_num(self.to_string()).unwrap() { - JsNum::BigInt(bigint) => LiteralKind::BigInt(bigint), - JsNum::Float(float) => LiteralKind::Number(float), - }, - STRING => LiteralKind::String, - TRUE_KW => LiteralKind::Bool(true), - FALSE_KW => LiteralKind::Bool(false), - REGEX => LiteralKind::Regex, - _ => unreachable!(), - } - } - +impl JsNumberLiteral { pub fn as_number(&self) -> Option { - if let LiteralKind::Number(num) = self.kind() { - Some(num) - } else { - None - } - } - - pub fn is_number(&self) -> bool { - matches!(self.kind(), LiteralKind::Number(_)) - } - - pub fn is_string(&self) -> bool { - self.kind() == LiteralKind::String - } - - pub fn is_null(&self) -> bool { - self.kind() == LiteralKind::Null - } - - pub fn is_bool(&self) -> bool { - matches!(self.kind(), LiteralKind::Bool(_)) + parse_js_number(self.value_token().unwrap().text()) } +} - pub fn is_regex(&self) -> bool { - self.kind() == LiteralKind::Regex +impl JsBigIntLiteral { + pub fn as_number(&self) -> Option { + parse_js_big_int(self.value_token().ok()?.text()) } +} +impl JsStringLiteral { /// Get the inner text of a string not including the quotes - pub fn inner_string_text(&self) -> Option { - if !self.is_string() { - return None; - } - + pub fn inner_string_text(&self) -> SyntaxText { let start = self.syntax().text_range().start() + TextSize::from(1); let end_char = self .syntax() @@ -415,11 +363,9 @@ impl Literal { let offset = self.syntax().text_range().start(); - Some( - self.syntax() - .text() - .slice(TextRange::new(start - offset, end - offset)), - ) + self.syntax() + .text() + .slice(TextRange::new(start - offset, end - offset)) } } @@ -516,7 +462,8 @@ impl ObjectProp { fn prop_name_syntax(name: PropName) -> Option { Some(match name { PropName::Ident(idt) => idt.syntax().clone(), - PropName::Literal(lit) => lit.syntax().clone(), + PropName::JsStringLiteral(lit) => lit.syntax().clone(), + PropName::JsNumberLiteral(lit) => lit.syntax().clone(), PropName::Name(name) => name.syntax().clone(), PropName::ComputedPropertyName(_) => return None, PropName::JsUnknownBinding(_) => todo!(), diff --git a/crates/rslint_parser/src/ast/generated/nodes.rs b/crates/rslint_parser/src/ast/generated/nodes.rs index 857d7430b9b5..07231ba7174c 100644 --- a/crates/rslint_parser/src/ast/generated/nodes.rs +++ b/crates/rslint_parser/src/ast/generated/nodes.rs @@ -386,6 +386,7 @@ impl ImportDecl { pub fn from_token(&self) -> SyntaxResult { support::required_token(&self.syntax, T![from]) } + pub fn source(&self) -> SyntaxResult { support::required_node(&self.syntax) } pub fn asserted_object(&self) -> SyntaxResult { support::required_node(&self.syntax) } @@ -453,6 +454,7 @@ impl ExportWildcard { pub fn from_token(&self) -> SyntaxResult { support::required_token(&self.syntax, T![from]) } + pub fn source(&self) -> SyntaxResult { support::required_node(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct ExportDecl { @@ -643,30 +645,6 @@ impl ArrowExpr { pub fn return_type(&self) -> Option { support::node(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Literal { - pub(crate) syntax: SyntaxNode, -} -impl Literal { - pub fn true_token(&self) -> SyntaxResult { - support::required_token(&self.syntax, T![true]) - } - pub fn false_token(&self) -> SyntaxResult { - support::required_token(&self.syntax, T![false]) - } - pub fn number_token(&self) -> SyntaxResult { - support::required_token(&self.syntax, T![number]) - } - pub fn regex_token(&self) -> SyntaxResult { - support::required_token(&self.syntax, T![regex]) - } - pub fn float_token(&self) -> SyntaxResult { - support::required_token(&self.syntax, T![float]) - } - pub fn big_int_token(&self) -> SyntaxResult { - support::required_token(&self.syntax, T![big_int]) - } -} -#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Template { pub(crate) syntax: SyntaxNode, } @@ -1300,21 +1278,57 @@ impl SpreadElement { pub fn element(&self) -> SyntaxResult { support::required_node(&self.syntax) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Null { +pub struct JsStringLiteral { pub(crate) syntax: SyntaxNode, } -impl Null { - pub fn null_token(&self) -> SyntaxResult { +impl JsStringLiteral { + pub fn value_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, T![js_string_literal_token]) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct JsNumberLiteral { + pub(crate) syntax: SyntaxNode, +} +impl JsNumberLiteral { + pub fn value_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, T![js_number_literal_token]) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct JsBigIntLiteral { + pub(crate) syntax: SyntaxNode, +} +impl JsBigIntLiteral { + pub fn value_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, T![js_big_int_literal_token]) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct JsBooleanLiteral { + pub(crate) syntax: SyntaxNode, +} +impl JsBooleanLiteral { + pub fn value_token(&self) -> SyntaxResult { + support::find_required_token(&self.syntax, &[T![true], T![false]]) + } +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct JsNullLiteral { + pub(crate) syntax: SyntaxNode, +} +impl JsNullLiteral { + pub fn value_token(&self) -> SyntaxResult { support::required_token(&self.syntax, T![null]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] -pub struct Undefined { +pub struct JsRegexLiteral { pub(crate) syntax: SyntaxNode, } -impl Undefined { - pub fn undefined_token(&self) -> SyntaxResult { - support::required_token(&self.syntax, T![undefined]) +impl JsRegexLiteral { + pub fn value_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, T![js_regex_literal_token]) } } #[derive(Debug, Clone, PartialEq, Eq, Hash)] @@ -1652,7 +1666,11 @@ impl NamedImports { pub struct ImportStringSpecifier { pub(crate) syntax: SyntaxNode, } -impl ImportStringSpecifier {} +impl ImportStringSpecifier { + pub fn js_string_literal_token_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, T![js_string_literal_token]) + } +} #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub struct Specifier { pub(crate) syntax: SyntaxNode, @@ -1671,6 +1689,9 @@ impl TsExternalModuleRef { pub fn l_paren_token(&self) -> SyntaxResult { support::required_token(&self.syntax, T!['(']) } + pub fn js_string_literal_token_token(&self) -> SyntaxResult { + support::required_token(&self.syntax, T![js_string_literal_token]) + } pub fn r_paren_token(&self) -> SyntaxResult { support::required_token(&self.syntax, T![')']) } @@ -2225,7 +2246,7 @@ pub enum Decl { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum JsAnyExpression { ArrowExpr(ArrowExpr), - Literal(Literal), + JsAnyLiteral(JsAnyLiteral), Template(Template), NameRef(NameRef), ThisExpr(ThisExpr), @@ -2278,6 +2299,15 @@ pub enum Pattern { JsUnknownPattern(JsUnknownPattern), } #[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub enum JsAnyLiteral { + JsStringLiteral(JsStringLiteral), + JsNumberLiteral(JsNumberLiteral), + JsBigIntLiteral(JsBigIntLiteral), + JsBooleanLiteral(JsBooleanLiteral), + JsNullLiteral(JsNullLiteral), + JsRegexLiteral(JsRegexLiteral), +} +#[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum TsType { TsAny(TsAny), TsUnknown(TsUnknown), @@ -2347,7 +2377,8 @@ pub enum ClassElement { #[derive(Debug, Clone, PartialEq, Eq, Hash)] pub enum PropName { ComputedPropertyName(ComputedPropertyName), - Literal(Literal), + JsStringLiteral(JsStringLiteral), + JsNumberLiteral(JsNumberLiteral), Ident(Ident), Name(Name), JsUnknownBinding(JsUnknownBinding), @@ -2944,17 +2975,6 @@ impl AstNode for ArrowExpr { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } -impl AstNode for Literal { - fn can_cast(kind: SyntaxKind) -> bool { kind == LITERAL } - fn cast(syntax: SyntaxNode) -> Option { - if Self::can_cast(syntax.kind()) { - Some(Self { syntax }) - } else { - None - } - } - fn syntax(&self) -> &SyntaxNode { &self.syntax } -} impl AstNode for Template { fn can_cast(kind: SyntaxKind) -> bool { kind == TEMPLATE } fn cast(syntax: SyntaxNode) -> Option { @@ -3472,8 +3492,41 @@ impl AstNode for SpreadElement { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } -impl AstNode for Null { - fn can_cast(kind: SyntaxKind) -> bool { kind == NULL } +impl AstNode for JsStringLiteral { + fn can_cast(kind: SyntaxKind) -> bool { kind == JS_STRING_LITERAL } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for JsNumberLiteral { + fn can_cast(kind: SyntaxKind) -> bool { kind == JS_NUMBER_LITERAL } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for JsBigIntLiteral { + fn can_cast(kind: SyntaxKind) -> bool { kind == JS_BIG_INT_LITERAL } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for JsBooleanLiteral { + fn can_cast(kind: SyntaxKind) -> bool { kind == JS_BOOLEAN_LITERAL } fn cast(syntax: SyntaxNode) -> Option { if Self::can_cast(syntax.kind()) { Some(Self { syntax }) @@ -3483,8 +3536,19 @@ impl AstNode for Null { } fn syntax(&self) -> &SyntaxNode { &self.syntax } } -impl AstNode for Undefined { - fn can_cast(kind: SyntaxKind) -> bool { kind == UNDEFINED } +impl AstNode for JsNullLiteral { + fn can_cast(kind: SyntaxKind) -> bool { kind == JS_NULL_LITERAL } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { &self.syntax } +} +impl AstNode for JsRegexLiteral { + fn can_cast(kind: SyntaxKind) -> bool { kind == JS_REGEX_LITERAL } fn cast(syntax: SyntaxNode) -> Option { if Self::can_cast(syntax.kind()) { Some(Self { syntax }) @@ -4630,9 +4694,6 @@ impl AstNode for Decl { impl From for JsAnyExpression { fn from(node: ArrowExpr) -> JsAnyExpression { JsAnyExpression::ArrowExpr(node) } } -impl From for JsAnyExpression { - fn from(node: Literal) -> JsAnyExpression { JsAnyExpression::Literal(node) } -} impl From