From a34cdcce351eac4962ddddd72f29542cca78cb19 Mon Sep 17 00:00:00 2001 From: Emanuele Stoppa Date: Tue, 20 Jun 2023 20:53:37 +0100 Subject: [PATCH] feat(rome_service, rome_js_parser): add parse options (#4587) * feat(rome_service, rome_js_parser): add parse options * chore: update all the API calls! Damn what a chore... * fix: doc tests --- Cargo.lock | 1 + crates/rome_formatter/src/comments/builder.rs | 6 +-- crates/rome_js_analyze/src/lib.rs | 16 +++++-- crates/rome_js_analyze/src/react/hooks.rs | 7 ++- .../nursery/no_constant_condition.rs | 3 +- crates/rome_js_analyze/src/utils/tests.rs | 25 ++++++++-- crates/rome_js_analyze/tests/spec_tests.rs | 43 ++++++++++++++--- crates/rome_js_formatter/src/comments.rs | 4 +- crates/rome_js_formatter/src/lib.rs | 14 +++--- crates/rome_js_formatter/src/parentheses.rs | 5 +- .../rome_js_formatter/src/syntax_rewriter.rs | 8 ++-- .../src/utils/binary_like_expression.rs | 6 +-- crates/rome_js_formatter/src/utils/jsx.rs | 14 ++++-- .../rome_js_formatter/src/utils/test_call.rs | 6 +-- crates/rome_js_formatter/tests/language.rs | 4 +- crates/rome_js_formatter/tests/quick_test.rs | 4 +- crates/rome_js_parser/Cargo.toml | 2 + crates/rome_js_parser/src/lib.rs | 2 + crates/rome_js_parser/src/options.rs | 28 +++++++++++ crates/rome_js_parser/src/parse.rs | 46 ++++++++++--------- crates/rome_js_parser/src/parser.rs | 24 ++++++++-- crates/rome_js_parser/src/tests.rs | 34 +++++++------- crates/rome_js_semantic/src/events.rs | 4 +- .../src/semantic_model/closure.rs | 5 +- .../src/semantic_model/is_constant.rs | 3 +- .../src/semantic_model/model.rs | 9 ++-- .../src/semantic_model/tests.rs | 16 +++++-- .../rome_js_semantic/src/tests/assertions.rs | 3 +- .../src/file_handlers/javascript.rs | 16 ++++++- crates/rome_service/src/file_handlers/json.rs | 9 +++- crates/rome_service/src/file_handlers/mod.rs | 2 +- crates/rome_service/src/settings.rs | 15 ++++++ crates/rome_service/src/workspace/server.rs | 2 + xtask/bench/src/language.rs | 15 ++++-- xtask/coverage/src/js/test262.rs | 11 +++-- xtask/coverage/src/jsx/jsx_babel.rs | 11 +++-- xtask/coverage/src/runner.rs | 23 ++++++++-- xtask/coverage/src/symbols/msts.rs | 11 ++++- xtask/coverage/src/ts/ts_babel.rs | 12 +++-- xtask/coverage/src/ts/ts_microsoft.rs | 3 +- xtask/lintdoc/src/main.rs | 3 +- 41 files changed, 349 insertions(+), 126 deletions(-) create mode 100644 crates/rome_js_parser/src/options.rs diff --git a/Cargo.lock b/Cargo.lock index 0699db9e652..35aa7910f7d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1962,6 +1962,7 @@ dependencies = [ "rome_js_unicode_table", "rome_parser", "rome_rowan", + "schemars", "smallvec", "tests_macros", "tracing", diff --git a/crates/rome_formatter/src/comments/builder.rs b/crates/rome_formatter/src/comments/builder.rs index df4ed277865..715405545af 100644 --- a/crates/rome_formatter/src/comments/builder.rs +++ b/crates/rome_formatter/src/comments/builder.rs @@ -639,7 +639,7 @@ mod tests { DecoratedComment, SourceComment, }; use crate::{TextSize, TransformSourceMap, TransformSourceMapBuilder}; - use rome_js_parser::parse_module; + use rome_js_parser::{parse_module, JsParserOptions}; use rome_js_syntax::{ JsIdentifierExpression, JsLanguage, JsParameters, JsParenthesizedExpression, JsPropertyObjectMember, JsReferenceIdentifier, JsShorthandPropertyObjectMember, @@ -850,7 +850,7 @@ b;"#; let source_map = source_map_builder.finish(); - let root = parse_module(source).syntax(); + let root = parse_module(source, JsParserOptions::default()).syntax(); // A lot of code that simply removes the parenthesized expression and moves the parens // trivia to the identifiers leading / trailing trivia. @@ -1011,7 +1011,7 @@ b;"#; Vec>, CommentsMap>, ) { - let tree = parse_module(source); + let tree = parse_module(source, JsParserOptions::default()); let style = TestCommentStyle::default(); let builder = CommentsBuilderVisitor::new(&style, source_map); diff --git a/crates/rome_js_analyze/src/lib.rs b/crates/rome_js_analyze/src/lib.rs index fb57ac0ac96..1ed4a702b61 100644 --- a/crates/rome_js_analyze/src/lib.rs +++ b/crates/rome_js_analyze/src/lib.rs @@ -160,7 +160,7 @@ mod tests { use rome_diagnostics::category; use rome_diagnostics::termcolor::NoColor; use rome_diagnostics::{Diagnostic, DiagnosticExt, PrintDiagnostic, Severity}; - use rome_js_parser::parse; + use rome_js_parser::{parse, JsParserOptions}; use rome_js_syntax::{JsFileSource, TextRange, TextSize}; use std::slice; @@ -186,7 +186,7 @@ mod tests { } }"#; - let parsed = parse(SOURCE, JsFileSource::tsx()); + let parsed = parse(SOURCE, JsFileSource::tsx(), JsParserOptions::default()); let mut error_ranges: Vec = Vec::new(); let mut options = AnalyzerOptions::default(); @@ -283,7 +283,11 @@ mod tests { } "; - let parsed = parse(SOURCE, JsFileSource::js_module()); + let parsed = parse( + SOURCE, + JsFileSource::js_module(), + JsParserOptions::default(), + ); let mut lint_ranges: Vec = Vec::new(); let mut parse_ranges: Vec = Vec::new(); @@ -365,7 +369,11 @@ mod tests { a == b; "; - let parsed = parse(SOURCE, JsFileSource::js_module()); + let parsed = parse( + SOURCE, + JsFileSource::js_module(), + JsParserOptions::default(), + ); let filter = AnalysisFilter { categories: RuleCategories::SYNTAX, diff --git a/crates/rome_js_analyze/src/react/hooks.rs b/crates/rome_js_analyze/src/react/hooks.rs index c34cfcead07..73d7e1ec053 100644 --- a/crates/rome_js_analyze/src/react/hooks.rs +++ b/crates/rome_js_analyze/src/react/hooks.rs @@ -208,11 +208,16 @@ pub fn is_binding_react_stable( #[cfg(test)] mod test { use super::*; + use rome_js_parser::JsParserOptions; use rome_js_syntax::JsFileSource; #[test] pub fn ok_react_stable_captures() { - let r = rome_js_parser::parse("const ref = useRef();", JsFileSource::js_module()); + let r = rome_js_parser::parse( + "const ref = useRef();", + JsFileSource::js_module(), + JsParserOptions::default(), + ); let node = r .syntax() .descendants() diff --git a/crates/rome_js_analyze/src/semantic_analyzers/nursery/no_constant_condition.rs b/crates/rome_js_analyze/src/semantic_analyzers/nursery/no_constant_condition.rs index 094c290ce6f..1b55a02e8ab 100644 --- a/crates/rome_js_analyze/src/semantic_analyzers/nursery/no_constant_condition.rs +++ b/crates/rome_js_analyze/src/semantic_analyzers/nursery/no_constant_condition.rs @@ -461,13 +461,14 @@ fn get_boolean_value(node: AnyJsLiteralExpression) -> bool { #[cfg(test)] mod tests { + use rome_js_parser::JsParserOptions; use rome_js_syntax::{AnyJsLiteralExpression, JsFileSource}; use rome_rowan::SyntaxNodeCast; use super::get_boolean_value; fn assert_boolean_value(code: &str, value: bool) { - let source = rome_js_parser::parse(code, JsFileSource::tsx()); + let source = rome_js_parser::parse(code, JsFileSource::tsx(), JsParserOptions::default()); if source.has_errors() { panic!("syntax error") diff --git a/crates/rome_js_analyze/src/utils/tests.rs b/crates/rome_js_analyze/src/utils/tests.rs index a4a87e1f348..98aeb00c692 100644 --- a/crates/rome_js_analyze/src/utils/tests.rs +++ b/crates/rome_js_analyze/src/utils/tests.rs @@ -1,5 +1,6 @@ use super::rename::*; use crate::utils::batch::JsBatchMutation; +use rome_js_parser::JsParserOptions; use rome_js_semantic::{semantic_model, SemanticModelOptions}; use rome_js_syntax::JsSyntaxNode; use rome_js_syntax::{ @@ -12,7 +13,11 @@ use std::{any::type_name, fmt::Debug}; /// Search and renames alls bindings where the name contains "a" replacing it to "b". /// Asserts the renaming worked. pub fn assert_rename_binding_a_to_b_ok(before: &str, expected: &str) { - let r = rome_js_parser::parse(before, JsFileSource::js_module()); + let r = rome_js_parser::parse( + before, + JsFileSource::js_module(), + JsParserOptions::default(), + ); let model = semantic_model(&r.tree(), SemanticModelOptions::default()); let bindings: Vec = r @@ -44,7 +49,11 @@ pub fn assert_rename_binding_a_to_b_ok(before: &str, expected: &str) { /// Search and renames one binding named "a" to "b". /// Asserts the renaming fails. pub fn assert_rename_binding_a_to_b_nok(before: &str) { - let r = rome_js_parser::parse(before, JsFileSource::js_module()); + let r = rome_js_parser::parse( + before, + JsFileSource::js_module(), + JsParserOptions::default(), + ); let model = semantic_model(&r.tree(), SemanticModelOptions::default()); let binding_a = r @@ -64,7 +73,11 @@ pub fn assert_remove_identifier_a_ok + Debug before: &str, expected: &str, ) { - let r = rome_js_parser::parse(before, JsFileSource::js_module()); + let r = rome_js_parser::parse( + before, + JsFileSource::js_module(), + JsParserOptions::default(), + ); let identifiers_a: Vec = r .syntax() @@ -147,7 +160,11 @@ macro_rules! assert_remove_ok { #[test] pub fn ok_find_attributes_by_name() { - let r = rome_js_parser::parse(r#""#, JsFileSource::jsx()); + let r = rome_js_parser::parse( + r#""#, + JsFileSource::jsx(), + JsParserOptions::default(), + ); let list = r .syntax() .descendants() diff --git a/crates/rome_js_analyze/tests/spec_tests.rs b/crates/rome_js_analyze/tests/spec_tests.rs index 1c48f01650a..72bcedafb00 100644 --- a/crates/rome_js_analyze/tests/spec_tests.rs +++ b/crates/rome_js_analyze/tests/spec_tests.rs @@ -13,6 +13,7 @@ use rome_diagnostics::{DiagnosticExt, Error, PrintDiagnostic, Severity}; use rome_js_parser::{ parse, test_utils::{assert_errors_are_absent, has_bogus_nodes_or_empty_slots}, + JsParserOptions, }; use rome_js_syntax::{JsFileSource, JsLanguage}; use rome_service::configuration::to_analyzer_configuration; @@ -74,6 +75,7 @@ fn run_test(input: &'static str, _: &str, _: &str, _: &str) { file_name, input_file, CheckActionType::Lint, + JsParserOptions::default(), ); } @@ -90,6 +92,7 @@ fn run_test(input: &'static str, _: &str, _: &str, _: &str) { file_name, input_file, CheckActionType::Lint, + JsParserOptions::default(), ) }; @@ -116,6 +119,7 @@ impl CheckActionType { } } +#[allow(clippy::too_many_arguments)] pub(crate) fn write_analysis_to_snapshot( snapshot: &mut String, input_code: &str, @@ -124,8 +128,9 @@ pub(crate) fn write_analysis_to_snapshot( file_name: &str, input_file: &Path, check_action_type: CheckActionType, + parser_options: JsParserOptions, ) -> usize { - let parsed = parse(input_code, source_type); + let parsed = parse(input_code, source_type, parser_options.clone()); let root = parsed.tree(); let mut diagnostics = Vec::new(); @@ -174,11 +179,23 @@ pub(crate) fn write_analysis_to_snapshot( for action in event.actions() { if check_action_type.is_suppression() { if action.is_suppression() { - check_code_action(input_file, input_code, source_type, &action); + check_code_action( + input_file, + input_code, + source_type, + &action, + parser_options.clone(), + ); diag = diag.add_code_suggestion(CodeSuggestionAdvice::from(action)); } } else if !action.is_suppression() { - check_code_action(input_file, input_code, source_type, &action); + check_code_action( + input_file, + input_code, + source_type, + &action, + parser_options.clone(), + ); diag = diag.add_code_suggestion(CodeSuggestionAdvice::from(action)); } } @@ -191,11 +208,23 @@ pub(crate) fn write_analysis_to_snapshot( for action in event.actions() { if check_action_type.is_suppression() { if action.category.matches("quickfix.suppressRule") { - check_code_action(input_file, input_code, source_type, &action); + check_code_action( + input_file, + input_code, + source_type, + &action, + parser_options.clone(), + ); code_fixes.push(code_fix_to_string(input_code, action)); } } else if !action.category.matches("quickfix.suppressRule") { - check_code_action(input_file, input_code, source_type, &action); + check_code_action( + input_file, + input_code, + source_type, + &action, + parser_options.clone(), + ); code_fixes.push(code_fix_to_string(input_code, action)); } } @@ -274,6 +303,7 @@ fn check_code_action( source: &str, source_type: JsFileSource, action: &AnalyzerAction, + options: JsParserOptions, ) { let (_, text_edit) = action.mutation.as_text_edits().unwrap_or_default(); @@ -298,7 +328,7 @@ fn check_code_action( } // Re-parse the modified code and panic if the resulting tree has syntax errors - let re_parse = parse(&output, source_type); + let re_parse = parse(&output, source_type, options); assert_errors_are_absent(&re_parse, path); } @@ -361,6 +391,7 @@ pub(crate) fn run_suppression_test(input: &'static str, _: &str, _: &str, _: &st file_name, input_file, CheckActionType::Suppression, + JsParserOptions::default(), ); insta::with_settings!({ diff --git a/crates/rome_js_formatter/src/comments.rs b/crates/rome_js_formatter/src/comments.rs index 0664f9aeafd..e258b304f34 100644 --- a/crates/rome_js_formatter/src/comments.rs +++ b/crates/rome_js_formatter/src/comments.rs @@ -72,13 +72,13 @@ impl FormatRule> for FormatJsLeadingComment { /// # Examples /// /// ``` -/// # use rome_js_parser::parse_module; +/// # use rome_js_parser::{JsParserOptions, parse_module}; /// # use rome_js_syntax::JsLanguage; /// # use rome_rowan::{Direction, SyntaxTriviaPieceComments}; /// use rome_js_formatter::comments::is_doc_comment; /// /// # fn parse_comment(source: &str) -> SyntaxTriviaPieceComments { -/// # let root = parse_module(source).tree(); +/// # let root = parse_module(source, JsParserOptions::default()).tree(); /// # root /// # .eof_token() /// # .expect("Root to have an EOF token") diff --git a/crates/rome_js_formatter/src/lib.rs b/crates/rome_js_formatter/src/lib.rs index e0b7cfd7ae9..f8f43df32e1 100644 --- a/crates/rome_js_formatter/src/lib.rs +++ b/crates/rome_js_formatter/src/lib.rs @@ -544,7 +544,7 @@ mod tests { use crate::context::JsFormatOptions; use rome_formatter::IndentStyle; - use rome_js_parser::{parse, parse_script}; + use rome_js_parser::{parse, parse_script, JsParserOptions}; use rome_js_syntax::JsFileSource; use rome_rowan::{TextRange, TextSize}; @@ -578,7 +578,7 @@ while( let range_start = TextSize::try_from(input.find("let").unwrap() - 2).unwrap(); let range_end = TextSize::try_from(input.find("const").unwrap()).unwrap(); - let tree = parse_script(input); + let tree = parse_script(input, JsParserOptions::default()); let result = format_range( JsFormatOptions::new(JsFileSource::js_script()) .with_indent_style(IndentStyle::Space(4)), @@ -611,7 +611,7 @@ function() { let range_start = TextSize::try_from(input.find("const").unwrap()).unwrap(); let range_end = TextSize::try_from(input.find('}').unwrap()).unwrap(); - let tree = parse_script(input); + let tree = parse_script(input, JsParserOptions::default()); let result = format_range( JsFormatOptions::new(JsFileSource::js_script()) .with_indent_style(IndentStyle::Space(4)), @@ -639,7 +639,7 @@ function() { let range_start = TextSize::from(5); let range_end = TextSize::from(5); - let tree = parse_script(input); + let tree = parse_script(input, JsParserOptions::default()); let result = format_range( JsFormatOptions::new(JsFileSource::js_script()) .with_indent_style(IndentStyle::Space(4)), @@ -668,7 +668,7 @@ function() { }"# ); - let tree = parse_script(input); + let tree = parse_script(input, JsParserOptions::default()); let result = format_range( JsFormatOptions::new(JsFileSource::js_script()) .with_indent_style(IndentStyle::Space(4)), @@ -700,7 +700,7 @@ function() { debug_assert_eq!(&input[range], r#" quux (); //"#); - let tree = parse_script(input); + let tree = parse_script(input, JsParserOptions::default()); let result = format_range( JsFormatOptions::new(JsFileSource::js_script()) .with_indent_style(IndentStyle::Space(4)), @@ -721,7 +721,7 @@ function() { let src = "statement();"; let syntax = JsFileSource::js_module(); - let tree = parse(src, syntax); + let tree = parse(src, syntax, JsParserOptions::default()); let result = format_range( JsFormatOptions::new(syntax), diff --git a/crates/rome_js_formatter/src/parentheses.rs b/crates/rome_js_formatter/src/parentheses.rs index 9296635de53..7b717328b43 100644 --- a/crates/rome_js_formatter/src/parentheses.rs +++ b/crates/rome_js_formatter/src/parentheses.rs @@ -1040,6 +1040,7 @@ pub(crate) fn debug_assert_is_parent(node: &JsSyntaxNode, parent: &JsSyntaxNode) pub(crate) mod tests { use super::NeedsParentheses; use crate::transform; + use rome_js_parser::JsParserOptions; use rome_js_syntax::{JsFileSource, JsLanguage}; use rome_rowan::AstNode; @@ -1050,7 +1051,7 @@ pub(crate) mod tests { index: Option, source_type: JsFileSource, ) { - let parse = rome_js_parser::parse(input, source_type); + let parse = rome_js_parser::parse(input, source_type, JsParserOptions::default()); let diagnostics = parse.diagnostics(); assert!( @@ -1091,7 +1092,7 @@ pub(crate) mod tests { index: Option, source_type: JsFileSource, ) { - let parse = rome_js_parser::parse(input, source_type); + let parse = rome_js_parser::parse(input, source_type, JsParserOptions::default()); let diagnostics = parse.diagnostics(); assert!( diff --git a/crates/rome_js_formatter/src/syntax_rewriter.rs b/crates/rome_js_formatter/src/syntax_rewriter.rs index d070c359ca9..16910dfe68f 100644 --- a/crates/rome_js_formatter/src/syntax_rewriter.rs +++ b/crates/rome_js_formatter/src/syntax_rewriter.rs @@ -446,7 +446,7 @@ mod tests { use super::JsFormatSyntaxRewriter; use crate::{format_node, JsFormatOptions, TextRange}; use rome_formatter::{SourceMarker, TransformSourceMap}; - use rome_js_parser::{parse, parse_module}; + use rome_js_parser::{parse, parse_module, JsParserOptions}; use rome_js_syntax::{ JsArrayExpression, JsBinaryExpression, JsExpressionStatement, JsFileSource, JsIdentifierExpression, JsLogicalExpression, JsSequenceExpression, @@ -456,7 +456,7 @@ mod tests { #[test] fn rebalances_logical_expressions() { - let root = parse_module("a && (b && c)").syntax(); + let root = parse_module("a && (b && c)", JsParserOptions::default()).syntax(); let transformed = JsFormatSyntaxRewriter::default().transform(root.clone()); @@ -485,7 +485,7 @@ mod tests { #[test] fn only_rebalances_logical_expressions_with_same_operator() { - let root = parse_module("a && (b || c)").syntax(); + let root = parse_module("a && (b || c)", JsParserOptions::default()).syntax(); let transformed = JsFormatSyntaxRewriter::default().transform(root); // Removes parentheses @@ -809,7 +809,7 @@ mod tests { } fn source_map_test(input: &str) -> (JsSyntaxNode, TransformSourceMap) { - let tree = parse(input, JsFileSource::jsx()).syntax(); + let tree = parse(input, JsFileSource::jsx(), JsParserOptions::default()).syntax(); let mut rewriter = JsFormatSyntaxRewriter::default(); let transformed = rewriter.transform(tree); diff --git a/crates/rome_js_formatter/src/utils/binary_like_expression.rs b/crates/rome_js_formatter/src/utils/binary_like_expression.rs index 5c61a46852e..af40e0a4a64 100644 --- a/crates/rome_js_formatter/src/utils/binary_like_expression.rs +++ b/crates/rome_js_formatter/src/utils/binary_like_expression.rs @@ -896,13 +896,13 @@ impl FusedIterator for BinaryLikePreorder {} mod tests { use crate::utils::binary_like_expression::{BinaryLikePreorder, VisitEvent}; use crate::utils::AnyJsBinaryLikeExpression; - use rome_js_parser::parse_module; + use rome_js_parser::{parse_module, JsParserOptions}; use rome_js_syntax::JsLogicalExpression; use rome_rowan::AstNode; #[test] fn in_order_visits_every_binary_like_expression() { - let parse = parse_module("a && b && c || d"); + let parse = parse_module("a && b && c || d", JsParserOptions::default()); let root = parse .syntax() .descendants() @@ -950,7 +950,7 @@ mod tests { #[test] fn in_order_skip_subtree() { - let parse = parse_module("a && b && c || d"); + let parse = parse_module("a && b && c || d", JsParserOptions::default()); let root = parse .syntax() .descendants() diff --git a/crates/rome_js_formatter/src/utils/jsx.rs b/crates/rome_js_formatter/src/utils/jsx.rs index c7deafa3397..25277df62e3 100644 --- a/crates/rome_js_formatter/src/utils/jsx.rs +++ b/crates/rome_js_formatter/src/utils/jsx.rs @@ -564,7 +564,7 @@ mod tests { jsx_split_children, JsxChild, JsxChildrenIterator, JsxSplitChunksIterator, JsxTextChunk, }; use rome_formatter::comments::Comments; - use rome_js_parser::parse; + use rome_js_parser::{parse, JsParserOptions}; use rome_js_syntax::{JsFileSource, JsxChildList, JsxText}; use rome_rowan::{AstNode, TextSize}; @@ -589,7 +589,11 @@ mod tests { } fn assert_jsx_text_chunks(text: &str, expected_chunks: Vec<(TextSize, JsxTextChunk)>) { - let parse = parse(&std::format!("<>{text}"), JsFileSource::jsx()); + let parse = parse( + &std::format!("<>{text}"), + JsFileSource::jsx(), + JsParserOptions::default(), + ); assert!( !parse.has_errors(), "Source should not have any errors {:?}", @@ -660,7 +664,11 @@ mod tests { } fn parse_jsx_children(children: &str) -> JsxChildList { - let parse = parse(&std::format!("
{children}
"), JsFileSource::jsx()); + let parse = parse( + &std::format!("
{children}
"), + JsFileSource::jsx(), + JsParserOptions::default(), + ); assert!( !parse.has_errors(), diff --git a/crates/rome_js_formatter/src/utils/test_call.rs b/crates/rome_js_formatter/src/utils/test_call.rs index 841230a1ba4..b93daca09c3 100644 --- a/crates/rome_js_formatter/src/utils/test_call.rs +++ b/crates/rome_js_formatter/src/utils/test_call.rs @@ -429,13 +429,13 @@ impl Iterator for CalleeNamesIterator { #[cfg(test)] mod test { use super::{contains_a_test_pattern, is_test_each_pattern_callee}; - use rome_js_parser::parse; + use rome_js_parser::{parse, JsParserOptions}; use rome_js_syntax::{JsCallExpression, JsFileSource, JsTemplateExpression}; use rome_rowan::AstNodeList; fn extract_call_expression(src: &str) -> JsCallExpression { let source_type = JsFileSource::js_module(); - let result = parse(src, source_type); + let result = parse(src, source_type, JsParserOptions::default()); let module = result .tree() .as_js_module() @@ -458,7 +458,7 @@ mod test { fn extract_template(src: &str) -> JsTemplateExpression { let source_type = JsFileSource::js_module(); - let result = parse(src, source_type); + let result = parse(src, source_type, JsParserOptions::default()); let module = result .tree() .as_js_module() diff --git a/crates/rome_js_formatter/tests/language.rs b/crates/rome_js_formatter/tests/language.rs index bc623eca227..d7529ce2df7 100644 --- a/crates/rome_js_formatter/tests/language.rs +++ b/crates/rome_js_formatter/tests/language.rs @@ -5,7 +5,7 @@ use rome_js_formatter::context::{ JsFormatContext, JsFormatOptions, QuoteProperties, QuoteStyle, Semicolons, }; use rome_js_formatter::{format_node, format_range, JsFormatLanguage}; -use rome_js_parser::parse; +use rome_js_parser::{parse, JsParserOptions}; use rome_js_syntax::{JsFileSource, JsLanguage}; use rome_parser::AnyParse; use rome_rowan::{FileSource, SyntaxNode}; @@ -29,7 +29,7 @@ impl TestFormatLanguage for JsTestFormatLanguage { type FormatLanguage = JsFormatLanguage; fn parse(&self, text: &str) -> AnyParse { - let parse = parse(text, self.source_type); + let parse = parse(text, self.source_type, JsParserOptions::default()); AnyParse::new( parse.syntax().as_send().unwrap(), diff --git a/crates/rome_js_formatter/tests/quick_test.rs b/crates/rome_js_formatter/tests/quick_test.rs index 1ccbf9a5f70..08f8dbd0be5 100644 --- a/crates/rome_js_formatter/tests/quick_test.rs +++ b/crates/rome_js_formatter/tests/quick_test.rs @@ -1,7 +1,7 @@ use rome_formatter_test::check_reformat::CheckReformat; use rome_js_formatter::context::{JsFormatOptions, QuoteStyle, Semicolons}; use rome_js_formatter::format_node; -use rome_js_parser::parse; +use rome_js_parser::{parse, JsParserOptions}; use rome_js_syntax::JsFileSource; mod language { @@ -21,7 +21,7 @@ fn quick_test() { ) "#; let syntax = JsFileSource::tsx(); - let tree = parse(src, syntax); + let tree = parse(src, syntax, JsParserOptions::default()); let options = JsFormatOptions::new(syntax) .with_semicolons(Semicolons::AsNeeded) .with_quote_style(QuoteStyle::Double) diff --git a/crates/rome_js_parser/Cargo.toml b/crates/rome_js_parser/Cargo.toml index 246142d2b4e..6943c60c733 100644 --- a/crates/rome_js_parser/Cargo.toml +++ b/crates/rome_js_parser/Cargo.toml @@ -24,6 +24,7 @@ indexmap = { workspace = true } cfg-if = "1.0.0" smallvec = { workspace = true } tracing = { workspace = true } +schemars = { workspace = true, optional = true} [dev-dependencies] tests_macros = { workspace = true } @@ -33,6 +34,7 @@ quickcheck_macros = "1.0.0" [features] serde = ["rome_js_syntax/serde"] +schemars = ["dep:schemars"] tests = [] # cargo-workspaces metadata diff --git a/crates/rome_js_parser/src/lib.rs b/crates/rome_js_parser/src/lib.rs index 5ad9b0cc839..f8ce0f3ec3f 100644 --- a/crates/rome_js_parser/src/lib.rs +++ b/crates/rome_js_parser/src/lib.rs @@ -123,6 +123,7 @@ pub mod test_utils; #[cfg(test)] mod tests; +pub mod options; mod prelude; pub mod syntax; mod token_source; @@ -131,6 +132,7 @@ use crate::prelude::*; pub(crate) use crate::ParsedSyntax::{Absent, Present}; pub use crate::{ lexer::{LexContext, ReLexContext}, + options::JsParserOptions, parse::*, }; pub(crate) use parser::{JsParser, ParseRecovery}; diff --git a/crates/rome_js_parser/src/options.rs b/crates/rome_js_parser/src/options.rs new file mode 100644 index 00000000000..2b523f1f0ae --- /dev/null +++ b/crates/rome_js_parser/src/options.rs @@ -0,0 +1,28 @@ +/// Options to pass to the JavaScript parser +#[derive(Debug, Clone, Default)] +#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +pub struct JsParserOptions { + /// Whether the parsing of the class parameter decorators should happen. + /// + /// This parameter decorators belong to the old language proposal. + pub parse_class_parameter_decorators: bool, +} + +impl JsParserOptions { + pub fn with_parse_class_parameter_decorators(mut self) -> Self { + self.parse_class_parameter_decorators = true; + self + } + + /// Should parse parameter decorators inside classes, e.g.: + /// + /// ```js + /// class C { + /// post(@Param() name) {} + /// } + /// ``` + pub fn should_parse_parameter_decorators(&self) -> bool { + self.parse_class_parameter_decorators + } +} diff --git a/crates/rome_js_parser/src/parse.rs b/crates/rome_js_parser/src/parse.rs index 75fb6ba2e9b..b59866ae16f 100644 --- a/crates/rome_js_parser/src/parse.rs +++ b/crates/rome_js_parser/src/parse.rs @@ -45,7 +45,7 @@ impl Parse { /// The syntax node represented by this Parse result /// /// ``` - /// use rome_js_parser::parse_script; + /// use rome_js_parser::{JsParserOptions, parse_script}; /// use rome_js_syntax::{JsIfStatement, JsSyntaxKind}; /// use rome_rowan::{AstNode, AstNodeList}; /// @@ -55,6 +55,7 @@ impl Parse { /// /* something */ /// } /// ", + /// JsParserOptions::default() /// ); /// /// // The first stmt in the root syntax node (Script) is the if statement. @@ -115,8 +116,9 @@ impl> Parse { fn parse_common( text: &str, source_type: JsFileSource, + options: JsParserOptions, ) -> (Vec>, Vec, Vec) { - let mut parser = JsParser::new(text, source_type); + let mut parser = JsParser::new(text, source_type, options); syntax::program::parse(&mut parser); let (events, trivia, errors) = parser.finish(); @@ -128,11 +130,11 @@ fn parse_common( /// Or turned into a typed [`JsScript`](JsScript) with [`tree`](Parse::tree). /// /// ``` -/// use rome_js_parser::parse_script; +/// use rome_js_parser::{JsParserOptions, parse_script}; /// use rome_js_syntax::{JsSyntaxToken, JsFileSource, JsSyntaxList, JsComputedMemberExpression}; /// use rome_rowan::{AstNode, Direction}; /// -/// let parse = parse_script("foo.bar[2]"); +/// let parse = parse_script("foo.bar[2]", JsParserOptions::default()); /// // Parse returns a JS Root which contains two lists, the directives and the statements, let's get the statements /// let stmt = parse.syntax().children().nth(1).unwrap(); /// // The untyped syntax node of `foo.bar[2]`, the root node is `Script`. @@ -155,10 +157,11 @@ fn parse_common( /// /// assert_eq!(&tokens, &vec!["foo", ".", "bar", "[", "2", "]"]); /// ``` -pub fn parse_script(text: &str) -> Parse { +pub fn parse_script(text: &str, options: JsParserOptions) -> Parse { parse( text, JsFileSource::js_module().with_module_kind(ModuleKind::Script), + options, ) .cast::() .unwrap() @@ -170,14 +173,14 @@ pub fn parse_script(text: &str) -> Parse { /// /// Check the diagnostics emitted by the code /// ``` -/// use rome_js_parser::parse_module; +/// use rome_js_parser::{JsParserOptions, parse_module}; /// let source = r#" /// import { someModule } from "./someModule.js"; /// /// someModule(); /// "#; /// -/// let parse = parse_module(source); +/// let parse = parse_module(source, JsParserOptions::default()); /// /// // Retrieve the diagnostics emitted /// assert_eq!(parse.diagnostics().len(), 0); @@ -185,7 +188,7 @@ pub fn parse_script(text: &str) -> Parse { /// /// Retrieve the emitted AST and check its kind: /// ``` -/// use rome_js_parser::parse_module; +/// use rome_js_parser::{JsParserOptions, parse_module}; /// use rome_js_syntax::JsSyntaxKind; /// use rome_rowan::AstNode; /// let source = r#" @@ -193,15 +196,15 @@ pub fn parse_script(text: &str) -> Parse { /// /// someModule(); /// "#; -/// let parse = parse_module(source); +/// let parse = parse_module(source, JsParserOptions::default()); /// /// let tree = parse.tree(); /// /// assert_eq!(tree.syntax().kind(), JsSyntaxKind::JS_MODULE); /// ``` /// -pub fn parse_module(text: &str) -> Parse { - parse(text, JsFileSource::js_module()) +pub fn parse_module(text: &str, options: JsParserOptions) -> Parse { + parse(text, JsFileSource::js_module(), options) .cast::() .unwrap() } @@ -211,27 +214,27 @@ pub fn parse_module(text: &str) -> Parse { /// ### Examples /// /// ``` -/// use rome_js_parser::parse; +/// use rome_js_parser::{JsParserOptions, parse}; /// use rome_js_syntax::{LanguageVariant, LanguageVersion, ModuleKind, JsFileSource}; /// // parse source text as TypeScript /// let mut module = JsFileSource::ts(); -/// let mut parsed = parse("type F = {}", module); +/// let mut parsed = parse("type F = {}", module, JsParserOptions::default()); /// assert_eq!(parsed.diagnostics().len(), 0); /// // parse source text as JSX /// module = JsFileSource::jsx(); -/// parsed = parse("", module); +/// parsed = parse("", module, JsParserOptions::default()); /// assert_eq!(parsed.diagnostics().len(), 0); /// // parse source text with granular control /// module = JsFileSource::default() /// .with_version(LanguageVersion::ESNext) /// .with_module_kind(ModuleKind::Module) /// .with_variant(LanguageVariant::Jsx); -/// parsed = parse("foo[bar]", module); +/// parsed = parse("foo[bar]", module, JsParserOptions::default()); /// assert_eq!(parsed.diagnostics().len(), 0); /// ``` -pub fn parse(text: &str, source_type: JsFileSource) -> Parse { +pub fn parse(text: &str, source_type: JsFileSource, options: JsParserOptions) -> Parse { let mut cache = NodeCache::default(); - parse_js_with_cache(text, source_type, &mut cache) + parse_js_with_cache(text, source_type, options, &mut cache) } /// Parses the provided string as a EcmaScript program using the provided syntax features and node cache. @@ -239,7 +242,7 @@ pub fn parse(text: &str, source_type: JsFileSource) -> Parse { /// ### Examples /// /// ``` -/// use rome_js_parser::parse_js_with_cache; +/// use rome_js_parser::{JsParserOptions, parse_js_with_cache}; /// use rome_js_syntax::JsFileSource; /// use rome_rowan::NodeCache; /// @@ -247,20 +250,21 @@ pub fn parse(text: &str, source_type: JsFileSource) -> Parse { /// let mut cache = NodeCache::default(); /// let mut source = "function f() { return 2 }"; /// -/// let parsed = parse_js_with_cache(source, source_type, &mut cache); +/// let parsed = parse_js_with_cache(source, source_type, JsParserOptions::default(), &mut cache); /// assert_eq!(parsed.diagnostics().len(), 0); /// /// source = "function bar() { return 3 }"; -/// let parsed = parse_js_with_cache(source, source_type, &mut cache); +/// let parsed = parse_js_with_cache(source, source_type, JsParserOptions::default(), &mut cache); /// assert_eq!(parsed.diagnostics().len(), 0); /// ``` pub fn parse_js_with_cache( text: &str, source_type: JsFileSource, + options: JsParserOptions, cache: &mut NodeCache, ) -> Parse { tracing::debug_span!("parse").in_scope(move || { - let (events, errors, tokens) = parse_common(text, source_type); + let (events, errors, tokens) = parse_common(text, source_type, options); let mut tree_sink = JsLosslessTreeSink::with_cache(text, &tokens, cache); rome_parser::event::process(&mut tree_sink, events, errors); let (green, parse_errors) = tree_sink.finish(); diff --git a/crates/rome_js_parser/src/parser.rs b/crates/rome_js_parser/src/parser.rs index bfa6ac155e7..7c7c1cd3e10 100644 --- a/crates/rome_js_parser/src/parser.rs +++ b/crates/rome_js_parser/src/parser.rs @@ -35,11 +35,13 @@ pub struct JsParser<'source> { pub source_type: JsFileSource, context: ParserContext, source: JsTokenSource<'source>, + #[allow(dead_code)] + options: JsParserOptions, } impl<'source> JsParser<'source> { /// Creates a new parser that parses the `source`. - pub fn new(source: &'source str, source_type: JsFileSource) -> Self { + pub fn new(source: &'source str, source_type: JsFileSource, options: JsParserOptions) -> Self { let source = JsTokenSource::from_str(source); JsParser { @@ -47,6 +49,7 @@ impl<'source> JsParser<'source> { source_type, context: ParserContext::default(), source, + options, } } @@ -212,6 +215,7 @@ pub struct JsParserCheckpoint { #[cfg(test)] mod tests { use crate::prelude::*; + use crate::JsParserOptions; use rome_js_syntax::{JsFileSource, JsSyntaxKind}; #[test] @@ -219,7 +223,11 @@ mod tests { expected = "Marker must either be `completed` or `abandoned` to avoid that children are implicitly attached to a marker's parent." )] fn uncompleted_markers_panic() { - let mut parser = JsParser::new("'use strict'", JsFileSource::default()); + let mut parser = JsParser::new( + "'use strict'", + JsFileSource::default(), + JsParserOptions::default(), + ); let _ = parser.start(); // drop the marker without calling complete or abandon @@ -227,7 +235,11 @@ mod tests { #[test] fn completed_marker_doesnt_panic() { - let mut p = JsParser::new("'use strict'", JsFileSource::default()); + let mut p = JsParser::new( + "'use strict'", + JsFileSource::default(), + JsParserOptions::default(), + ); let m = p.start(); p.expect(JsSyntaxKind::JS_STRING_LITERAL); @@ -236,7 +248,11 @@ mod tests { #[test] fn abandoned_marker_doesnt_panic() { - let mut p = JsParser::new("'use strict'", JsFileSource::default()); + let mut p = JsParser::new( + "'use strict'", + JsFileSource::default(), + JsParserOptions::default(), + ); let m = p.start(); m.abandon(&mut p); diff --git a/crates/rome_js_parser/src/tests.rs b/crates/rome_js_parser/src/tests.rs index 49e2180596d..a73100b2266 100644 --- a/crates/rome_js_parser/src/tests.rs +++ b/crates/rome_js_parser/src/tests.rs @@ -1,4 +1,4 @@ -use crate::{parse, parse_module, test_utils::assert_errors_are_absent, Parse}; +use crate::{parse, parse_module, test_utils::assert_errors_are_absent, JsParserOptions, Parse}; use expect_test::expect_file; use rome_console::fmt::{Formatter, Termcolor}; use rome_console::markup; @@ -17,7 +17,7 @@ fn parser_smoke_test() { import "x" with { type: "json" } "#; - let module = parse(src, JsFileSource::tsx()); + let module = parse(src, JsFileSource::tsx(), JsParserOptions::default()); assert_errors_are_absent(&module, Path::new("parser_smoke_test")); } @@ -27,7 +27,7 @@ fn parser_missing_smoke_test() { console.log("Hello world"; "#; - let module = parse_module(src); + let module = parse_module(src, JsParserOptions::default()); let arg_list = module .syntax() @@ -58,7 +58,7 @@ fn try_parse(path: &str, text: &str) -> Parse { path.try_into().unwrap() }; - let parse = parse(text, source_type); + let parse = parse(text, source_type, JsParserOptions::default()); assert_eq!( parse.syntax().to_string(), @@ -160,7 +160,7 @@ fn assert_errors_are_present(program: &Parse, path: &Path) { #[test] pub fn test_trivia_attached_to_tokens() { let text = "/**/let a = 1; // nice variable \n /*hey*/ let \t b = 2; // another nice variable"; - let m = parse_module(text); + let m = parse_module(text, JsParserOptions::default()); let mut tokens = m.syntax().descendants_tokens(Direction::Next); let is_let = |x: &JsSyntaxToken| x.text_trimmed() == "let"; @@ -194,7 +194,7 @@ pub fn test_trivia_attached_to_tokens() { #[test] pub fn jsroot_display_text_and_trimmed() { let code = " let a = 1; \n "; - let root = parse_module(code); + let root = parse_module(code, JsParserOptions::default()); let syntax = root.syntax(); assert_eq!(format!("{}", syntax), code); @@ -210,7 +210,7 @@ pub fn jsroot_display_text_and_trimmed() { pub fn jsroot_ranges() { // 0123456789A let code = " let a = 1;"; - let root = parse_module(code); + let root = parse_module(code, JsParserOptions::default()); let syntax = root.syntax(); let first_let = syntax.first_token().unwrap(); @@ -239,7 +239,7 @@ pub fn jsroot_ranges() { pub fn node_range_must_be_correct() { // 0123456789A123456789B123456789 let text = " function foo() { let a = 1; }"; - let root = parse_module(text); + let root = parse_module(text, JsParserOptions::default()); let var_decl = root .syntax() @@ -260,7 +260,7 @@ pub fn node_range_must_be_correct() { pub fn last_trivia_must_be_appended_to_eof() { // 0123456789A123456789B123456789CC let text = " function foo() { let a = 1; }\n"; - let root = parse_module(text); + let root = parse_module(text, JsParserOptions::default()); let syntax = root.syntax(); let range = syntax.text_range(); @@ -275,7 +275,7 @@ pub fn last_trivia_must_be_appended_to_eof() { pub fn just_trivia_must_be_appended_to_eof() { // 0123456789A123456789B123456789C123 let text = "// just trivia... nothing else...."; - let root = parse_module(text); + let root = parse_module(text, JsParserOptions::default()); let syntax = root.syntax(); let range = syntax.text_range(); @@ -289,7 +289,7 @@ pub fn just_trivia_must_be_appended_to_eof() { #[test] pub fn node_contains_comments() { let text = "true && true // comment"; - let root = parse_module(text); + let root = parse_module(text, JsParserOptions::default()); let syntax = root.syntax(); assert!(syntax.has_comments_descendants()); @@ -298,7 +298,7 @@ pub fn node_contains_comments() { #[test] fn parser_regexp_after_operator() { fn assert_no_errors(src: &str) { - let module = parse(src, JsFileSource::js_script()); + let module = parse(src, JsFileSource::js_script(), JsParserOptions::default()); assert_errors_are_absent(&module, Path::new("parser_regexp_after_operator")); } assert_no_errors(r#"a=/a/"#); @@ -311,7 +311,7 @@ fn parser_regexp_after_operator() { #[test] pub fn node_contains_trailing_comments() { let text = "true && (3 - 2 == 0) // comment"; - let root = parse_module(text); + let root = parse_module(text, JsParserOptions::default()); let syntax = root.syntax(); let node = syntax .descendants() @@ -330,7 +330,7 @@ pub fn node_contains_leading_comments() { let text = r"true && // comment (3 - 2 == 0)"; - let root = parse_module(text); + let root = parse_module(text, JsParserOptions::default()); let syntax = root.syntax(); let node = syntax .descendants() @@ -349,7 +349,7 @@ pub fn node_has_comments() { let text = r"true && // comment (3 - 2 == 0)"; - let root = parse_module(text); + let root = parse_module(text, JsParserOptions::default()); let syntax = root.syntax(); let node = syntax .descendants() @@ -366,7 +366,7 @@ pub fn node_has_comments() { fn diagnostics_print_correctly() { let text = r"const a"; - let root = parse_module(text); + let root = parse_module(text, JsParserOptions::default()); for diagnostic in root.diagnostics() { let mut write = rome_diagnostics::termcolor::Buffer::no_color(); let error = diagnostic @@ -396,7 +396,7 @@ class Foo { @decorator declare [b]: number; } "#; - let root = parse(code, JsFileSource::ts()); + let root = parse(code, JsFileSource::ts(), JsParserOptions::default()); let syntax = root.syntax(); dbg!(syntax, root.diagnostics()); diff --git a/crates/rome_js_semantic/src/events.rs b/crates/rome_js_semantic/src/events.rs index c6d127994ed..b3700a4f197 100644 --- a/crates/rome_js_semantic/src/events.rs +++ b/crates/rome_js_semantic/src/events.rs @@ -140,7 +140,7 @@ impl SemanticEvent { /// use rome_js_parser::*; /// use rome_js_syntax::*; /// use rome_js_semantic::*; -/// let tree = parse("let a = 1", JsFileSource::js_script()); +/// let tree = parse("let a = 1", JsFileSource::js_script(), JsParserOptions::default()); /// let mut extractor = SemanticEventExtractor::new(); /// for e in tree.syntax().preorder() { /// match e { @@ -1068,7 +1068,7 @@ impl Iterator for SemanticEventIterator { /// use rome_js_parser::*; /// use rome_js_syntax::*; /// use rome_js_semantic::*; -/// let tree = parse("let a = 1", JsFileSource::js_script()); +/// let tree = parse("let a = 1", JsFileSource::js_script(), JsParserOptions::default()); /// for e in semantic_events(tree.syntax()) { /// dbg!(e); /// } diff --git a/crates/rome_js_semantic/src/semantic_model/closure.rs b/crates/rome_js_semantic/src/semantic_model/closure.rs index 14611f2a6d1..f50487f214c 100644 --- a/crates/rome_js_semantic/src/semantic_model/closure.rs +++ b/crates/rome_js_semantic/src/semantic_model/closure.rs @@ -359,11 +359,12 @@ impl ClosureExtensions for T {} #[cfg(test)] mod test { use super::*; + use rome_js_parser::JsParserOptions; use rome_js_syntax::{JsArrowFunctionExpression, JsFileSource, JsSyntaxKind}; use rome_rowan::SyntaxNodeCast; fn assert_closure(code: &str, name: &str, captures: &[&str]) { - let r = rome_js_parser::parse(code, JsFileSource::tsx()); + let r = rome_js_parser::parse(code, JsFileSource::tsx(), JsParserOptions::default()); let model = semantic_model(&r.tree(), SemanticModelOptions::default()); let closure = if name != "ARROWFUNCTION" { @@ -405,7 +406,7 @@ mod test { } fn get_closure_children(code: &str, name: &str) -> Vec { - let r = rome_js_parser::parse(code, JsFileSource::tsx()); + let r = rome_js_parser::parse(code, JsFileSource::tsx(), JsParserOptions::default()); let model = semantic_model(&r.tree(), SemanticModelOptions::default()); let closure = if name != "ARROWFUNCTION" { diff --git a/crates/rome_js_semantic/src/semantic_model/is_constant.rs b/crates/rome_js_semantic/src/semantic_model/is_constant.rs index f87b216d390..3df2966aefc 100644 --- a/crates/rome_js_semantic/src/semantic_model/is_constant.rs +++ b/crates/rome_js_semantic/src/semantic_model/is_constant.rs @@ -12,6 +12,7 @@ pub fn is_constant(expr: &AnyJsExpression) -> bool { #[cfg(test)] mod tests { + use rome_js_parser::JsParserOptions; use rome_js_syntax::{JsFileSource, JsIdentifierBinding, JsVariableDeclarator}; use crate::{semantic_model, SemanticModelOptions}; @@ -19,7 +20,7 @@ mod tests { fn assert_is_const(code: &str, is_const: bool) { use rome_rowan::AstNode; use rome_rowan::SyntaxNodeCast; - let r = rome_js_parser::parse(code, JsFileSource::js_module()); + let r = rome_js_parser::parse(code, JsFileSource::js_module(), JsParserOptions::default()); let model = semantic_model(&r.tree(), SemanticModelOptions::default()); let a_reference = r diff --git a/crates/rome_js_semantic/src/semantic_model/model.rs b/crates/rome_js_semantic/src/semantic_model/model.rs index 2e2d361aa13..80fba36e51c 100644 --- a/crates/rome_js_semantic/src/semantic_model/model.rs +++ b/crates/rome_js_semantic/src/semantic_model/model.rs @@ -138,11 +138,12 @@ impl SemanticModel { /// Can also be called from [AstNode]::scope extension method. /// /// ```rust + /// use rome_js_parser::JsParserOptions; /// use rome_rowan::{AstNode, SyntaxNodeCast}; /// use rome_js_syntax::{JsFileSource, JsReferenceIdentifier}; /// use rome_js_semantic::{semantic_model, SemanticModelOptions, SemanticScopeExtensions}; /// - /// let r = rome_js_parser::parse("function f(){let a = arguments[0]; let b = a + 1;}", JsFileSource::js_module()); + /// let r = rome_js_parser::parse("function f(){let a = arguments[0]; let b = a + 1;}", JsFileSource::js_module(), JsParserOptions::default()); /// let model = semantic_model(&r.tree(), SemanticModelOptions::default()); /// /// let arguments_reference = r @@ -187,11 +188,12 @@ impl SemanticModel { /// Can also be called from "binding" extension method. /// /// ```rust + /// use rome_js_parser::JsParserOptions; /// use rome_rowan::{AstNode, SyntaxNodeCast}; /// use rome_js_syntax::{JsFileSource, JsReferenceIdentifier}; /// use rome_js_semantic::{semantic_model, BindingExtensions, SemanticModelOptions}; /// - /// let r = rome_js_parser::parse("function f(){let a = arguments[0]; let b = a + 1;}", JsFileSource::js_module()); + /// let r = rome_js_parser::parse("function f(){let a = arguments[0]; let b = a + 1;}", JsFileSource::js_module(), JsParserOptions::default()); /// let model = semantic_model(&r.tree(), SemanticModelOptions::default()); /// /// let arguments_reference = r @@ -339,11 +341,12 @@ impl SemanticModel { /// Returns all [Call] of a [AnyJsFunction]. /// /// ```rust + /// use rome_js_parser::JsParserOptions; /// use rome_rowan::{AstNode, SyntaxNodeCast}; /// use rome_js_syntax::{JsFileSource, AnyJsFunction}; /// use rome_js_semantic::{semantic_model, CallsExtensions, SemanticModelOptions}; /// - /// let r = rome_js_parser::parse("function f(){} f() f()", JsFileSource::js_module()); + /// let r = rome_js_parser::parse("function f(){} f() f()", JsFileSource::js_module(), JsParserOptions::default()); /// let model = semantic_model(&r.tree(), SemanticModelOptions::default()); /// /// let f_declaration = r diff --git a/crates/rome_js_semantic/src/semantic_model/tests.rs b/crates/rome_js_semantic/src/semantic_model/tests.rs index e68caebf999..ecd1bdefee1 100644 --- a/crates/rome_js_semantic/src/semantic_model/tests.rs +++ b/crates/rome_js_semantic/src/semantic_model/tests.rs @@ -4,6 +4,7 @@ mod test { semantic_model, BindingExtensions, CanBeImportedExported, SemanticModelOptions, SemanticScopeExtensions, }; + use rome_js_parser::JsParserOptions; use rome_js_syntax::{ JsFileSource, JsIdentifierAssignment, JsIdentifierBinding, JsReferenceIdentifier, JsSyntaxKind, TsIdentifierBinding, @@ -15,6 +16,7 @@ mod test { let r = rome_js_parser::parse( "function f(){let a = arguments[0]; let b = a + 1; b = 2; console.log(b)}", JsFileSource::js_module(), + JsParserOptions::default(), ); let model = semantic_model(&r.tree(), SemanticModelOptions::default()); @@ -115,7 +117,11 @@ mod test { #[test] pub fn ok_semantic_model_function_scope() { - let r = rome_js_parser::parse("function f() {} function g() {}", JsFileSource::js_module()); + let r = rome_js_parser::parse( + "function f() {} function g() {}", + JsFileSource::js_module(), + JsParserOptions::default(), + ); let model = semantic_model(&r.tree(), SemanticModelOptions::default()); let function_f = r @@ -153,7 +159,7 @@ mod test { /// Finds the last time a token named "name" is used and see if its node is marked as exported fn assert_is_exported(is_exported: bool, name: &str, code: &str) { - let r = rome_js_parser::parse(code, JsFileSource::tsx()); + let r = rome_js_parser::parse(code, JsFileSource::tsx(), JsParserOptions::default()); let model = semantic_model(&r.tree(), SemanticModelOptions::default()); let node = r @@ -286,7 +292,11 @@ mod test { #[test] pub fn ok_semantic_model_globals() { - let r = rome_js_parser::parse("console.log()", JsFileSource::js_module()); + let r = rome_js_parser::parse( + "console.log()", + JsFileSource::js_module(), + JsParserOptions::default(), + ); let mut options = SemanticModelOptions::default(); options.globals.insert("console".into()); diff --git a/crates/rome_js_semantic/src/tests/assertions.rs b/crates/rome_js_semantic/src/tests/assertions.rs index 1e8316655de..f4f86c9890d 100644 --- a/crates/rome_js_semantic/src/tests/assertions.rs +++ b/crates/rome_js_semantic/src/tests/assertions.rs @@ -4,6 +4,7 @@ use rome_diagnostics::location::AsSpan; use rome_diagnostics::{ Advices, Diagnostic, DiagnosticExt, Location, LogCategory, PrintDiagnostic, Visit, }; +use rome_js_parser::JsParserOptions; use rome_js_syntax::{AnyJsRoot, JsFileSource, JsSyntaxToken, TextRange, TextSize, WalkEvent}; use rome_rowan::{AstNode, NodeOrToken}; use std::collections::{BTreeMap, HashMap}; @@ -104,7 +105,7 @@ use std::collections::{BTreeMap, HashMap}; /// if(true) ;/*NOEVENT*/; /// ``` pub fn assert(code: &str, test_name: &str) { - let r = rome_js_parser::parse(code, JsFileSource::tsx()); + let r = rome_js_parser::parse(code, JsFileSource::tsx(), JsParserOptions::default()); if r.has_errors() { let mut console = EnvConsole::default(); diff --git a/crates/rome_service/src/file_handlers/javascript.rs b/crates/rome_service/src/file_handlers/javascript.rs index cc2fb8c9959..96324d231ea 100644 --- a/crates/rome_service/src/file_handlers/javascript.rs +++ b/crates/rome_service/src/file_handlers/javascript.rs @@ -29,6 +29,7 @@ use rome_js_formatter::context::{ trailing_comma::TrailingComma, QuoteProperties, QuoteStyle, Semicolons, }; use rome_js_formatter::{context::JsFormatOptions, format_node}; +use rome_js_parser::JsParserOptions; use rome_js_semantic::{semantic_model, SemanticModelOptions}; use rome_js_syntax::{ AnyJsRoot, JsFileSource, JsLanguage, JsSyntaxNode, TextRange, TextSize, TokenAtOffset, @@ -51,6 +52,12 @@ pub struct JsFormatterSettings { pub semicolons: Option, } +#[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] +#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] +pub struct JsParserSettings { + pub parse_class_parameter_decorators: bool, +} + #[derive(Debug, Clone, Default, serde::Serialize, serde::Deserialize)] #[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))] pub struct JsLinterSettings { @@ -66,6 +73,7 @@ impl Language for JsLanguage { type LinterSettings = JsLinterSettings; type FormatOptions = JsFormatOptions; type OrganizeImportsSettings = JsOrganizeImportsSettings; + type ParserSettings = JsParserSettings; fn lookup_settings(languages: &LanguagesSettings) -> &LanguageSettings { &languages.javascript @@ -143,6 +151,7 @@ fn parse( rome_path: &RomePath, language_hint: LanguageId, text: &str, + settings: SettingsHandle, cache: &mut NodeCache, ) -> AnyParse { let source_type = @@ -152,8 +161,11 @@ fn parse( LanguageId::TypeScriptReact => JsFileSource::tsx(), _ => JsFileSource::js_module(), }); - - let parse = rome_js_parser::parse_js_with_cache(text, source_type, cache); + let settings = &settings.as_ref().languages.javascript.parser; + let options = JsParserOptions { + parse_class_parameter_decorators: settings.parse_class_parameter_decorators, + }; + let parse = rome_js_parser::parse_js_with_cache(text, source_type, options, cache); let root = parse.syntax(); let diagnostics = parse.into_diagnostics(); AnyParse::new( diff --git a/crates/rome_service/src/file_handlers/json.rs b/crates/rome_service/src/file_handlers/json.rs index 368663528fb..1ef13b128b6 100644 --- a/crates/rome_service/src/file_handlers/json.rs +++ b/crates/rome_service/src/file_handlers/json.rs @@ -27,6 +27,7 @@ impl Language for JsonLanguage { type LinterSettings = (); type FormatOptions = JsonFormatOptions; type OrganizeImportsSettings = (); + type ParserSettings = (); fn lookup_settings(language: &LanguagesSettings) -> &LanguageSettings { &language.json @@ -83,7 +84,13 @@ impl ExtensionHandler for JsonFileHandler { } } -fn parse(_: &RomePath, _: LanguageId, text: &str, cache: &mut NodeCache) -> AnyParse { +fn parse( + _: &RomePath, + _: LanguageId, + text: &str, + _: SettingsHandle, + cache: &mut NodeCache, +) -> AnyParse { let parse = rome_json_parser::parse_json_with_cache(text, cache); let root = parse.syntax(); let diagnostics = parse.into_diagnostics(); diff --git a/crates/rome_service/src/file_handlers/mod.rs b/crates/rome_service/src/file_handlers/mod.rs index 372b89e9f58..354e483794b 100644 --- a/crates/rome_service/src/file_handlers/mod.rs +++ b/crates/rome_service/src/file_handlers/mod.rs @@ -168,7 +168,7 @@ pub struct Capabilities { pub(crate) formatter: FormatterCapabilities, } -type Parse = fn(&RomePath, Language, &str, &mut NodeCache) -> AnyParse; +type Parse = fn(&RomePath, Language, &str, SettingsHandle, &mut NodeCache) -> AnyParse; #[derive(Default)] pub struct ParserCapabilities { diff --git a/crates/rome_service/src/settings.rs b/crates/rome_service/src/settings.rs index 2560d4c2fff..1b10e43e8db 100644 --- a/crates/rome_service/src/settings.rs +++ b/crates/rome_service/src/settings.rs @@ -82,6 +82,15 @@ impl WorkspaceSettings { self.languages.javascript.formatter.semicolons = formatter.semicolons; } + if let Some(parser) = javascript.parser { + self.languages + .javascript + .parser + .parse_class_parameter_decorators = parser + .unsafe_parameter_decorators_enabled + .unwrap_or_default(); + } + let organize_imports = javascript.organize_imports; if let Some(_organize_imports) = organize_imports {} } @@ -207,6 +216,9 @@ pub trait Language: rome_rowan::Language { /// Fully resolved formatter options type for this language type FormatOptions: rome_formatter::FormatOptions; + /// Settings that belong to the parser + type ParserSettings: Default; + /// Read the settings type for this language from the [LanguagesSettings] map fn lookup_settings(languages: &LanguagesSettings) -> &LanguageSettings; @@ -232,6 +244,9 @@ pub struct LanguageSettings { /// Organize imports settings for this language pub organize_imports: L::OrganizeImportsSettings, + + /// Parser settings for this language + pub parser: L::ParserSettings, } /// Filesystem settings for the entire workspace diff --git a/crates/rome_service/src/workspace/server.rs b/crates/rome_service/src/workspace/server.rs index 820b960aaf3..356a41734a9 100644 --- a/crates/rome_service/src/workspace/server.rs +++ b/crates/rome_service/src/workspace/server.rs @@ -180,10 +180,12 @@ impl WorkspaceServer { )); } + let settings = self.settings(); let parsed = parse( rome_path, document.language_hint, document.content.as_str(), + settings, &mut document.node_cache, ); diff --git a/xtask/bench/src/language.rs b/xtask/bench/src/language.rs index 7a4af8f5960..10e08c36bc7 100644 --- a/xtask/bench/src/language.rs +++ b/xtask/bench/src/language.rs @@ -4,6 +4,7 @@ use rome_analyze::{AnalysisFilter, AnalyzerOptions, ControlFlow, Never, RuleCate use rome_formatter::{FormatResult, Formatted, PrintResult, Printed}; use rome_js_analyze::analyze; use rome_js_formatter::context::{JsFormatContext, JsFormatOptions}; +use rome_js_parser::JsParserOptions; use rome_js_syntax::{AnyJsRoot, JsFileSource, JsSyntaxNode}; use rome_json_formatter::context::{JsonFormatContext, JsonFormatOptions}; use rome_json_syntax::JsonSyntaxNode; @@ -28,9 +29,10 @@ impl<'a> Parse<'a> { pub fn parse(&self) -> Parsed { match self { - Parse::JavaScript(source_type, code) => { - Parsed::JavaScript(rome_js_parser::parse(code, *source_type), *source_type) - } + Parse::JavaScript(source_type, code) => Parsed::JavaScript( + rome_js_parser::parse(code, *source_type, JsParserOptions::default()), + *source_type, + ), Parse::Json(code) => Parsed::Json(rome_json_parser::parse_json(code)), } } @@ -38,7 +40,12 @@ impl<'a> Parse<'a> { pub fn parse_with_cache(&self, cache: &mut NodeCache) -> Parsed { match self { Parse::JavaScript(source_type, code) => Parsed::JavaScript( - rome_js_parser::parse_js_with_cache(code, *source_type, cache), + rome_js_parser::parse_js_with_cache( + code, + *source_type, + JsParserOptions::default(), + cache, + ), *source_type, ), Parse::Json(code) => Parsed::Json(rome_json_parser::parse_json_with_cache(code, cache)), diff --git a/xtask/coverage/src/js/test262.rs b/xtask/coverage/src/js/test262.rs index b06597f2d6b..5f3367a8d30 100644 --- a/xtask/coverage/src/js/test262.rs +++ b/xtask/coverage/src/js/test262.rs @@ -2,7 +2,7 @@ use crate::runner::{ create_bogus_node_in_tree_diagnostic, TestCase, TestCaseFiles, TestRunOutcome, TestSuite, }; use regex::Regex; -use rome_js_parser::parse; +use rome_js_parser::{parse, JsParserOptions}; use rome_js_syntax::JsFileSource; use rome_rowan::syntax::SyntaxKind; use rome_rowan::AstNode; @@ -97,9 +97,14 @@ impl Test262TestCase { .filter(|neg| neg.phase == Phase::Parse) .is_some(); - let files = TestCaseFiles::single(self.name.clone(), self.code.clone(), source_type); + let files = TestCaseFiles::single( + self.name.clone(), + self.code.clone(), + source_type, + JsParserOptions::default(), + ); - match parse(&code, source_type).ok() { + match parse(&code, source_type, JsParserOptions::default()).ok() { Ok(root) if !should_fail => { if let Some(bogus) = root .syntax() diff --git a/xtask/coverage/src/jsx/jsx_babel.rs b/xtask/coverage/src/jsx/jsx_babel.rs index ddcbf3ad598..bfaa8e35c26 100644 --- a/xtask/coverage/src/jsx/jsx_babel.rs +++ b/xtask/coverage/src/jsx/jsx_babel.rs @@ -3,7 +3,7 @@ use crate::{ check_file_encoding, runner::{TestCase, TestCaseFiles, TestRunOutcome, TestSuite}, }; -use rome_js_parser::parse; +use rome_js_parser::{parse, JsParserOptions}; use rome_js_syntax::{JsFileSource, ModuleKind}; use rome_rowan::SyntaxKind; use std::path::Path; @@ -36,8 +36,13 @@ impl TestCase for BabelJsxTestCase { fn run(&self) -> TestRunOutcome { let source_type = JsFileSource::jsx().with_module_kind(ModuleKind::Script); - let files = TestCaseFiles::single(self.name().to_string(), self.code.clone(), source_type); - let result = parse(&self.code, source_type); + let files = TestCaseFiles::single( + self.name().to_string(), + self.code.clone(), + source_type, + JsParserOptions::default(), + ); + let result = parse(&self.code, source_type, JsParserOptions::default()); if result.diagnostics().is_empty() { if let Some(bogus) = result diff --git a/xtask/coverage/src/runner.rs b/xtask/coverage/src/runner.rs index 9766ff4dee3..c03d736e3e2 100644 --- a/xtask/coverage/src/runner.rs +++ b/xtask/coverage/src/runner.rs @@ -5,7 +5,7 @@ use rome_diagnostics::console::markup; use rome_diagnostics::termcolor::Buffer; use rome_diagnostics::Error; use rome_diagnostics::PrintDiagnostic; -use rome_js_parser::{parse, Parse}; +use rome_js_parser::{parse, JsParserOptions, Parse}; use rome_js_syntax::{AnyJsRoot, JsFileSource, JsSyntaxNode}; use rome_rowan::SyntaxKind; use std::fmt::Debug; @@ -78,11 +78,13 @@ pub(crate) struct TestCaseFile { /// The source type used to parse the file source_type: JsFileSource, + + options: JsParserOptions, } impl TestCaseFile { pub(crate) fn parse(&self) -> Parse { - parse(&self.code, self.source_type) + parse(&self.code, self.source_type, self.options.clone()) } pub(crate) fn name(&self) -> &str { @@ -109,12 +111,18 @@ pub(crate) struct TestCaseFiles { } impl TestCaseFiles { - pub(crate) fn single(name: String, code: String, source_type: JsFileSource) -> Self { + pub(crate) fn single( + name: String, + code: String, + source_type: JsFileSource, + options: JsParserOptions, + ) -> Self { Self { files: vec![TestCaseFile { name, code, source_type, + options, }], } } @@ -123,11 +131,18 @@ impl TestCaseFiles { Self { files: vec![] } } - pub(crate) fn add(&mut self, name: String, code: String, source_type: JsFileSource) { + pub(crate) fn add( + &mut self, + name: String, + code: String, + source_type: JsFileSource, + options: JsParserOptions, + ) { self.files.push(TestCaseFile { name, code, source_type, + options, }) } diff --git a/xtask/coverage/src/symbols/msts.rs b/xtask/coverage/src/symbols/msts.rs index a3110a4a447..10c1fe2dc35 100644 --- a/xtask/coverage/src/symbols/msts.rs +++ b/xtask/coverage/src/symbols/msts.rs @@ -4,6 +4,7 @@ use rome_js_syntax::JsFileSource; use super::utils::{parse_separated_list, parse_str, parse_until_chr, parse_whitespace0}; use crate::check_file_encoding; use crate::runner::{TestCase, TestCaseFiles, TestRunOutcome, TestSuite}; +use rome_js_parser::JsParserOptions; use std::fmt::Write; use std::path::{Path, PathBuf}; use std::str::FromStr; @@ -62,6 +63,7 @@ impl TestCase for SymbolsMicrosoftTestCase { self.name.clone(), "".to_string(), JsFileSource::tsx(), + JsParserOptions::default(), ), errors: vec![], } @@ -69,9 +71,14 @@ impl TestCase for SymbolsMicrosoftTestCase { } }; - let t = TestCaseFiles::single(self.name.clone(), code.clone(), JsFileSource::tsx()); + let t = TestCaseFiles::single( + self.name.clone(), + code.clone(), + JsFileSource::tsx(), + JsParserOptions::default(), + ); - let r = rome_js_parser::parse(&code, JsFileSource::tsx()); + let r = rome_js_parser::parse(&code, JsFileSource::tsx(), JsParserOptions::default()); let mut actual: Vec<_> = rome_js_semantic::semantic_events(r.syntax()) .into_iter() .filter(|x| { diff --git a/xtask/coverage/src/ts/ts_babel.rs b/xtask/coverage/src/ts/ts_babel.rs index 997cb65873b..527473447c8 100644 --- a/xtask/coverage/src/ts/ts_babel.rs +++ b/xtask/coverage/src/ts/ts_babel.rs @@ -3,6 +3,7 @@ use crate::{ check_file_encoding, runner::{TestCase, TestCaseFiles, TestRunOutcome, TestSuite}, }; +use rome_js_parser::JsParserOptions; use rome_js_syntax::{JsFileSource, LanguageVariant}; use rome_rowan::SyntaxKind; use std::path::Path; @@ -42,9 +43,14 @@ impl TestCase for BabelTypescriptTestCase { fn run(&self) -> TestRunOutcome { let source_type = JsFileSource::ts().with_variant(self.variant); - let files = TestCaseFiles::single(self.name().to_string(), self.code.clone(), source_type); - - let result = rome_js_parser::parse(&self.code, source_type); + let files = TestCaseFiles::single( + self.name().to_string(), + self.code.clone(), + source_type, + JsParserOptions::default(), + ); + + let result = rome_js_parser::parse(&self.code, source_type, JsParserOptions::default()); if self.expected_to_fail && result.diagnostics().is_empty() { TestRunOutcome::IncorrectlyPassed(files) diff --git a/xtask/coverage/src/ts/ts_microsoft.rs b/xtask/coverage/src/ts/ts_microsoft.rs index ba95916249f..5634c8637ad 100644 --- a/xtask/coverage/src/ts/ts_microsoft.rs +++ b/xtask/coverage/src/ts/ts_microsoft.rs @@ -3,6 +3,7 @@ use crate::runner::{ create_bogus_node_in_tree_diagnostic, TestCase, TestCaseFiles, TestRunOutcome, TestSuite, }; use regex::Regex; +use rome_js_parser::JsParserOptions; use rome_js_syntax::{JsFileSource, ModuleKind}; use rome_rowan::{AstNode, SyntaxKind}; use std::convert::TryFrom; @@ -184,7 +185,7 @@ fn add_file_if_supported(files: &mut TestCaseFiles, name: String, content: Strin source_type = source_type.with_module_kind(ModuleKind::Script); } - files.add(name, content, source_type) + files.add(name, content, source_type, JsParserOptions::default()) } } diff --git a/xtask/lintdoc/src/main.rs b/xtask/lintdoc/src/main.rs index b1ddc00dd1e..6a4779e2edb 100644 --- a/xtask/lintdoc/src/main.rs +++ b/xtask/lintdoc/src/main.rs @@ -11,6 +11,7 @@ use rome_console::{ use rome_diagnostics::termcolor::NoColor; use rome_diagnostics::{Diagnostic, DiagnosticExt, PrintDiagnostic}; use rome_js_analyze::{analyze, visit_registry}; +use rome_js_parser::JsParserOptions; use rome_js_syntax::{JsFileSource, JsLanguage, Language, LanguageVariant, ModuleKind}; use rome_service::settings::WorkspaceSettings; use std::{ @@ -543,7 +544,7 @@ fn assert_lint( match test.block_type { BlockType::Js(source_type) => { - let parse = rome_js_parser::parse(code, source_type); + let parse = rome_js_parser::parse(code, source_type, JsParserOptions::default()); if parse.has_errors() { for diag in parse.into_diagnostics() {