diff --git a/.github/workflows/performance-and-size.yml b/.github/workflows/performance-and-size.yml index ae4d5851..2e980aa8 100644 --- a/.github/workflows/performance-and-size.yml +++ b/.github/workflows/performance-and-size.yml @@ -45,7 +45,6 @@ jobs: env: CARGO_PROFILE_RELEASE_DEBUG: true - - name: Download files run: | curl https://esm.sh/v128/react-dom@18.2.0/es2022/react-dom.mjs > react.js @@ -62,7 +61,7 @@ jobs: \`\`\`ts " >> $GITHUB_STEP_SUMMARY lines=$(scc -c --no-cocomo -f json .\private\tocheck\all.ts | jq ".[0].Code") - echo "// $lines of TypeScript" + echo "// $lines of TypeScript" >> $GITHUB_STEP_SUMMARY cat demo.ts >> $GITHUB_STEP_SUMMARY echo "\`\`\` diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ed192f46..2a2238a6 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -25,14 +25,14 @@ Now in the `ezno` directory, `cargo run` should show the CLI. If you don't want to run the whole Ezno CLI. You can run just the checker with ```shell -cargo run -p ezno-checker -F ezno-parser --example check path/to/file.ts +cargo run -p ezno-checker --example run path/to/file.ts ``` If you want to check all the checker tests ```shell cargo test -p ezno-checker-specification -# To including staging.md (which is really useful for keeping new fixes/additions separate) +# To include the Staging file (which is really useful for keeping new fixes/additions separate) cargo test -p ezno-checker-specification -F staging # and for all the tests (which includes all of to_implement.md) cargo test -p ezno-checker-specification -F all diff --git a/README.md b/README.md index da2b3fa7..479afa44 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ A JavaScript compiler and TypeScript checker written in Rust with a focus on sta > [!IMPORTANT] > Ezno is in active development and **currently does not support enough features to check existing projects**. Check out the [getting started guide](./checker/docs/getting-started.md) for experimenting with what it [currently supports](./checker/specification/specification.md). - + What Ezno is - A type checker for JavaScript usable through a CLI ([with a LSP also in the works](https://github.com/kaleidawave/ezno/issues/22)) diff --git a/checker/definitions/internal.ts.d2.bin b/checker/definitions/internal.ts.d2.bin new file mode 100644 index 00000000..8b33f2bb Binary files /dev/null and b/checker/definitions/internal.ts.d2.bin differ diff --git a/checker/specification/specification.md b/checker/specification/specification.md index eaa8f808..ac82ab37 100644 --- a/checker/specification/specification.md +++ b/checker/specification/specification.md @@ -718,17 +718,6 @@ doThing(6, 1) satisfies 6; - Expected 2, found 5 -#### Generic condition - -```ts -declare function isNumber(t: T): T extends number ? true : false; - -isNumber(5) satisfies true; -isNumber("5") satisfies number; -``` - -- Expected number, found false - ### Effects > Side effects of functions. Registered internally as `Event`s @@ -1445,7 +1434,7 @@ interface X { > In the future, their definition could be considered and evaluated at runtime ```ts -/hi/ satisfies string; +const regexp = /hi/ satisfies string; ``` - Expected string, found /hi/ @@ -1558,34 +1547,6 @@ d satisfies 1; - Expected 1, found 2 -#### Try-catch and throw - -```ts -try { - throw 2 -} catch (err) { - err satisfies string -} -``` - -- Expected string, found 2 - -#### Throw effects carry through - -```ts -function throwType(a) { - throw a -} - -try { - throwType(3) -} catch (err) { - err satisfies string -} -``` - -- Expected string, found 3 - #### Interface merging ```ts @@ -1916,28 +1877,6 @@ getSecondCharacter("string") satisfies "b"; - Expected boolean, found (s: string) => string | undefined - Expected "b", found "t" -#### Double generics - -> Really want to only have one covariant and one contravariant but want to keep TSC semantics - -```ts -declare function what(a: T, b: T): T; - -what(2, 3) satisfies string; -``` - -- Expected string, found 2 | 3 - -#### More accurate generic - -```ts -declare function unwrap(a: T | { item: T }): T; - -unwrap({ item: 5 }) satisfies string; -``` - -- Expected string, found 5 - #### As casts > Disabled normally, allowed for these tests. Provides TSC compatibility and because narrowing not implemented (including secret feature) @@ -2072,18 +2011,6 @@ function add() { - Argument of type "hi" is not assignable to parameter of type number -#### Across alias - -```ts -type WithLabel = { label: string, item: T }; - -declare function getItem(a: WithLabel): T; - -getItem({ label: "item 1", item: 5 }) satisfies string; -``` - -- Expected string, found 5 - #### Mapped types ```ts diff --git a/checker/specification/to_implement.md b/checker/specification/to_implement.md index 406b7a26..e139019d 100644 --- a/checker/specification/to_implement.md +++ b/checker/specification/to_implement.md @@ -596,3 +596,80 @@ y.pop(); - TODO cannot push - TODO cannot pop + +### Breaking + +> Were working, but a temporary change broke them + +#### Try-catch and throw + +```ts +try { + throw 2 +} catch (err) { + err satisfies string +} +``` + +- Expected string, found 2 + +#### Throw effects carry through + +```ts +function throwType(a) { + throw a +} + +try { + throwType(3) +} catch (err) { + err satisfies string +} +``` + +- Expected string, found 3 + +#### Generic condition + +```ts +declare function isNumber(t: T): T extends number ? true : false; + +isNumber(5) satisfies true; +isNumber("5") satisfies number; +``` + +- Expected number, found false + +#### More accurate generic + +```ts +declare function unwrap(a: T | { item: T }): T; + +unwrap({ item: 5 }) satisfies string; +``` + +- Expected string, found 5 + +#### Across alias + +```ts +type WithLabel = { label: string, item: T }; + +declare function getItem(a: WithLabel): T; + +getItem({ label: "item 1", item: 5 }) satisfies string; +``` + +- Expected string, found 5 + +#### Double generics + +> Really want to only have one covariant and one contravariant but want to keep TSC semantics + +```ts +declare function what(a: T, b: T): T; + +what(2, 3) satisfies string; +``` + +- Expected string, found 2 | 3 diff --git a/checker/src/synthesis/declarations.rs b/checker/src/synthesis/declarations.rs index e3fc4ca5..4c313969 100644 --- a/checker/src/synthesis/declarations.rs +++ b/checker/src/synthesis/declarations.rs @@ -81,9 +81,7 @@ pub(crate) fn synthesise_declaration( } } } - Declaration::DeclareFunction(_) - | Declaration::DeclareVariable(_) - | Declaration::DeclareInterface(_) + Declaration::DeclareVariable(_) | Declaration::Function(_) | Declaration::Enum(_) | Declaration::Interface(_) diff --git a/checker/src/synthesis/definitions.rs b/checker/src/synthesis/definitions.rs index e544dacf..1aba60fa 100644 --- a/checker/src/synthesis/definitions.rs +++ b/checker/src/synthesis/definitions.rs @@ -1,12 +1,12 @@ -use parser::{ASTNode, Declaration, Statement, StatementOrDeclaration}; +use parser::{ + ASTNode, Declaration, ExpressionOrStatementPosition, Statement, StatementOrDeclaration, +}; use source_map::SourceId; use crate::{ context::{Names, RootContext, VariableRegisterArguments}, diagnostics::TypeCheckWarning, - synthesis::{ - functions::synthesise_function_annotation, type_annotations::synthesise_type_annotation, - }, + synthesis::type_annotations::synthesise_type_annotation, Environment, LocalInformation, TypeId, }; @@ -20,10 +20,7 @@ pub(super) fn type_definition_file( ) -> (Names, LocalInformation) { use std::collections::HashMap; - use parser::{ - declarations::{DeclareVariableDeclaration, TypeAlias}, - TypeDeclaration, - }; + use parser::declarations::{DeclareVariableDeclaration, TypeAlias}; let mut idx_to_types = HashMap::new(); @@ -35,7 +32,7 @@ pub(super) fn type_definition_file( match statement { StatementOrDeclaration::Declaration(Declaration::Interface(interface)) => { let ty = environment.register_interface( - &interface.on.name, + interface.on.name.as_option_str().unwrap_or_default(), interface.on.is_nominal, interface.on.type_parameters.as_deref(), interface.on.extends.as_deref(), @@ -46,10 +43,10 @@ pub(super) fn type_definition_file( } StatementOrDeclaration::Declaration(Declaration::TypeAlias(alias)) => { environment.new_alias( - &alias.type_name.name, - alias.type_name.type_parameters.as_deref(), - &alias.type_expression, - *alias.get_position(), + &alias.name.identifier.as_option_str().map_or_else(String::new, str::to_owned), + alias.parameters.as_deref(), + &alias.references, + alias.get_position(), checking_data, ); } @@ -60,10 +57,6 @@ pub(super) fn type_definition_file( for declaration in definition.items { // TODO more match declaration { - StatementOrDeclaration::Declaration(Declaration::DeclareFunction(func)) => { - // TODO abstract - synthesise_declare_function(&func, source, &mut environment, checking_data); - } StatementOrDeclaration::Declaration(Declaration::DeclareVariable( DeclareVariableDeclaration { keyword: _, declarations, position: _, decorators: _ }, )) => { @@ -98,39 +91,8 @@ pub(super) fn type_definition_file( checking_data, ); } - StatementOrDeclaration::Declaration(Declaration::TypeAlias(TypeAlias { - type_name, - type_expression: _, - .. - })) => { - let TypeDeclaration { type_parameters, .. } = &type_name; - - // To remove when implementing - #[allow(clippy::redundant_pattern_matching)] - if let Some(_) = type_parameters { - todo!() - // let ty = if let Some(type_parameters) = type_parameters { - // let mut root = env.new_lexical_root(); - // let type_parameters = generic_type_parameters_from_generic_type_constraints( - // type_parameters, - // &mut env, - // error_handler, - // type_mappings, - // ); - // let borrow = type_parameters.0.borrow(); - // for parameter in borrow.iter().cloned() { - // env.declare_generic_type_parameter(parameter); - // } - // env.get_type(&type_expression, error_handler, type_mappings).unwrap() - // } else { - // env.get_type(&type_expression, error_handler, type_mappings).unwrap() - // }; - // todo!("This should have two passes with a empty type"); - } else { - // todo!("Modify alias") - // let ty = env.get_type_handle_errors(&type_expression, checking_data); - // env.register_type(ty); - } + StatementOrDeclaration::Declaration(Declaration::TypeAlias(TypeAlias { .. })) => { + todo!() } StatementOrDeclaration::Statement(Statement::Comment(..) | Statement::Empty(..)) => {} item => checking_data.diagnostics_container.add_warning( @@ -145,49 +107,7 @@ pub(super) fn type_definition_file( (Names { variables, named_types, variable_names }, info) } -pub(super) fn synthesise_declare_function( - function: &parser::ast::DeclareFunctionDeclaration, - source: SourceId, - environment: &mut Environment, - checking_data: &mut crate::CheckingData, -) { - let declared_at = function.get_position().with_source(source); - let base = synthesise_function_annotation( - &function.type_parameters, - &function.parameters, - function.return_type.as_ref(), - environment, - checking_data, - function.performs.as_ref().into(), - &declared_at, - crate::features::functions::FunctionBehavior::ArrowFunction { is_async: false }, - None, - ); - - let base = checking_data.types.new_function_type_annotation( - base.type_parameters, - base.parameters, - base.return_type, - // TODO - &declared_at, - base.effects, - base.constant_function, - ); - - // TODO not sure - let open = checking_data.types.new_open_type(base); - - let _context = decorators_to_context(&function.decorators); - - environment.register_variable_handle_error( - function.name.as_str(), - VariableRegisterArguments { constant: true, space: None, initial_value: Some(open) }, - function.get_position().with_source(source), - &mut checking_data.diagnostics_container, - ); -} - -pub(crate) fn decorators_to_context(decorators: &[parser::Decorator]) -> Option { +pub(crate) fn _decorators_to_context(decorators: &[parser::Decorator]) -> Option { decorators.iter().find_map(|dec| { if dec.name.first() == Some(&"server".to_owned()) { Some("server".to_owned()) diff --git a/checker/src/synthesis/expressions.rs b/checker/src/synthesis/expressions.rs index 450beb25..d8609aee 100644 --- a/checker/src/synthesis/expressions.rs +++ b/checker/src/synthesis/expressions.rs @@ -224,7 +224,7 @@ pub(super) fn synthesise_expression( _ => unreachable!(), }; return evaluate_logical_operation_with_expression( - (lhs_ty, *lhs.get_position()), + (lhs_ty, lhs.get_position()), operator, &**rhs, checking_data, @@ -663,7 +663,7 @@ pub(super) fn synthesise_expression( Instance::RValue(result) } Expression::ConditionalTernary { condition, truthy_result, falsy_result, .. } => { - let condition_pos = *condition.get_position(); + let condition_pos = condition.get_position(); let condition = synthesise_expression(condition, environment, checking_data, TypeId::ANY_TYPE); @@ -1002,7 +1002,7 @@ pub(super) fn synthesise_object_literal( Some(member_position), ); } - ObjectLiteralMember::Property(key, expression, _) => { + ObjectLiteralMember::Property { key, value, assignment: _, position: _ } => { let key = parser_property_key_to_checker_property_key( key.get_ast_ref(), environment, @@ -1021,12 +1021,8 @@ pub(super) fn synthesise_object_literal( .and_then(|l| if let Logical::Pure(l) = l { Some(l.as_get_type()) } else { None }) .unwrap_or(TypeId::ANY_TYPE); - let value = synthesise_expression( - expression, - environment, - checking_data, - property_expecting, - ); + let value = + synthesise_expression(value, environment, checking_data, property_expecting); let value = crate::types::properties::PropertyValue::Value(value); object_builder.append( diff --git a/checker/src/synthesis/functions.rs b/checker/src/synthesis/functions.rs index 32def3e4..2736b1c6 100644 --- a/checker/src/synthesis/functions.rs +++ b/checker/src/synthesis/functions.rs @@ -36,7 +36,7 @@ where U::Body: SynthesisableFunctionBody, { fn get_position(&self) -> Span { - *ASTNode::get_position(self) + ASTNode::get_position(self) } fn get_name(&self) -> Option<&str> { @@ -484,9 +484,7 @@ fn param_name_to_string(param: &VariableField) -> String { match item.get_ast_ref() { parser::ArrayDestructuringField::Spread(name, _) => { buf.push_str("..."); - if let VariableIdentifier::Standard(name, ..) = name { - buf.push_str(name); - } + buf.push_str(¶m_name_to_string(name)); } parser::ArrayDestructuringField::Name(name, _) => { buf.push_str(¶m_name_to_string(name)); diff --git a/checker/src/synthesis/hoisting.rs b/checker/src/synthesis/hoisting.rs index cc99dc39..62779c40 100644 --- a/checker/src/synthesis/hoisting.rs +++ b/checker/src/synthesis/hoisting.rs @@ -13,7 +13,6 @@ use crate::{ modules::{import_items, ImportKind, NamePair}, variables::VariableMutability, }, - synthesis::definitions::synthesise_declare_function, CheckingData, ReadFromFS, TypeId, }; @@ -35,7 +34,6 @@ pub(crate) fn hoist_statements( if let StatementOrDeclaration::Declaration(declaration) = item { match declaration { parser::Declaration::DeclareVariable(_) - | parser::Declaration::DeclareFunction(_) | parser::Declaration::Class(_) | parser::Declaration::Variable(_) | parser::Declaration::Function(_) => {} @@ -47,21 +45,9 @@ pub(crate) fn hoist_statements( "namespace", ns.position.with_source(environment.get_source()), ), - parser::Declaration::DeclareInterface(interface) => { - // TODO any difference bc `declare`? - let ty = environment.register_interface( - &interface.name, - interface.is_nominal, - interface.type_parameters.as_deref(), - interface.extends.as_deref(), - interface.position.with_source(environment.get_source()), - checking_data, - ); - idx_to_types.insert(interface.position.start, ty); - } parser::Declaration::Interface(interface) => { let ty = environment.register_interface( - &interface.on.name, + interface.on.name.identifier.as_option_str().unwrap_or_default(), interface.on.is_nominal, interface.on.type_parameters.as_deref(), interface.on.extends.as_deref(), @@ -72,10 +58,10 @@ pub(crate) fn hoist_statements( } parser::Declaration::TypeAlias(alias) => { environment.new_alias( - &alias.type_name.name, - alias.type_name.type_parameters.as_deref(), - &alias.type_expression, - *alias.get_position(), + alias.name.as_option_str().unwrap_or_default(), + alias.parameters.as_deref(), + &alias.references, + alias.get_position(), checking_data, ); } @@ -158,19 +144,23 @@ pub(crate) fn hoist_statements( } Exportable::TypeAlias(alias) => { let export = environment.new_alias::<_, EznoParser>( - &alias.type_name.name, - alias.type_name.type_parameters.as_deref(), - &alias.type_expression, - *alias.get_position(), + alias.name.as_option_str().unwrap_or_default(), + alias.parameters.as_deref(), + &alias.references, + alias.get_position(), checking_data, ); if let crate::Scope::Module { ref mut exported, .. } = environment.context_type.scope { - exported - .named_types - .push((alias.type_name.name.clone(), export)); + exported.named_types.push(( + alias + .name + .as_option_str() + .map_or_else(String::new, str::to_owned), + export, + )); } } // Other exported things are skipped @@ -232,12 +222,6 @@ pub(crate) fn hoist_statements( ); } } - parser::Declaration::DeclareFunction(function) => synthesise_declare_function( - function, - environment.get_source(), - environment, - checking_data, - ), parser::Declaration::Enum(r#enum) => { checking_data.raise_unimplemented_error( "enum", @@ -344,7 +328,6 @@ pub(crate) fn hoist_statements( }, parser::Declaration::Class(_) | parser::Declaration::TypeAlias(_) - | parser::Declaration::DeclareInterface(_) | parser::Declaration::Import(_) => {} }, StatementOrDeclaration::Marker(_, _) => {} diff --git a/checker/src/synthesis/interfaces.rs b/checker/src/synthesis/interfaces.rs index bec31131..07e44092 100644 --- a/checker/src/synthesis/interfaces.rs +++ b/checker/src/synthesis/interfaces.rs @@ -145,7 +145,6 @@ pub(super) fn synthesise_signatures { // Fix for performing const annotations. TODO want to do better @@ -175,7 +174,7 @@ pub(super) fn synthesise_signatures checking_data.raise_unimplemented_error( "interface constructor", position.with_source(environment.get_source()), diff --git a/checker/src/synthesis/mod.rs b/checker/src/synthesis/mod.rs index 1f7ef659..ccae6a5e 100644 --- a/checker/src/synthesis/mod.rs +++ b/checker/src/synthesis/mod.rs @@ -107,7 +107,7 @@ impl crate::ASTImplementation for EznoParser { } fn expression_position<'_a>(expression: &'_a Self::Expression<'_a>) -> source_map::Span { - *ASTNode::get_position(expression) + ASTNode::get_position(expression) } fn type_parameter_name<'_a>(parameter: &'_a Self::TypeParameter<'_a>) -> &'_a str { @@ -243,20 +243,6 @@ impl crate::GenericTypeParameter for parser::TypeParameter { } } -impl<'a> From> for Performs<'a> { - fn from(value: Option<&'a parser::types::AnnotationPerforms>) -> Self { - match value { - Some(parser::types::AnnotationPerforms::PerformsConst { identifier }) => { - Performs::Const(identifier.clone()) - } - Some(parser::types::AnnotationPerforms::PerformsStatements { body: statements }) => { - Performs::Block(statements) - } - None => Performs::None, - } - } -} - /// For the REPL in Ezno's CLI pub mod interactive { use std::{collections::HashSet, mem, path::PathBuf}; diff --git a/checker/src/synthesis/statements.rs b/checker/src/synthesis/statements.rs index d57436f5..d8c761ac 100644 --- a/checker/src/synthesis/statements.rs +++ b/checker/src/synthesis/statements.rs @@ -54,7 +54,7 @@ pub(super) fn synthesise_statement( environment: &mut Environment, checking_data: &mut CheckingData, ) { - let condition_pos = *current.0.get_position(); + let condition_pos = current.0.get_position(); let condition = synthesise_multiple_expression( current.0, environment, diff --git a/checker/src/synthesis/variables.rs b/checker/src/synthesis/variables.rs index 45201fbd..80cfb4d2 100644 --- a/checker/src/synthesis/variables.rs +++ b/checker/src/synthesis/variables.rs @@ -58,7 +58,7 @@ pub(crate) fn register_variable( for (idx, field) in items.iter().enumerate() { match field.get_ast_ref() { ArrayDestructuringField::Spread(variable, _pos) => { - register_variable_identifier( + register_variable( variable, environment, checking_data, @@ -78,7 +78,7 @@ pub(crate) fn register_variable( &key, environment, checking_data, - *name.get_position(), + name.get_position(), ); register_variable(name, environment, checking_data, argument); } @@ -100,7 +100,7 @@ pub(crate) fn register_variable( &key, environment, checking_data, - *variable.get_position(), + variable.get_position(), ); register_variable_identifier( variable, @@ -170,7 +170,9 @@ pub(super) fn synthesise_variable_declaration_item< .get(&(environment.get_source(), get_position.start)) .map(|(ty, pos)| (*ty, *pos)); - let value_ty = if let Some(value) = U::as_option_expr_ref(&variable_declaration.expression) { + let value_ty = if let Some(value) = + U::as_option_expression_ref(&variable_declaration.expression) + { let expecting = var_ty_and_pos.as_ref().map_or(TypeId::ANY_TYPE, |(var_ty, _)| *var_ty); let value_ty = @@ -280,7 +282,7 @@ fn assign_to_fields( &key_ty, &mut checking_data.types, None, - *name.get_position(), + name.get_position(), &checking_data.options, ); let value = match property { diff --git a/parser/README.md b/parser/README.md index 5ef878fa..891b874a 100644 --- a/parser/README.md +++ b/parser/README.md @@ -6,7 +6,24 @@ Contains "string to AST" parser, AST definitions, AST back to text/string form m [![crates.io badge](https://img.shields.io/crates/v/ezno-parser?style=flat-square)](https://crates.io/crates/ezno-parser) [![docs.rs badge](https://img.shields.io/docsrs/ezno-parser?style=flat-square)](https://docs.rs/ezno-parser/latest) -Also checkout other parsers such as [swc](https://github.com/swc-project/swc), [rome](https://github.com/rome/tools), [oxc](https://github.com/web-infra-dev/oxc) and [boa](https://github.com/boa-dev/boa). +```rs +use ezno_parser::{ASTNode, Expression}; + +fn main() { + let expressions = [ + "4 + 2 * 5", + "4 * 2 + 5", + "4 * 2 * 5", + "console.log(4 * 2, t ? true : `Hi`) == 2 && 4 == 2", + ]; + for expression in expressions { + let expression = Expression::from_string(expression.to_owned(), Default::default()); + println!("{expression:#?}"); + } +} +``` + +> Also checkout other parsers such as [boa](https://github.com/boa-dev/boa), [biome](https://github.com/biomejs/biome), [oxc](https://github.com/oxc-project/oxc) and [swc](https://github.com/swc-project/swc). ## Goals diff --git a/parser/examples/code_blocks_to_script.rs b/parser/examples/code_blocks_to_script.rs index be21d5a0..052dfb0f 100644 --- a/parser/examples/code_blocks_to_script.rs +++ b/parser/examples/code_blocks_to_script.rs @@ -1,8 +1,10 @@ use std::{collections::HashSet, io::Write}; use ezno_parser::{ + ast::{InterfaceDeclaration, TypeAlias}, visiting::{VisitOptions, Visitors}, - ASTNode, Declaration, Module, StatementOrDeclaration, + ASTNode, Declaration, Decorated, Module, StatementOrDeclaration, StatementPosition, + VariableIdentifier, }; fn main() -> Result<(), Box> { @@ -64,11 +66,24 @@ fn main() -> Result<(), Box> { // TODO quick fix to also register interface and type alias names to prevent conflicts for item in module.items { match item { - StatementOrDeclaration::Declaration(Declaration::TypeAlias(t)) => { - names.insert(t.type_name.name.clone()); + StatementOrDeclaration::Declaration(Declaration::TypeAlias(TypeAlias { + name: StatementPosition { identifier: VariableIdentifier::Standard(s, _), .. }, + .. + })) => { + names.insert(s.clone()); } - StatementOrDeclaration::Declaration(Declaration::Interface(i)) => { - names.insert(i.on.name.clone()); + StatementOrDeclaration::Declaration(Declaration::Interface(Decorated { + on: + InterfaceDeclaration { + name: + StatementPosition { + identifier: VariableIdentifier::Standard(s, _), .. + }, + .. + }, + .. + })) => { + names.insert(s.clone()); } _ => {} } @@ -85,7 +100,7 @@ fn main() -> Result<(), Box> { } } - eprintln!("{:?} blocks", final_blocks.len()); + // eprintln!("Generated {:?} blocks", final_blocks.len()); if let Some(out) = out { let mut out = std::fs::File::create(out).expect("Cannot open file"); diff --git a/parser/src/block.rs b/parser/src/block.rs index a7b7b50f..3e80f4b1 100644 --- a/parser/src/block.rs +++ b/parser/src/block.rs @@ -49,11 +49,11 @@ impl StatementOrDeclaration { } impl ASTNode for StatementOrDeclaration { - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { StatementOrDeclaration::Statement(item) => item.get_position(), StatementOrDeclaration::Declaration(item) => item.get_position(), - StatementOrDeclaration::Marker(_, pos) => pos, + StatementOrDeclaration::Marker(_, pos) => *pos, } } @@ -180,8 +180,8 @@ impl ASTNode for Block { } } - fn get_position(&self) -> &Span { - &self.1 + fn get_position(&self) -> Span { + self.1 } } @@ -304,7 +304,7 @@ impl From for BlockOrSingleStatement { } impl ASTNode for BlockOrSingleStatement { - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { BlockOrSingleStatement::Braced(blk) => blk.get_position(), BlockOrSingleStatement::SingleStatement(stmt) => stmt.get_position(), @@ -395,6 +395,20 @@ pub fn statements_and_declarations_to_string( } } + if let (false, StatementOrDeclaration::Declaration(dec)) = + (options.include_type_annotations, item) + { + match dec { + Declaration::Function(item) if item.on.name.declare => { + continue; + } + Declaration::Class(item) if item.on.name.declare => { + continue; + } + _ => {} + } + } + options.add_indent(local.depth, buf); item.to_string_from_buffer(buf, options, local); if (!at_end || options.trailing_semicolon) && item.requires_semi_colon() { diff --git a/parser/src/comments.rs b/parser/src/comments.rs index 31ecb678..609cdd27 100644 --- a/parser/src/comments.rs +++ b/parser/src/comments.rs @@ -122,10 +122,10 @@ impl ASTNode for WithComment { } } - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { Self::None(ast) => ast.get_position(), - Self::PostfixComment(_, _, position) | Self::PrefixComment(_, _, position) => position, + Self::PostfixComment(_, _, position) | Self::PrefixComment(_, _, position) => *position, } } diff --git a/parser/src/declarations/classes/class_member.rs b/parser/src/declarations/classes/class_member.rs index 1b4df345..700486e5 100644 --- a/parser/src/declarations/classes/class_member.rs +++ b/parser/src/declarations/classes/class_member.rs @@ -8,6 +8,7 @@ use crate::{ ThisParameter, }, property_key::PublicOrPrivate, + tokens::token_as_identifier, visiting::Visitable, ASTNode, Block, Expression, FunctionBase, ParseOptions, ParseResult, PropertyKey, TSXKeyword, TSXToken, TypeAnnotation, WithComment, @@ -26,6 +27,14 @@ pub enum ClassMember { Method(IsStatic, ClassFunction), Property(IsStatic, ClassProperty), StaticBlock(Block), + /// Really for interfaces but here + Indexer { + name: String, + indexer_type: TypeAnnotation, + return_type: TypeAnnotation, + is_readonly: bool, + position: Span, + }, Comment(String, bool, Span), } @@ -48,13 +57,13 @@ pub struct ClassProperty { } impl ASTNode for ClassMember { - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { Self::Constructor(cst) => cst.get_position(), Self::Method(_, mtd) => mtd.get_position(), - Self::Property(_, prop) => &prop.position, + Self::Property(_, prop) => prop.position, Self::StaticBlock(blk) => blk.get_position(), - Self::Comment(.., pos) => pos, + Self::Indexer { position: pos, .. } | Self::Comment(.., pos) => *pos, } } @@ -85,13 +94,37 @@ impl ASTNode for ClassMember { let readonly_position = state.optionally_expect_keyword(reader, TSXKeyword::Readonly); + if let Some(Token(TSXToken::OpenBracket, _)) = reader.peek() { + if let Some(Token(TSXToken::Colon, _)) = reader.peek_n(2) { + let Token(_, start) = reader.next().unwrap(); + let (name, _) = token_as_identifier( + reader.next().ok_or_else(parse_lexing_error)?, + "class indexer", + )?; + reader.expect_next(TSXToken::Colon)?; + let indexer_type = TypeAnnotation::from_reader(reader, state, options)?; + reader.expect_next(TSXToken::CloseBracket)?; + reader.expect_next(TSXToken::Colon)?; + let return_type = TypeAnnotation::from_reader(reader, state, options)?; + return Ok(ClassMember::Indexer { + name, + is_readonly: readonly_position.is_some(), + indexer_type, + position: start.union(return_type.get_position()), + return_type, + }); + } + } + // TODO not great let start = reader.peek().unwrap().1; let (header, key) = crate::functions::get_method_name(reader, state, options)?; match reader.peek() { - Some(Token(TSXToken::OpenParentheses, _)) if readonly_position.is_none() => { + Some(Token(TSXToken::OpenParentheses | TSXToken::OpenChevron, _)) + if readonly_position.is_none() => + { let function = ClassFunction::from_reader_with_config( reader, state, @@ -124,7 +157,7 @@ impl ASTNode for ClassMember { is_static, ClassProperty { is_readonly: readonly_position.is_some(), - position: *key.get_position(), + position: key.get_position(), key, type_annotation: member_type, value: member_expression.map(Box::new), @@ -190,6 +223,16 @@ impl ASTNode for ClassMember { } } } + Self::Indexer { name, indexer_type, return_type, is_readonly: _, position: _ } => { + if options.include_type_annotations { + buf.push('['); + buf.push_str(name); + buf.push_str(": "); + indexer_type.to_string_from_buffer(buf, options, local); + buf.push_str("]: "); + return_type.to_string_from_buffer(buf, options, local); + } + } } } } @@ -219,7 +262,6 @@ impl FunctionBased for ClassFunctionBase { type ParameterVisibility = (); type Body = FunctionBody; - #[cfg(feature = "full-typescript")] fn has_body(body: &Self::Body) -> bool { body.0.is_some() } @@ -288,7 +330,6 @@ impl FunctionBased for ClassConstructorBase { // ChainVariable::UnderClassConstructor(this.body.1) // } - #[cfg(feature = "full-typescript")] fn has_body(body: &Self::Body) -> bool { body.0.is_some() } diff --git a/parser/src/declarations/classes/mod.rs b/parser/src/declarations/classes/mod.rs index 8ef6757e..3b0b8839 100644 --- a/parser/src/declarations/classes/mod.rs +++ b/parser/src/declarations/classes/mod.rs @@ -2,7 +2,10 @@ mod class_member; use std::fmt::Debug; -use crate::{derive_ASTNode, throw_unexpected_token_with_token, to_string_bracketed, Expression}; +use crate::{ + derive_ASTNode, throw_unexpected_token_with_token, to_string_bracketed, Expression, + ParseErrors, VariableIdentifier, +}; pub use class_member::*; use iterator_endiate::EndiateIteratorExt; @@ -49,8 +52,8 @@ impl &Span { - &self.position + fn get_position(&self) -> Span { + self.position } } @@ -62,6 +65,14 @@ impl ClassDeclaration { start: TokenStart, ) -> ParseResult { let name = U::from_reader(reader, state, options)?; + + if let Some(VariableIdentifier::Standard(name, pos)) = name.as_option_variable_identifier() + { + if let "extends" = name.as_str() { + return Err(crate::ParseError::new(ParseErrors::ExpectedIdentifier, *pos)); + } + } + let type_parameters = reader .conditional_next(|token| *token == TSXToken::OpenChevron) .is_some() diff --git a/parser/src/declarations/export.rs b/parser/src/declarations/export.rs index 0efb81ad..de0026a3 100644 --- a/parser/src/declarations/export.rs +++ b/parser/src/declarations/export.rs @@ -56,8 +56,8 @@ pub enum Exportable { } impl ASTNode for ExportDeclaration { - fn get_position(&self) -> &Span { - self.get() + fn get_position(&self) -> Span { + *self.get() } fn from_reader( @@ -103,7 +103,7 @@ impl ASTNode for ExportDeclaration { .transpose()?; let position = start.union( - return_type.as_ref().map_or(¶meters.position, ASTNode::get_position), + return_type.as_ref().map_or(parameters.position, ASTNode::get_position), ); Ok(ExportDeclaration::DefaultFunction { @@ -408,8 +408,8 @@ pub enum ExportPart { impl ListItem for ExportPart {} impl ASTNode for ExportPart { - fn get_position(&self) -> &Span { - GetFieldByType::get(self) + fn get_position(&self) -> Span { + *GetFieldByType::get(self) } // TODO also single line comments here diff --git a/parser/src/declarations/import.rs b/parser/src/declarations/import.rs index 66d113cb..5d248e3d 100644 --- a/parser/src/declarations/import.rs +++ b/parser/src/declarations/import.rs @@ -178,8 +178,8 @@ impl ASTNode for ImportDeclaration { self.from.to_string_from_buffer(buf); } - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } } @@ -325,8 +325,8 @@ pub enum ImportPart { impl ListItem for ImportPart {} impl ASTNode for ImportPart { - fn get_position(&self) -> &Span { - GetFieldByType::get(self) + fn get_position(&self) -> Span { + *GetFieldByType::get(self) } // TODO also single line comments here diff --git a/parser/src/declarations/mod.rs b/parser/src/declarations/mod.rs index ca5c19a6..34120f42 100644 --- a/parser/src/declarations/mod.rs +++ b/parser/src/declarations/mod.rs @@ -35,7 +35,7 @@ pub mod import; pub mod variable; pub use super::types::{ - declares::*, + declare_variable::*, enum_declaration::{EnumDeclaration, EnumMember}, interface::InterfaceDeclaration, type_alias::TypeAlias, @@ -58,11 +58,8 @@ pub enum Declaration { TypeAlias(TypeAlias), // Special TS only DeclareVariable(DeclareVariableDeclaration), - DeclareFunction(DeclareFunctionDeclaration), #[cfg(feature = "full-typescript")] Namespace(crate::types::namespace::Namespace), - #[from_ignore] - DeclareInterface(InterfaceDeclaration), // Top level only Import(ImportDeclaration), Export(Decorated), @@ -236,15 +233,6 @@ impl crate::ASTNode for Declaration { TSXToken::Keyword(TSXKeyword::Import) => { ImportDeclaration::from_reader(reader, state, options).map(Into::into) } - #[cfg(feature = "extras")] - TSXToken::Keyword(TSXKeyword::From) => { - ImportDeclaration::reversed_from_reader(reader, state, options).map(Into::into) - } - #[cfg(feature = "full-typescript")] - TSXToken::Keyword(TSXKeyword::Namespace) => { - crate::types::namespace::Namespace::from_reader(reader, state, options) - .map(Into::into) - } TSXToken::Keyword(TSXKeyword::Interface) if options.type_annotations => { InterfaceDeclaration::from_reader(reader, state, options) .map(|on| Declaration::Interface(Decorated::new(decorators, on))) @@ -265,11 +253,25 @@ impl crate::ASTNode for Declaration { ) .map(Into::into) } - TSXToken::Keyword(t) if t.is_in_function_header() => { - DeclareFunctionDeclaration::from_reader_sub_declare_with_decorators( - reader, state, options, decorators, - ) - .map(Into::into) + TSXToken::Keyword(TSXKeyword::Class) => { + let mut class = ClassDeclaration::::from_reader( + reader, state, options, + )?; + class.name.declare = true; + class.position.start = start.0; + Ok(Declaration::Class(Decorated::new(decorators, class))) + } + TSXToken::Keyword(TSXKeyword::Function) => { + let mut function = StatementFunction::from_reader(reader, state, options)?; + function.name.declare = true; + function.position.start = start.0; + Ok(Declaration::Function(Decorated::new(decorators, function))) + } + TSXToken::Keyword(TSXKeyword::Type) => { + let mut alias = TypeAlias::from_reader(reader, state, options)?; + alias.name.declare = true; + alias.position.start = start.0; + Ok(Declaration::TypeAlias(alias)) } _ => throw_unexpected_token_with_token( reader.next().ok_or_else(parse_lexing_error)?, @@ -278,10 +280,21 @@ impl crate::ASTNode for Declaration { TSXToken::Keyword(TSXKeyword::Const), TSXToken::Keyword(TSXKeyword::Var), TSXToken::Keyword(TSXKeyword::Function), + TSXToken::Keyword(TSXKeyword::Class), + TSXToken::Keyword(TSXKeyword::Type), ], ), } } + #[cfg(feature = "extras")] + TSXToken::Keyword(TSXKeyword::From) => { + ImportDeclaration::reversed_from_reader(reader, state, options).map(Into::into) + } + #[cfg(feature = "full-typescript")] + TSXToken::Keyword(TSXKeyword::Namespace) => { + crate::types::namespace::Namespace::from_reader(reader, state, options) + .map(Into::into) + } _ => throw_unexpected_token_with_token( reader.next().ok_or_else(parse_lexing_error)?, &[ @@ -314,24 +327,16 @@ impl crate::ASTNode for Declaration { Declaration::Import(is) => is.to_string_from_buffer(buf, options, local), Declaration::Export(es) => es.to_string_from_buffer(buf, options, local), Declaration::Function(f) => f.to_string_from_buffer(buf, options, local), - // TODO should skip these under no types Declaration::Interface(id) => id.to_string_from_buffer(buf, options, local), Declaration::TypeAlias(ta) => ta.to_string_from_buffer(buf, options, local), Declaration::Enum(r#enum) => r#enum.to_string_from_buffer(buf, options, local), - Declaration::DeclareFunction(dfd) => dfd.to_string_from_buffer(buf, options, local), Declaration::DeclareVariable(dvd) => dvd.to_string_from_buffer(buf, options, local), #[cfg(feature = "full-typescript")] Declaration::Namespace(ns) => ns.to_string_from_buffer(buf, options, local), - Declaration::DeclareInterface(did) => { - if options.include_type_annotations { - buf.push_str("declare "); - did.to_string_from_buffer(buf, options, local); - } - } } } - fn get_position(&self) -> &source_map::Span { - self.get() + fn get_position(&self) -> Span { + *self.get() } } diff --git a/parser/src/declarations/variable.rs b/parser/src/declarations/variable.rs index bfed7993..f3134a91 100644 --- a/parser/src/declarations/variable.rs +++ b/parser/src/declarations/variable.rs @@ -26,9 +26,9 @@ pub trait DeclarationExpression: local: crate::LocalToStringInformation, ); - fn get_decl_position(&self) -> Option<&Span>; + fn get_declaration_position(&self) -> Option; - fn as_option_expr_ref(&self) -> Option<&Expression>; + fn as_option_expression_ref(&self) -> Option<&Expression>; fn as_option_expr_mut(&mut self) -> Option<&mut Expression>; } @@ -66,11 +66,11 @@ impl DeclarationExpression for Option { } } - fn get_decl_position(&self) -> Option<&Span> { + fn get_declaration_position(&self) -> Option { self.as_ref().map(ASTNode::get_position) } - fn as_option_expr_ref(&self) -> Option<&Expression> { + fn as_option_expression_ref(&self) -> Option<&Expression> { self.as_ref() } @@ -105,11 +105,11 @@ impl DeclarationExpression for crate::Expression { ASTNode::to_string_from_buffer(self, buf, options, local); } - fn get_decl_position(&self) -> Option<&Span> { + fn get_declaration_position(&self) -> Option { Some(ASTNode::get_position(self)) } - fn as_option_expr_ref(&self) -> Option<&Expression> { + fn as_option_expression_ref(&self) -> Option<&Expression> { Some(self) } @@ -149,7 +149,7 @@ impl ASTNode for VariableDeclarationItem let expression = TExpr::expression_from_reader(reader, state, options)?; let position = name.get_position().union( expression - .get_decl_position() + .get_declaration_position() .or(type_annotation.as_ref().map(ASTNode::get_position)) .unwrap_or(name.get_position()), ); @@ -173,7 +173,7 @@ impl ASTNode for VariableDeclarationItem let available_space = u32::from(options.max_line_length).checked_sub(buf.characters_on_current_line()); - if let Some(e) = TExpr::as_option_expr_ref(&self.expression) { + if let Some(e) = TExpr::as_option_expression_ref(&self.expression) { let extends_limit = crate::is_node_over_length(e, options, local, available_space); if extends_limit { buf.push_new_line(); @@ -183,8 +183,8 @@ impl ASTNode for VariableDeclarationItem self.expression.expression_to_string_from_buffer(buf, options, local); } - fn get_position(&self) -> &Span { - self.get() + fn get_position(&self) -> Span { + *self.get() } } @@ -259,7 +259,7 @@ impl ASTNode for VariableDeclaration { { return Err(crate::ParseError::new( crate::ParseErrors::DestructuringRequiresValue, - *value.name.get_ast_ref().get_position(), + value.name.get_ast_ref().get_position(), )); } @@ -320,8 +320,8 @@ impl ASTNode for VariableDeclaration { } } - fn get_position(&self) -> &Span { - self.get() + fn get_position(&self) -> Span { + *self.get() } } diff --git a/parser/src/errors.rs b/parser/src/errors.rs index 6d43e96f..45671125 100644 --- a/parser/src/errors.rs +++ b/parser/src/errors.rs @@ -28,6 +28,7 @@ pub enum ParseErrors<'a> { AwaitRequiresForOf, CannotUseLeadingParameterHere, ExpectedIdentifier, + ExpectedNumberLiteral, } #[allow(missing_docs)] @@ -187,6 +188,9 @@ impl<'a> Display for ParseErrors<'a> { ParseErrors::ExpectedIdentifier => { write!(f, "Expected variable identifier") } + ParseErrors::ExpectedNumberLiteral => { + write!(f, "Expected number literal") + } } } } diff --git a/parser/src/expressions/arrow_function.rs b/parser/src/expressions/arrow_function.rs index 1fabcf36..31704385 100644 --- a/parser/src/expressions/arrow_function.rs +++ b/parser/src/expressions/arrow_function.rs @@ -214,7 +214,7 @@ pub enum ExpressionOrBlock { } impl ASTNode for ExpressionOrBlock { - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { ExpressionOrBlock::Expression(expression) => expression.get_position(), ExpressionOrBlock::Block(block) => block.get_position(), diff --git a/parser/src/expressions/assignments.rs b/parser/src/expressions/assignments.rs index 950fc692..f9e0be3c 100644 --- a/parser/src/expressions/assignments.rs +++ b/parser/src/expressions/assignments.rs @@ -1,4 +1,7 @@ -use crate::{derive_ASTNode, PropertyReference, TSXToken}; +use crate::{ + ast::{object_literal::ObjectLiteralMember, FunctionArgument}, + derive_ASTNode, PropertyKey, PropertyReference, TSXToken, +}; use derive_partial_eq_extras::PartialEqExtras; use get_field_by_type::GetFieldByType; use iterator_endiate::EndiateIteratorExt; @@ -33,8 +36,8 @@ pub enum VariableOrPropertyAccess { } impl ASTNode for VariableOrPropertyAccess { - fn get_position(&self) -> &Span { - self.get() + fn get_position(&self) -> Span { + *self.get() } fn from_reader( @@ -122,13 +125,13 @@ impl TryFrom for VariableOrPropertyAccess { } else { Err(ParseError::new( crate::ParseErrors::InvalidLHSAssignment, - *inner.get_position(), + inner.get_position(), )) } } expression => Err(ParseError::new( crate::ParseErrors::InvalidLHSAssignment, - *expression.get_position(), + expression.get_position(), )), } } @@ -170,6 +173,8 @@ impl VariableOrPropertyAccess { } /// TODO visitable is current skipped... +/// +/// Includes [Destructuring assignment](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) #[apply(derive_ASTNode)] #[derive(PartialEqExtras, Debug, Clone, Visitable, derive_enum_from_into::EnumFrom)] #[partial_eq_ignore_types(Span)] @@ -181,10 +186,10 @@ pub enum LHSOfAssignment { impl LHSOfAssignment { #[must_use] - pub fn get_position(&self) -> &Span { + pub fn get_position(&self) -> Span { match self { LHSOfAssignment::ObjectDestructuring(_, pos) - | LHSOfAssignment::ArrayDestructuring(_, pos) => pos, + | LHSOfAssignment::ArrayDestructuring(_, pos) => *pos, LHSOfAssignment::VariableOrPropertyAccess(var_prop_access) => { var_prop_access.get_position() } @@ -217,9 +222,7 @@ impl LHSOfAssignment { member.to_string_from_buffer(buf, options, local); if !at_end { buf.push(','); - if !matches!(member.get_ast_ref(), ArrayDestructuringField::None) { - options.push_gap_optionally(buf); - } + options.push_gap_optionally(buf); } } buf.push(']'); @@ -230,3 +233,149 @@ impl LHSOfAssignment { } } } + +impl TryFrom for LHSOfAssignment { + type Error = ParseError; + + fn try_from(value: Expression) -> Result { + match value { + Expression::ArrayLiteral(inner, position) => { + let mut members = Vec::with_capacity(inner.len()); + for member in inner { + let new_member = match member.0 { + Some(FunctionArgument::Comment { content, is_multiline: _, position }) => { + WithComment::PrefixComment( + content, + ArrayDestructuringField::None, + position, + ) + } + Some(FunctionArgument::Spread(expression, span)) => { + let lhs: LHSOfAssignment = expression.try_into()?; + let lhs: crate::VariableField = lhs_to_variable_field(lhs)?; + WithComment::None(ArrayDestructuringField::Spread(lhs, span)) + } + Some(FunctionArgument::Standard(expression)) => { + WithComment::None(match expression { + Expression::Assignment { lhs, rhs, position: _ } => { + let lhs: crate::VariableField = lhs_to_variable_field(lhs)?; + ArrayDestructuringField::Name(lhs, Some(rhs)) + } + Expression::ArrayLiteral(..) | Expression::ObjectLiteral(..) => { + let lhs: LHSOfAssignment = expression.try_into()?; + let lhs: crate::VariableField = lhs_to_variable_field(lhs)?; + ArrayDestructuringField::Name(lhs, None) + } + Expression::VariableReference(reference, pos) => { + let name = crate::VariableIdentifier::Standard(reference, pos); + ArrayDestructuringField::Name( + crate::VariableField::Name(name), + None, + ) + } + _ => { + return Err(ParseError::new( + crate::ParseErrors::InvalidLHSAssignment, + expression.get_position(), + )) + } + }) + } + None => WithComment::None(ArrayDestructuringField::None), + }; + members.push(new_member); + } + Ok(Self::ArrayDestructuring(members, position)) + } + Expression::ObjectLiteral(inner) => { + let mut members = Vec::with_capacity(inner.members.len()); + for member in inner.members { + let new_member: ObjectDestructuringField = match member { + ObjectLiteralMember::Spread(expression, _) => { + if let Expression::VariableReference(reference, pos) = expression { + ObjectDestructuringField::Spread( + crate::VariableIdentifier::Standard(reference, pos), + pos, + ) + } else { + return Err(ParseError::new( + crate::ParseErrors::InvalidLHSAssignment, + expression.get_position(), + )); + } + } + ObjectLiteralMember::Shorthand(name, pos) => { + ObjectDestructuringField::Name( + crate::VariableIdentifier::Standard(name, pos), + None, + pos, + ) + } + ObjectLiteralMember::Property { assignment, key, position, value } => { + if assignment { + if let PropertyKey::Ident(name, pos, _) = key.get_ast() { + ObjectDestructuringField::Name( + crate::VariableIdentifier::Standard(name, pos), + Some(Box::new(value)), + pos, + ) + } else { + return Err(ParseError::new( + crate::ParseErrors::InvalidLHSAssignment, + position, + )); + } + } else { + let (name, default_value) = + if let Expression::Assignment { lhs, rhs, position: _ } = value + { + let lhs: crate::VariableField = lhs_to_variable_field(lhs)?; + (lhs, Some(rhs)) + } else { + let lhs: LHSOfAssignment = value.try_into()?; + let lhs: crate::VariableField = lhs_to_variable_field(lhs)?; + (lhs, None) + }; + + ObjectDestructuringField::Map { + from: key.get_ast(), + name: WithComment::None(name), + default_value, + position, + } + } + } + ObjectLiteralMember::Method(_) => { + return Err(ParseError::new( + crate::ParseErrors::InvalidLHSAssignment, + inner.position, + )) + } + }; + members.push(WithComment::None(new_member)); + } + Ok(Self::ObjectDestructuring(members, inner.position)) + } + expression => VariableOrPropertyAccess::try_from(expression) + .map(LHSOfAssignment::VariableOrPropertyAccess), + } + } +} + +fn lhs_to_variable_field(lhs: LHSOfAssignment) -> Result { + match lhs { + LHSOfAssignment::VariableOrPropertyAccess(VariableOrPropertyAccess::Variable( + name, + pos, + )) => Ok(crate::VariableField::Name(crate::VariableIdentifier::Standard(name, pos))), + LHSOfAssignment::ArrayDestructuring(fields, pos) => { + Ok(crate::VariableField::Array(fields, pos)) + } + LHSOfAssignment::ObjectDestructuring(fields, pos) => { + Ok(crate::VariableField::Object(fields, pos)) + } + LHSOfAssignment::VariableOrPropertyAccess(a) => { + Err(ParseError::new(crate::ParseErrors::InvalidLHSAssignment, a.get_position())) + } + } +} diff --git a/parser/src/expressions/mod.rs b/parser/src/expressions/mod.rs index eea98da2..b3992185 100644 --- a/parser/src/expressions/mod.rs +++ b/parser/src/expressions/mod.rs @@ -216,8 +216,8 @@ impl ASTNode for Expression { ); } - fn get_position(&self) -> &Span { - get_field_by_type::GetFieldByType::get(self) + fn get_position(&self) -> Span { + *GetFieldByType::get(self) } } @@ -373,131 +373,70 @@ impl Expression { let position = s.union(operand.get_position()); Expression::UnaryOperation { operator, operand: Box::new(operand), position } } - t @ Token(TSXToken::OpenBracket, start) => { - let mut bracket_depth = 1; - // TODO this can be bad for large object literals, needs to exit if expression without = - let after_bracket = reader.scan(|token, _| match token { - TSXToken::OpenBracket => { - bracket_depth += 1; - false - } - TSXToken::CloseBracket => { - bracket_depth -= 1; - bracket_depth == 0 - } - _ => false, - }); - if let Some(Token(token_type, _)) = after_bracket { - if let TSXToken::Assign = token_type { - let (members, end) = - parse_bracketed(reader, state, options, None, TSXToken::CloseBracket)?; - let Token(_assignment, assignment_pos) = reader.next().unwrap(); - let rhs = Expression::from_reader_with_precedence( - reader, - state, - options, - ASSIGNMENT_PRECEDENCE, - Some(assignment_pos), - )?; - let array_position = start.union(end); - Expression::Assignment { - position: array_position.union(rhs.get_position()), - lhs: LHSOfAssignment::ArrayDestructuring(members, array_position), - rhs: Box::new(rhs), - } - } else { - let (items, end) = parse_bracketed::( - reader, - state, - options, - None, - TSXToken::CloseBracket, - )?; + Token(TSXToken::OpenBracket, start) => { + let (items, end) = parse_bracketed::( + reader, + state, + options, + None, + TSXToken::CloseBracket, + )?; - Expression::ArrayLiteral(items, start.union(end)) - } - } else { - return Err(ParseError::new( - crate::ParseErrors::UnmatchedBrackets, - t.get_span(), - )); - } + Expression::ArrayLiteral(items, start.union(end)) } - t @ Token(TSXToken::OpenBrace, start) => { - let mut brace_depth = 1; - let after_brace = reader.scan(|token, _| match token { - TSXToken::OpenBrace => { - brace_depth += 1; - false - } - TSXToken::CloseBrace => { - brace_depth -= 1; - brace_depth == 0 - } - _ => false, - }); - if let Some(Token(token_type, _)) = after_brace { - if let TSXToken::Assign = token_type { - let (members, end) = - parse_bracketed(reader, state, options, None, TSXToken::CloseBrace)?; - let Token(_assignment, assignment_pos) = reader.next().unwrap(); - let rhs = Box::new(Expression::from_reader_with_precedence( - reader, - state, - options, - ASSIGNMENT_PRECEDENCE, - Some(assignment_pos), - )?); - let object_position = start.union(end); - Expression::Assignment { - position: object_position.union(rhs.get_position()), - lhs: LHSOfAssignment::ObjectDestructuring(members, object_position), - rhs, - } - } else { - ObjectLiteral::from_reader_sub_open_curly(reader, state, options, start) - .map(Expression::ObjectLiteral)? - } - } else { - return Err(ParseError::new( - crate::ParseErrors::UnmatchedBrackets, - t.get_span(), - )); - } + Token(TSXToken::OpenBrace, start) => { + ObjectLiteral::from_reader_sub_open_curly(reader, state, options, start) + .map(Expression::ObjectLiteral)? } t @ Token(TSXToken::OpenParentheses, start) => { let mut parentheses_depth = 1; - let next = reader.scan(|token, _| match token { - TSXToken::OpenParentheses => { - parentheses_depth += 1; - false - } - TSXToken::CloseParentheses => { - parentheses_depth -= 1; - parentheses_depth == 0 - } - _ => false, - }); - if let Some(Token(token_type, _)) = next { - if let TSXToken::Arrow = token_type { - let arrow_function = ArrowFunction::from_reader_sub_open_paren( - reader, state, options, false, start, - )?; - Expression::ArrowFunction(arrow_function) + let is_arrow_function = if let Some(Token( + TSXToken::Keyword(..) + | TSXToken::Identifier(..) + | TSXToken::OpenBrace + | TSXToken::OpenBracket + | TSXToken::CloseParentheses + | TSXToken::Spread, + _, + )) = reader.peek() + { + let next = reader.scan(|token, _| match token { + TSXToken::OpenParentheses => { + parentheses_depth += 1; + false + } + TSXToken::CloseParentheses => { + parentheses_depth -= 1; + parentheses_depth == 0 + } + _ => false, + }); + + if let Some(Token(token_type, _)) = next { + matches!(token_type, TSXToken::Arrow) } else { - let parenthesize_expression = - MultipleExpression::from_reader(reader, state, options)?; - let end = reader.expect_next_get_end(TSXToken::CloseParentheses)?; - Expression::ParenthesizedExpression( - Box::new(parenthesize_expression), - start.union(end), - ) + return Err(ParseError::new( + crate::ParseErrors::UnmatchedBrackets, + t.get_span(), + )); } } else { - return Err(ParseError::new( - crate::ParseErrors::UnmatchedBrackets, - t.get_span(), - )); + false + }; + + if is_arrow_function { + let arrow_function = ArrowFunction::from_reader_sub_open_paren( + reader, state, options, false, start, + )?; + Expression::ArrowFunction(arrow_function) + } else { + let parenthesize_expression = + MultipleExpression::from_reader(reader, state, options)?; + let end = reader.expect_next_get_end(TSXToken::CloseParentheses)?; + Expression::ParenthesizedExpression( + Box::new(parenthesize_expression), + start.union(end), + ) } } t @ Token(TSXToken::Keyword(TSXKeyword::New), start) => { @@ -909,7 +848,7 @@ impl Expression { return Ok(top); } reader.next(); - let condition_position = *top.get_position(); + let condition_position = top.get_position(); let condition = Box::new(top); let lhs = Box::new(Self::from_reader(reader, state, options)?); reader.expect_next(TSXToken::Colon)?; @@ -1036,11 +975,9 @@ impl Expression { parent_precedence, Some(assignment_pos), )?; - top = Expression::Assignment { - position: top.get_position().union(new_rhs.get_position()), - lhs: LHSOfAssignment::VariableOrPropertyAccess(top.try_into()?), - rhs: Box::new(new_rhs), - }; + let position = top.get_position().union(new_rhs.get_position()); + let lhs = top.try_into()?; + top = Expression::Assignment { position, lhs, rhs: Box::new(new_rhs) }; } TSXToken::MultiLineComment(_) | TSXToken::Comment(_) => { let (content, is_multiline, position) = @@ -1515,10 +1452,19 @@ impl Expression { operand.to_string_using_precedence(buf, options, local, right_argument); } Self::Assignment { lhs, rhs, .. } => { + let require_parenthesis = + matches!(lhs, LHSOfAssignment::ObjectDestructuring(..)) && local2.on_left; + + if require_parenthesis { + buf.push('('); + } lhs.to_string_from_buffer(buf, options, local); buf.push_str(if options.pretty { " = " } else { "=" }); let right_argument = local2.with_precedence(self_precedence).on_right(); rhs.to_string_using_precedence(buf, options, local, right_argument); + if require_parenthesis { + buf.push(')'); + } } Self::BinaryAssignmentOperation { lhs, operator, rhs, .. } => { lhs.to_string_from_buffer(buf, options, local); @@ -1785,6 +1731,7 @@ fn function_header_ish( #[derive(Clone, Copy)] pub(crate) struct ExpressionToStringArgument { + /// On left of statement pub on_left: bool, pub parent_precedence: u8, } @@ -1878,9 +1825,9 @@ impl ASTNode for MultipleExpression { } } - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { - MultipleExpression::Multiple { position, .. } => position, + MultipleExpression::Multiple { position, .. } => *position, MultipleExpression::Single(expr) => expr.get_position(), } } @@ -2147,10 +2094,10 @@ impl ASTNode for FunctionArgument { } } - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { FunctionArgument::Comment { position, .. } | FunctionArgument::Spread(_, position) => { - position + *position } FunctionArgument::Standard(expr) => expr.get_position(), } @@ -2168,8 +2115,8 @@ impl From for FunctionArgument { pub struct ArrayElement(pub Option); impl ASTNode for ArrayElement { - fn get_position(&self) -> &Span { - self.0.as_ref().map_or(&Span::NULL, |s| s.get_position()) + fn get_position(&self) -> Span { + self.0.as_ref().map_or(Span::NULL, ASTNode::get_position) } fn from_reader( @@ -2201,7 +2148,7 @@ impl Expression { /// IIFE = immediate invoked function execution #[must_use] pub fn build_iife(block: Block) -> Self { - let position = *block.get_position(); + let position = block.get_position(); Expression::FunctionCall { function: Expression::ParenthesizedExpression( Box::new( diff --git a/parser/src/expressions/object_literal.rs b/parser/src/expressions/object_literal.rs index 6b8a12dd..32a2dd9f 100644 --- a/parser/src/expressions/object_literal.rs +++ b/parser/src/expressions/object_literal.rs @@ -29,7 +29,13 @@ pub struct ObjectLiteral { pub enum ObjectLiteralMember { Spread(Expression, Span), Shorthand(String, Span), - Property(WithComment>, Expression, Span), + Property { + key: WithComment>, + /// Makes object destructuring syntax a subset of object literal syntax + assignment: bool, + value: Expression, + position: Span, + }, Method(ObjectLiteralMethod), } @@ -43,7 +49,7 @@ impl crate::Visitable for ObjectLiteralMember { ) { match self { ObjectLiteralMember::Shorthand(_, _) - | ObjectLiteralMember::Property(_, _, _) + | ObjectLiteralMember::Property { .. } | ObjectLiteralMember::Spread(_, _) => {} ObjectLiteralMember::Method(method) => method.visit(visitors, data, options, chain), } @@ -57,7 +63,7 @@ impl crate::Visitable for ObjectLiteralMember { chain: &mut temporary_annex::Annex, ) { match self { - ObjectLiteralMember::Property(_, _, _) + ObjectLiteralMember::Property { .. } | ObjectLiteralMember::Spread(_, _) | ObjectLiteralMember::Shorthand(_, _) => {} ObjectLiteralMember::Method(method) => method.visit_mut(visitors, data, options, chain), @@ -143,8 +149,8 @@ impl FunctionBased for ObjectLiteralMethodBase { impl Eq for ObjectLiteralMember {} impl ASTNode for ObjectLiteral { - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn from_reader( @@ -251,10 +257,16 @@ impl ASTNode for ObjectLiteralMember { throw_unexpected_token_with_token(token, &[TSXToken::Colon]) } } else { - reader.expect_next(TSXToken::Colon)?; - let expression = Expression::from_reader(reader, state, options)?; - let position = key.get_position().union(expression.get_position()); - Ok(Self::Property(key, expression, position)) + let token = reader.next().ok_or_else(parse_lexing_error)?; + let assignment = match token.0 { + TSXToken::Colon => false, + TSXToken::Assign => true, + _ => return throw_unexpected_token_with_token(token, &[TSXToken::Colon]), + }; + // let assignment = if let + let value = Expression::from_reader(reader, state, options)?; + let position = key.get_position().union(value.get_position()); + Ok(Self::Property { assignment, key, value, position }) } } } @@ -267,11 +279,11 @@ impl ASTNode for ObjectLiteralMember { local: crate::LocalToStringInformation, ) { match self { - Self::Property(name, expression, _) => { - name.to_string_from_buffer(buf, options, local); + Self::Property { assignment: _, key, value, position: _ } => { + key.to_string_from_buffer(buf, options, local); buf.push(':'); options.push_gap_optionally(buf); - expression.to_string_from_buffer(buf, options, local); + value.to_string_from_buffer(buf, options, local); } Self::Shorthand(name, ..) => { buf.push_str(name.as_str()); @@ -286,10 +298,12 @@ impl ASTNode for ObjectLiteralMember { }; } - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { Self::Method(method) => method.get_position(), - Self::Shorthand(_, pos) | Self::Property(_, _, pos) | Self::Spread(_, pos) => pos, + Self::Shorthand(_, pos) + | Self::Property { position: pos, .. } + | Self::Spread(_, pos) => *pos, } } } diff --git a/parser/src/expressions/template_literal.rs b/parser/src/expressions/template_literal.rs index 8f347093..159bfec7 100644 --- a/parser/src/expressions/template_literal.rs +++ b/parser/src/expressions/template_literal.rs @@ -48,8 +48,8 @@ impl crate::Visitable for TemplateLiteralPart } impl ASTNode for TemplateLiteral { - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn from_reader( diff --git a/parser/src/extensions/decorators.rs b/parser/src/extensions/decorators.rs index 120f0b90..c861f54a 100644 --- a/parser/src/extensions/decorators.rs +++ b/parser/src/extensions/decorators.rs @@ -21,8 +21,8 @@ pub struct Decorator { } impl ASTNode for Decorator { - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn from_reader( @@ -118,8 +118,8 @@ pub struct Decorated { } impl ASTNode for Decorated { - fn get_position(&self) -> &Span { - self.get() + fn get_position(&self) -> Span { + *self.get() } fn from_reader( @@ -149,7 +149,7 @@ impl Decorated { pub fn new(decorators: Vec, on: U) -> Self { let position = - decorators.first().map_or(on.get_position(), |d| &d.position).union(on.get_position()); + decorators.first().map_or(on.get_position(), |d| d.position).union(on.get_position()); Self { decorators, on, position } } diff --git a/parser/src/extensions/is_expression.rs b/parser/src/extensions/is_expression.rs index b4098c1a..b57776ef 100644 --- a/parser/src/extensions/is_expression.rs +++ b/parser/src/extensions/is_expression.rs @@ -48,8 +48,8 @@ impl ASTNode for IsExpression { buf.push('}'); } - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } } diff --git a/parser/src/extensions/jsx.rs b/parser/src/extensions/jsx.rs index 2e197b60..6293772c 100644 --- a/parser/src/extensions/jsx.rs +++ b/parser/src/extensions/jsx.rs @@ -75,8 +75,8 @@ impl ASTNode for JSXElement { } } - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } } @@ -89,8 +89,8 @@ pub struct JSXFragment { } impl ASTNode for JSXFragment { - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn from_reader( @@ -153,7 +153,7 @@ impl ASTNode for JSXRoot { } } - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { JSXRoot::Element(element) => element.get_position(), JSXRoot::Fragment(fragment) => fragment.get_position(), @@ -233,13 +233,13 @@ pub enum JSXNode { } impl ASTNode for JSXNode { - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { JSXNode::TextNode(_, pos) | JSXNode::InterpolatedExpression(_, pos) - | JSXNode::Comment(_, pos) => pos, + | JSXNode::Comment(_, pos) => *pos, JSXNode::Element(element) => element.get_position(), - JSXNode::LineBreak => &source_map::Nullable::NULL, + JSXNode::LineBreak => source_map::Nullable::NULL, } } @@ -317,12 +317,12 @@ pub enum JSXAttribute { } impl ASTNode for JSXAttribute { - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { - JSXAttribute::Static(_, _, span) - | JSXAttribute::Dynamic(_, _, span) - | JSXAttribute::BooleanAttribute(_, span) => span, - JSXAttribute::Spread(_, spread_pos) => spread_pos, + JSXAttribute::Static(_, _, pos) + | JSXAttribute::Dynamic(_, _, pos) + | JSXAttribute::BooleanAttribute(_, pos) => *pos, + JSXAttribute::Spread(_, spread_pos) => *spread_pos, JSXAttribute::Shorthand(expr) => expr.get_position(), } } diff --git a/parser/src/functions/mod.rs b/parser/src/functions/mod.rs index ba3fdf24..65cc919c 100644 --- a/parser/src/functions/mod.rs +++ b/parser/src/functions/mod.rs @@ -9,7 +9,7 @@ use crate::{ }; use crate::{PropertyKey, TSXKeyword}; use derive_partial_eq_extras::PartialEqExtras; -use source_map::{Span, ToString}; +use source_map::{Nullable, Span, ToString}; use tokenizer_lib::sized_tokens::TokenStart; use tokenizer_lib::{Token, TokenReader}; @@ -200,8 +200,8 @@ impl ASTNode for FunctionBase { self.body.to_string_from_buffer(buf, options, local.next_level()); } - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } } @@ -234,8 +234,15 @@ impl FunctionBase { } let body = T::Body::from_reader(reader, state, options)?; let body_pos = body.get_position(); + let end_pos = if body_pos == Span::NULL { + return_type.as_ref().map_or(parameters.position, ASTNode::get_position) + } else { + body_pos + }; + let position = - header_left.unwrap_or_else(|| parameters.position.get_start()).union(body_pos); + header_left.unwrap_or_else(|| parameters.position.get_start()).union(end_pos); + Ok(Self { header, name, type_parameters, parameters, return_type, body, position }) } } @@ -303,7 +310,6 @@ impl FunctionBased for GeneralFunctionBase type ParameterVisibility = (); type Body = T::FunctionBody; - #[cfg(feature = "full-typescript")] fn has_body(body: &Self::Body) -> bool { T::has_function_body(body) } @@ -392,11 +398,11 @@ pub enum FunctionHeader { } impl ASTNode for FunctionHeader { - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { - FunctionHeader::VirginFunctionHeader { position, .. } => position, + FunctionHeader::VirginFunctionHeader { position, .. } => *position, #[cfg(feature = "extras")] - FunctionHeader::ChadFunctionHeader { position, .. } => position, + FunctionHeader::ChadFunctionHeader { position, .. } => *position, } } @@ -674,16 +680,18 @@ pub(crate) fn get_method_name( Ok((function_header, key)) } +// #[cfg(feature = "full-typescript")] /// None if overloaded (declaration only) -#[cfg(feature = "full-typescript")] #[apply(derive_ASTNode)] #[derive(Debug, Clone, PartialEq, Eq, visitable_derive::Visitable)] pub struct FunctionBody(pub Option); -#[cfg(feature = "full-typescript")] +// #[cfg(not(feature = "full-typescript"))] +// pub type FunctionBody = Block; + impl ASTNode for FunctionBody { - fn get_position(&self) -> &Span { - self.0.as_ref().map_or(&source_map::Nullable::NULL, |Block(_, pos)| pos) + fn get_position(&self) -> Span { + self.0.as_ref().map_or(source_map::Nullable::NULL, |Block(_, pos)| *pos) } fn from_reader( @@ -713,6 +721,3 @@ impl ASTNode for FunctionBody { } } } - -#[cfg(not(feature = "full-typescript"))] -pub type FunctionBody = Block; diff --git a/parser/src/functions/parameters.rs b/parser/src/functions/parameters.rs index 006c777f..ee4a50f2 100644 --- a/parser/src/functions/parameters.rs +++ b/parser/src/functions/parameters.rs @@ -191,8 +191,8 @@ where L: LeadingParameter, V: ParameterVisibility, { - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn from_reader( @@ -275,7 +275,7 @@ where reader.conditional_next(|tok| matches!(tok, TSXToken::Spread)) { let name = SpreadParameterName::from_reader(reader, state, options)?; - let name_position = *name.get_position(); + let name_position = name.get_position(); let type_annotation = if options.type_annotations && reader.conditional_next(|tok| matches!(tok, TSXToken::Colon)).is_some() @@ -286,7 +286,7 @@ where }; let position = spread_pos - .union(type_annotation.as_ref().map_or(&name_position, ASTNode::get_position)); + .union(type_annotation.as_ref().map_or(name_position, ASTNode::get_position)); rest_parameter = Some(Box::new(SpreadParameter { name, type_annotation, position })); diff --git a/parser/src/lib.rs b/parser/src/lib.rs index dda585c0..5cd10945 100644 --- a/parser/src/lib.rs +++ b/parser/src/lib.rs @@ -43,7 +43,7 @@ pub use statements::Statement; pub use tokens::{TSXKeyword, TSXToken}; pub use types::{ type_annotations::{self, TypeAnnotation}, - type_declarations::{self, TypeDeclaration, TypeParameter}, + type_declarations::{self, TypeParameter}, }; pub use variable_fields::*; pub(crate) use visiting::{ @@ -312,7 +312,7 @@ pub trait ASTNode: Sized + Clone + PartialEq + std::fmt::Debug + Sync + Send + ' } /// Returns position of node as span AS IT WAS PARSED. May be `Span::NULL` if AST was doesn't match anything in source - fn get_position(&self) -> &Span; + fn get_position(&self) -> Span; fn from_reader( reader: &mut impl TokenReader, @@ -812,20 +812,24 @@ pub trait ExpressionOrStatementPosition: fn as_option_variable_identifier_mut(&mut self) -> Option<&mut VariableIdentifier>; fn as_option_str(&self) -> Option<&str> { - if let Some(VariableIdentifier::Standard(name, _)) = self.as_option_variable_identifier() { - Some(name) + if let Some(identifier) = self.as_option_variable_identifier() { + identifier.as_option_str() } else { None } } - #[cfg(feature = "full-typescript")] fn has_function_body(body: &Self::FunctionBody) -> bool; + + fn is_declare(&self) -> bool; } #[derive(Debug, PartialEq, Eq, Clone)] #[apply(derive_ASTNode)] -pub struct StatementPosition(pub VariableIdentifier); +pub struct StatementPosition { + pub identifier: VariableIdentifier, + pub declare: bool, +} impl ExpressionOrStatementPosition for StatementPosition { type FunctionBody = FunctionBody; @@ -835,21 +839,25 @@ impl ExpressionOrStatementPosition for StatementPosition { state: &mut crate::ParsingState, options: &ParseOptions, ) -> ParseResult { - VariableIdentifier::from_reader(reader, state, options).map(Self) + VariableIdentifier::from_reader(reader, state, options) + .map(|identifier| Self { identifier, declare: false }) } fn as_option_variable_identifier(&self) -> Option<&VariableIdentifier> { - Some(&self.0) + Some(&self.identifier) } fn as_option_variable_identifier_mut(&mut self) -> Option<&mut VariableIdentifier> { - Some(&mut self.0) + Some(&mut self.identifier) } - #[cfg(feature = "full-typescript")] fn has_function_body(body: &Self::FunctionBody) -> bool { body.0.is_some() } + + fn is_declare(&self) -> bool { + self.declare + } } #[derive(Debug, PartialEq, Eq, Clone)] @@ -886,10 +894,13 @@ impl ExpressionOrStatementPosition for ExpressionPosition { self.0.as_mut() } - #[cfg(feature = "full-typescript")] fn has_function_body(_: &Self::FunctionBody) -> bool { true } + + fn is_declare(&self) -> bool { + false + } } pub trait ListItem: Sized { diff --git a/parser/src/modules.rs b/parser/src/modules.rs index f97bad9d..02854a2f 100644 --- a/parser/src/modules.rs +++ b/parser/src/modules.rs @@ -29,8 +29,8 @@ impl ASTNode for Module { statements_and_declarations_to_string(&self.items, buf, options, local); } - fn get_position(&self) -> &Span { - &self.span + fn get_position(&self) -> Span { + self.span } fn from_reader( diff --git a/parser/src/property_key.rs b/parser/src/property_key.rs index b9d84fc0..98c4b98f 100644 --- a/parser/src/property_key.rs +++ b/parser/src/property_key.rs @@ -3,6 +3,7 @@ use crate::{ visiting::{Chain, VisitOptions, Visitable}, Quoted, TSXToken, }; +use get_field_by_type::GetFieldByType; use source_map::Span; use std::fmt::Debug; use temporary_annex::Annex; @@ -90,8 +91,9 @@ impl PropertyKeyKind for PublicOrPrivate { } /// A key for a member in a class or object literal -#[derive(Debug, PartialEq, Eq, Clone)] #[apply(derive_ASTNode)] +#[derive(Debug, PartialEq, Eq, Clone, get_field_by_type::GetFieldByType)] +#[get_field_by_type_target(Span)] pub enum PropertyKey { Ident(String, Span, T), StringLiteral(String, Quoted, Span), @@ -101,15 +103,6 @@ pub enum PropertyKey { } impl PropertyKey { - pub fn get_position(&self) -> &Span { - match self { - PropertyKey::Ident(_, pos, _) - | PropertyKey::StringLiteral(_, _, pos) - | PropertyKey::NumberLiteral(_, pos) - | PropertyKey::Computed(_, pos) => pos, - } - } - pub fn is_private(&self) -> bool { match self { PropertyKey::Ident(_, _, p) => U::is_private(p), @@ -130,13 +123,8 @@ impl PartialEq for PropertyKey { } impl ASTNode for PropertyKey { - fn get_position(&self) -> &Span { - match self { - PropertyKey::Ident(_, pos, _) - | PropertyKey::StringLiteral(_, _, pos) - | PropertyKey::NumberLiteral(_, pos) - | PropertyKey::Computed(_, pos) => pos, - } + fn get_position(&self) -> Span { + *self.get() } fn from_reader( diff --git a/parser/src/statements/for_statement.rs b/parser/src/statements/for_statement.rs index b6aa22c7..e9ee4a17 100644 --- a/parser/src/statements/for_statement.rs +++ b/parser/src/statements/for_statement.rs @@ -20,8 +20,8 @@ pub struct ForLoopStatement { } impl ASTNode for ForLoopStatement { - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn from_reader( @@ -40,7 +40,7 @@ impl ASTNode for ForLoopStatement { } else { return Err(ParseError::new( ParseErrors::AwaitRequiresForOf, - *condition.get_position(), + condition.get_position(), )); } } @@ -267,11 +267,11 @@ impl ASTNode for ForLoopCondition { buf.push(')'); } - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { ForLoopCondition::ForOf { position, .. } | ForLoopCondition::ForIn { position, .. } - | ForLoopCondition::Statements { position, .. } => position, + | ForLoopCondition::Statements { position, .. } => *position, } } } diff --git a/parser/src/statements/if_statement.rs b/parser/src/statements/if_statement.rs index bd264977..60aaaa2a 100644 --- a/parser/src/statements/if_statement.rs +++ b/parser/src/statements/if_statement.rs @@ -77,8 +77,8 @@ impl ASTNode for IfStatement { Ok(IfStatement { condition, inner, else_conditions, trailing_else, position }) } - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn to_string_from_buffer( @@ -128,8 +128,8 @@ impl ASTNode for ConditionalElseStatement { Self::from_reader_sub_without_else(reader, state, options, else_start) } - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn to_string_from_buffer( @@ -178,8 +178,8 @@ impl ASTNode for UnconditionalElseStatement { Self::from_reader_sub_without_else(reader, state, options, else_position) } - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn to_string_from_buffer( diff --git a/parser/src/statements/mod.rs b/parser/src/statements/mod.rs index 3f95849c..1bf3f211 100644 --- a/parser/src/statements/mod.rs +++ b/parser/src/statements/mod.rs @@ -78,8 +78,8 @@ pub struct ThrowStatement(pub Box, pub Span); impl Eq for Statement {} impl ASTNode for Statement { - fn get_position(&self) -> &Span { - get_field_by_type::GetFieldByType::get(self) + fn get_position(&self) -> Span { + *get_field_by_type::GetFieldByType::get(self) } fn from_reader( @@ -313,8 +313,8 @@ pub struct VarVariableStatement { } impl ASTNode for VarVariableStatement { - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn from_reader( @@ -332,7 +332,7 @@ impl ASTNode for VarVariableStatement { { return Err(crate::ParseError::new( crate::ParseErrors::DestructuringRequiresValue, - *value.name.get_ast_ref().get_position(), + value.name.get_ast_ref().get_position(), )); } declarations.push(value); diff --git a/parser/src/statements/switch_statement.rs b/parser/src/statements/switch_statement.rs index 242a1b9b..a228bb4d 100644 --- a/parser/src/statements/switch_statement.rs +++ b/parser/src/statements/switch_statement.rs @@ -26,8 +26,8 @@ pub enum SwitchBranch { } impl ASTNode for SwitchStatement { - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn from_reader( diff --git a/parser/src/statements/try_catch_statement.rs b/parser/src/statements/try_catch_statement.rs index faab6dd1..724f01f4 100644 --- a/parser/src/statements/try_catch_statement.rs +++ b/parser/src/statements/try_catch_statement.rs @@ -21,8 +21,8 @@ pub struct TryCatchStatement { } impl ASTNode for TryCatchStatement { - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn from_reader( diff --git a/parser/src/statements/while_statement.rs b/parser/src/statements/while_statement.rs index 649871c9..109d0b48 100644 --- a/parser/src/statements/while_statement.rs +++ b/parser/src/statements/while_statement.rs @@ -16,8 +16,8 @@ pub struct WhileStatement { } impl ASTNode for WhileStatement { - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn from_reader( @@ -60,8 +60,8 @@ pub struct DoWhileStatement { } impl ASTNode for DoWhileStatement { - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn from_reader( diff --git a/parser/src/types/declare_variable.rs b/parser/src/types/declare_variable.rs new file mode 100644 index 00000000..d46dc876 --- /dev/null +++ b/parser/src/types/declare_variable.rs @@ -0,0 +1,79 @@ +use tokenizer_lib::{sized_tokens::TokenStart, Token}; + +use crate::{ + declarations::VariableDeclarationItem, derive_ASTNode, errors::parse_lexing_error, ASTNode, + Decorator, ParseOptions, ParseResult, Span, TSXKeyword, TSXToken, TokenReader, VariableKeyword, +}; + +/// A `declare var/let/const` thingy. +#[apply(derive_ASTNode)] +#[derive(Debug, Clone, PartialEq, Eq, get_field_by_type::GetFieldByType)] +#[get_field_by_type_target(Span)] +pub struct DeclareVariableDeclaration { + pub keyword: VariableKeyword, + /// TODO expressions advised against, but still parse + pub declarations: Vec>>, + pub position: Span, + pub decorators: Vec, +} + +impl ASTNode for DeclareVariableDeclaration { + fn get_position(&self) -> Span { + self.position + } + + fn from_reader( + reader: &mut impl TokenReader, + state: &mut crate::ParsingState, + options: &ParseOptions, + ) -> ParseResult { + let start = state.expect_keyword(reader, TSXKeyword::Declare)?; + Self::from_reader_sub_declare(reader, state, options, Some(start), Vec::new()) + } + + fn to_string_from_buffer( + &self, + buf: &mut T, + options: &crate::ToStringOptions, + local: crate::LocalToStringInformation, + ) { + if options.include_type_annotations { + buf.push_str("declare "); + buf.push_str(self.keyword.as_str()); + crate::declarations::variable::declarations_to_string( + &self.declarations, + buf, + options, + local, + ); + } + } +} + +impl DeclareVariableDeclaration { + pub fn from_reader_sub_declare( + reader: &mut impl TokenReader, + state: &mut crate::ParsingState, + options: &ParseOptions, + start: Option, + decorators: Vec, + ) -> ParseResult { + let token = reader.next().ok_or_else(parse_lexing_error)?; + let start = start.unwrap_or(token.1); + let keyword = VariableKeyword::from_reader(token)?; + let mut declarations = Vec::new(); + loop { + let value = VariableDeclarationItem::from_reader(reader, state, options)?; + declarations.push(value); + if let Some(Token(TSXToken::Comma, _)) = reader.peek() { + reader.next(); + } else { + break; + } + } + + let position = start.union(declarations.last().unwrap().get_position()); + + Ok(DeclareVariableDeclaration { keyword, declarations, position, decorators }) + } +} diff --git a/parser/src/types/declares.rs b/parser/src/types/declares.rs deleted file mode 100644 index a6b52351..00000000 --- a/parser/src/types/declares.rs +++ /dev/null @@ -1,234 +0,0 @@ -use tokenizer_lib::{sized_tokens::TokenStart, Token}; - -use crate::{ - declarations::VariableDeclarationItem, derive_ASTNode, errors::parse_lexing_error, - parse_bracketed, to_string_bracketed, tokens::token_as_identifier, - types::type_annotations::TypeAnnotationFunctionParameters, ASTNode, Decorator, ParseOptions, - ParseResult, Span, TSXKeyword, TSXToken, TokenReader, TypeAnnotation, TypeParameter, - VariableKeyword, -}; - -/// A `declare var/let/const` thingy. -#[apply(derive_ASTNode)] -#[derive(Debug, Clone, PartialEq, Eq, get_field_by_type::GetFieldByType)] -#[get_field_by_type_target(Span)] -pub struct DeclareVariableDeclaration { - pub keyword: VariableKeyword, - /// TODO expressions advised against, but still parse - pub declarations: Vec>>, - pub position: Span, - pub decorators: Vec, -} - -impl ASTNode for DeclareVariableDeclaration { - fn get_position(&self) -> &Span { - &self.position - } - - fn from_reader( - reader: &mut impl TokenReader, - state: &mut crate::ParsingState, - options: &ParseOptions, - ) -> ParseResult { - let start = state.expect_keyword(reader, TSXKeyword::Declare)?; - Self::from_reader_sub_declare(reader, state, options, Some(start), Vec::new()) - } - - fn to_string_from_buffer( - &self, - buf: &mut T, - options: &crate::ToStringOptions, - local: crate::LocalToStringInformation, - ) { - if options.include_type_annotations { - buf.push_str("declare "); - buf.push_str(self.keyword.as_str()); - crate::declarations::variable::declarations_to_string( - &self.declarations, - buf, - options, - local, - ); - } - } -} - -impl DeclareVariableDeclaration { - pub fn from_reader_sub_declare( - reader: &mut impl TokenReader, - state: &mut crate::ParsingState, - options: &ParseOptions, - start: Option, - decorators: Vec, - ) -> ParseResult { - let token = reader.next().ok_or_else(parse_lexing_error)?; - let start = start.unwrap_or(token.1); - let keyword = VariableKeyword::from_reader(token)?; - let mut declarations = Vec::new(); - loop { - let value = VariableDeclarationItem::from_reader(reader, state, options)?; - declarations.push(value); - if let Some(Token(TSXToken::Comma, _)) = reader.peek() { - reader.next(); - } else { - break; - } - } - - let position = start.union(declarations.last().unwrap().get_position()); - - Ok(DeclareVariableDeclaration { keyword, declarations, position, decorators }) - } -} - -#[apply(derive_ASTNode)] -#[derive(Debug, Clone, PartialEq, Eq, get_field_by_type::GetFieldByType)] -#[get_field_by_type_target(Span)] -pub struct DeclareFunctionDeclaration { - pub name: String, - pub type_parameters: Option>, - pub parameters: TypeAnnotationFunctionParameters, - pub return_type: Option, - #[cfg(feature = "extras")] - pub performs: Option, - pub decorators: Vec, - pub position: Span, -} - -impl ASTNode for DeclareFunctionDeclaration { - fn get_position(&self) -> &Span { - &self.position - } - - fn from_reader( - reader: &mut impl TokenReader, - state: &mut crate::ParsingState, - options: &ParseOptions, - ) -> ParseResult { - let _ = state.expect_keyword(reader, TSXKeyword::Declare)?; - Self::from_reader_sub_declare_with_decorators(reader, state, options, Vec::new()) - } - - fn to_string_from_buffer( - &self, - buf: &mut T, - options: &crate::ToStringOptions, - local: crate::LocalToStringInformation, - ) { - if options.include_type_annotations { - buf.push_str("declare function "); - buf.push_str(self.name.as_str()); - if let Some(type_parameters) = &self.type_parameters { - to_string_bracketed(type_parameters, ('<', '>'), buf, options, local); - } - self.parameters.to_string_from_buffer(buf, options, local); - if let Some(return_type) = &self.return_type { - buf.push_str(": "); - return_type.to_string_from_buffer(buf, options, local); - } - } - } -} - -impl DeclareFunctionDeclaration { - pub fn from_reader_sub_declare_with_decorators( - reader: &mut impl TokenReader, - state: &mut crate::ParsingState, - options: &ParseOptions, - decorators: Vec, - ) -> ParseResult { - let start = state.expect_keyword(reader, TSXKeyword::Function)?; - let token = reader.next().ok_or_else(parse_lexing_error)?; - let (name, _) = token_as_identifier(token, "declare function name")?; - let type_parameters = reader - .conditional_next(|tok| *tok == TSXToken::OpenChevron) - .is_some() - .then(|| parse_bracketed(reader, state, options, None, TSXToken::CloseChevron)) - .transpose()? - .map(|(tp, _)| tp); - - let parameters = TypeAnnotationFunctionParameters::from_reader(reader, state, options)?; - let return_type = reader - .conditional_next(|tok| matches!(tok, TSXToken::Colon)) - .is_some() - .then(|| TypeAnnotation::from_reader(reader, state, options)) - .transpose()?; - - #[cfg(feature = "extras")] - let performs = if let Some(Token(TSXToken::Keyword(TSXKeyword::Performs), _)) = reader.peek() { - Some(super::AnnotationPerforms::from_reader(reader, state, options)?) - } else { - None - }; - - let position = - start.union(return_type.as_ref().map_or(¶meters.position, ASTNode::get_position)); - - Ok(Self { - name, - type_parameters, - parameters, - return_type, - #[cfg(feature = "extras")] - performs, - decorators, - position, - }) - } -} - -#[derive(Debug, Clone, PartialEq, Eq)] -#[cfg_attr(target_family = "wasm", derive(tsify::Tsify))] -pub struct DeclareClassDeclaration { - pub name: String, - pub type_parameters: Option>, - pub extends: Option, - // members: Vec -} - -impl ASTNode for DeclareClassDeclaration { - fn get_position(&self) -> &Span { - todo!() - } - - fn from_reader( - reader: &mut impl TokenReader, - state: &mut crate::ParsingState, - options: &ParseOptions, - ) -> ParseResult { - let _ = state.expect_keyword(reader, TSXKeyword::Declare)?; - Self::from_reader_sub_declare(reader, state, options) - } - - fn to_string_from_buffer( - &self, - _buf: &mut T, - _options: &crate::ToStringOptions, - _local: crate::LocalToStringInformation, - ) { - todo!() - } -} - -impl DeclareClassDeclaration { - pub(crate) fn from_reader_sub_declare( - reader: &mut impl TokenReader, - state: &mut crate::ParsingState, - options: &ParseOptions, - ) -> ParseResult { - let _ = state.expect_keyword(reader, TSXKeyword::Class)?; - let (name, _) = - token_as_identifier(reader.next().ok_or_else(parse_lexing_error)?, "class")?; - let extends = if let Some(Token(TSXToken::Keyword(TSXKeyword::Extends), _)) = reader.peek() - { - reader.next(); - Some(TypeAnnotation::from_reader(reader, state, options)?) - } else { - None - }; - reader.expect_next(TSXToken::OpenBrace)?; - // TODO members - reader.expect_next(TSXToken::CloseBrace)?; - Ok(Self { name, extends, type_parameters: None }) - } -} diff --git a/parser/src/types/enum_declaration.rs b/parser/src/types/enum_declaration.rs index 89ca5fd7..dd75f873 100644 --- a/parser/src/types/enum_declaration.rs +++ b/parser/src/types/enum_declaration.rs @@ -16,8 +16,8 @@ pub struct EnumDeclaration { } impl ASTNode for EnumDeclaration { - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn from_reader( @@ -93,9 +93,9 @@ pub enum EnumMember { } impl ASTNode for EnumMember { - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { - EnumMember::Variant { position, .. } => position, + EnumMember::Variant { position, .. } => *position, } } diff --git a/parser/src/types/interface.rs b/parser/src/types/interface.rs index 2b27ce97..892d3b9d 100644 --- a/parser/src/types/interface.rs +++ b/parser/src/types/interface.rs @@ -3,8 +3,8 @@ use crate::{ functions::MethodHeader, parse_bracketed, property_key::PublicOrPrivate, throw_unexpected_token_with_token, to_string_bracketed, tokens::token_as_identifier, types::type_annotations::TypeAnnotationFunctionParameters, ASTNode, Expression, - NumberRepresentation, ParseOptions, ParseResult, PropertyKey, Span, TSXKeyword, TSXToken, - TypeAnnotation, TypeDeclaration, TypeParameter, WithComment, + ExpressionOrStatementPosition, NumberRepresentation, ParseOptions, ParseResult, PropertyKey, + Span, StatementPosition, TSXKeyword, TSXToken, TypeAnnotation, TypeParameter, WithComment, }; use get_field_by_type::GetFieldByType; @@ -15,7 +15,8 @@ use tokenizer_lib::{sized_tokens::TokenReaderWithTokenEnds, Token, TokenReader}; #[derive(Debug, Clone, PartialEq, Eq, get_field_by_type::GetFieldByType)] #[get_field_by_type_target(Span)] pub struct InterfaceDeclaration { - pub name: String, + pub is_declare: bool, + pub name: StatementPosition, #[cfg(feature = "extras")] pub is_nominal: bool, pub type_parameters: Option>, @@ -55,14 +56,15 @@ impl ASTNode for InterfaceDeclaration { .conditional_next(|t| matches!(t, TSXToken::Keyword(TSXKeyword::Nominal))) .is_some(); - // if let Some(Token(TSXToken::Keyword(TSXKeyword::Nominal), _)) = reader.peek() { - // Some((reader.next().unwrap().1)) - // } else { - // None - // }; - - let TypeDeclaration { name, type_parameters, .. } = - TypeDeclaration::from_reader(reader, state, options)?; + let name = StatementPosition::from_reader(reader, state, options)?; + let type_parameters = reader + .conditional_next(|token| *token == TSXToken::OpenChevron) + .is_some() + .then(|| { + crate::parse_bracketed(reader, state, options, None, TSXToken::CloseChevron) + .map(|(params, _)| params) + }) + .transpose()?; let extends = if reader .conditional_next(|t| matches!(t, TSXToken::Keyword(TSXKeyword::Extends))) @@ -97,6 +99,7 @@ impl ASTNode for InterfaceDeclaration { let position = start.union(reader.expect_next_get_end(TSXToken::CloseBrace)?); Ok(InterfaceDeclaration { name, + is_declare: false, #[cfg(feature = "extras")] is_nominal, type_parameters, @@ -113,8 +116,11 @@ impl ASTNode for InterfaceDeclaration { local: crate::LocalToStringInformation, ) { if options.include_type_annotations { + if self.name.is_declare() { + buf.push_str("declare "); + } buf.push_str("interface "); - buf.push_str(&self.name); + self.name.identifier.to_string_from_buffer(buf, options, local); if let Some(type_parameters) = &self.type_parameters { to_string_bracketed(type_parameters, ('<', '>'), buf, options, local); options.push_gap_optionally(buf); @@ -145,8 +151,8 @@ impl ASTNode for InterfaceDeclaration { } } - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } } @@ -162,8 +168,6 @@ pub enum InterfaceMember { parameters: TypeAnnotationFunctionParameters, return_type: Option, is_optional: bool, - #[cfg(feature = "extras")] - performs: Option, position: Span, }, Property { @@ -190,8 +194,6 @@ pub enum InterfaceMember { type_parameters: Option>, return_type: Option, is_readonly: bool, - #[cfg(feature = "extras")] - performs: Option, position: Span, }, Caller { @@ -238,9 +240,10 @@ impl ASTNode for InterfaceMember { None }; // TODO parameter.pos can be union'ed with itself - let position = readonly_position.as_ref().unwrap_or(¶meters.position).union( - return_type.as_ref().map_or(¶meters.position, ASTNode::get_position), - ); + let position = readonly_position + .as_ref() + .unwrap_or(¶meters.position) + .union(return_type.as_ref().map_or(parameters.position, ASTNode::get_position)); Ok(InterfaceMember::Caller { is_readonly: readonly_position.is_some(), position, @@ -262,7 +265,7 @@ impl ASTNode for InterfaceMember { None }; let position = - *return_type.as_ref().map_or(¶meters.position, ASTNode::get_position); + return_type.as_ref().map_or(parameters.position, ASTNode::get_position); Ok(InterfaceMember::Caller { is_readonly: readonly_position.is_some(), @@ -296,14 +299,7 @@ impl ASTNode for InterfaceMember { None }; - #[cfg(feature = "extras")] - let performs = if let Some(Token(TSXToken::Keyword(TSXKeyword::Performs), _)) = reader.peek() { - Some(super::AnnotationPerforms::from_reader(reader, state, options)?) - } else { - None - }; - - let end = return_type.as_ref().map_or(¶meters.position, ASTNode::get_position); + let end = return_type.as_ref().map_or(parameters.position, ASTNode::get_position); let position = readonly_position.as_ref().unwrap_or(&new_span).union(end); @@ -311,8 +307,6 @@ impl ASTNode for InterfaceMember { is_readonly: readonly_position.is_some(), position, parameters, - #[cfg(feature = "extras")] - performs, type_parameters, return_type, }) @@ -456,7 +450,7 @@ impl ASTNode for InterfaceMember { (property_key, type_parameters.map(|(tp, _)| tp)) }; - let start = readonly_position.as_ref().unwrap_or_else(|| name.get_position()); + let start = readonly_position.unwrap_or_else(|| name.get_position()); // TODO a little weird as only functions can have type parameters: match reader.next().ok_or_else(parse_lexing_error)? { @@ -481,15 +475,6 @@ impl ASTNode for InterfaceMember { None }; - #[cfg(feature = "extras")] - let performs = if let Some(Token(TSXToken::Keyword(TSXKeyword::Performs), _)) = - reader.peek() - { - Some(super::AnnotationPerforms::from_reader(reader, state, options)?) - } else { - None - }; - Ok(InterfaceMember::Method { header, name, @@ -498,8 +483,6 @@ impl ASTNode for InterfaceMember { return_type, is_optional: false, position, - #[cfg(feature = "extras")] - performs, }) } Token(TSXToken::QuestionMark, _) => { @@ -522,15 +505,6 @@ impl ASTNode for InterfaceMember { None }; - #[cfg(feature = "extras")] - let performs = if let Some(Token(TSXToken::Keyword(TSXKeyword::Performs), _)) = - reader.peek() - { - Some(super::AnnotationPerforms::from_reader(reader, state, options)?) - } else { - None - }; - Ok(InterfaceMember::Method { header, name, @@ -539,8 +513,6 @@ impl ASTNode for InterfaceMember { is_optional: true, position, return_type, - #[cfg(feature = "extras")] - performs, }) } Token(TSXToken::Colon, _) => { @@ -645,8 +617,8 @@ impl ASTNode for InterfaceMember { } } - fn get_position(&self) -> &Span { - GetFieldByType::get(self) + fn get_position(&self) -> Span { + *GetFieldByType::get(self) } } diff --git a/parser/src/types/mod.rs b/parser/src/types/mod.rs index e048d315..5d46f637 100644 --- a/parser/src/types/mod.rs +++ b/parser/src/types/mod.rs @@ -1,6 +1,6 @@ //! Includes type annotations + syntax added by TypeScript (and Ezno) such as `declare` declarations -pub mod declares; +pub mod declare_variable; pub mod enum_declaration; pub mod interface; pub mod namespace; @@ -39,48 +39,3 @@ impl Visibility { ) } } - -#[cfg(feature = "extras")] -#[derive(Debug, Clone, PartialEq, Eq)] -#[apply(derive_ASTNode)] -pub enum AnnotationPerforms { - PerformsStatements { body: crate::Block }, - PerformsConst { identifier: String }, -} - -#[cfg(feature = "extras")] -impl crate::ASTNode for AnnotationPerforms { - fn get_position(&self) -> &source_map::Span { - todo!() - } - - fn from_reader( - reader: &mut impl tokenizer_lib::TokenReader, - state: &mut crate::ParsingState, - options: &crate::ParseOptions, - ) -> crate::ParseResult { - let _ = reader.expect_next(crate::TSXToken::Keyword(crate::TSXKeyword::Performs))?; - if let Some(tokenizer_lib::Token(crate::TSXToken::OpenBrace, _)) = reader.peek() { - // let expression = Expression::from_reader(reader, state, options)?; - // reader.expect_next(TSXToken::CloseParentheses)?; - // Some(Box::new(expression)) - - let body = crate::Block::from_reader(reader, state, options)?; - Ok(AnnotationPerforms::PerformsStatements { body }) - } else { - reader.expect_next(crate::TSXToken::Keyword(crate::TSXKeyword::Const))?; - let (identifier, _) = - crate::tokens::token_as_identifier(reader.next().unwrap(), "performs const")?; - Ok(AnnotationPerforms::PerformsConst { identifier }) - } - } - - fn to_string_from_buffer( - &self, - _buf: &mut T, - _options: &crate::ToStringOptions, - _local: crate::LocalToStringInformation, - ) { - todo!() - } -} diff --git a/parser/src/types/namespace.rs b/parser/src/types/namespace.rs index e4c07e69..bbfb4758 100644 --- a/parser/src/types/namespace.rs +++ b/parser/src/types/namespace.rs @@ -43,7 +43,7 @@ impl crate::ASTNode for Namespace { } } - fn get_position(&self) -> &source_map::Span { - self.get() + fn get_position(&self) -> source_map::Span { + *self.get() } } diff --git a/parser/src/types/type_alias.rs b/parser/src/types/type_alias.rs index 57d40c20..aa5dd8ac 100644 --- a/parser/src/types/type_alias.rs +++ b/parser/src/types/type_alias.rs @@ -1,14 +1,18 @@ use source_map::Span; -use crate::{derive_ASTNode, ASTNode, TSXToken, TypeAnnotation, TypeDeclaration}; +use crate::{ + derive_ASTNode, to_string_bracketed, ASTNode, ExpressionOrStatementPosition, StatementPosition, + TSXToken, TypeAnnotation, TypeParameter, +}; /// e.g. `type NumberArray = Array` #[apply(derive_ASTNode)] #[derive(Debug, Clone, PartialEq, Eq, get_field_by_type::GetFieldByType)] #[get_field_by_type_target(Span)] pub struct TypeAlias { - pub type_name: TypeDeclaration, - pub type_expression: TypeAnnotation, + pub name: StatementPosition, + pub parameters: Option>, + pub references: TypeAnnotation, pub position: Span, } @@ -19,11 +23,21 @@ impl ASTNode for TypeAlias { options: &crate::ParseOptions, ) -> crate::ParseResult { let start = state.expect_keyword(reader, crate::TSXKeyword::Type)?; - let type_name = TypeDeclaration::from_reader(reader, state, options)?; + let name = StatementPosition::from_reader(reader, state, options)?; + let parameters = reader + .conditional_next(|token| *token == TSXToken::OpenChevron) + .is_some() + .then(|| { + crate::parse_bracketed(reader, state, options, None, TSXToken::CloseChevron) + .map(|(params, _)| params) + }) + .transpose()?; + reader.expect_next(TSXToken::Assign)?; - let type_expression = TypeAnnotation::from_reader(reader, state, options)?; - let position = start.union(type_expression.get_position()); - Ok(Self { type_name, type_expression, position }) + let references = TypeAnnotation::from_reader(reader, state, options)?; + let position = start.union(references.get_position()); + + Ok(Self { name, parameters, references, position }) } fn to_string_from_buffer( @@ -33,14 +47,20 @@ impl ASTNode for TypeAlias { local: crate::LocalToStringInformation, ) { if options.include_type_annotations { + if self.name.is_declare() { + buf.push_str("declare "); + } buf.push_str("type "); - self.type_name.to_string_from_buffer(buf, options, local); + self.name.identifier.to_string_from_buffer(buf, options, local); + if let Some(type_parameters) = &self.parameters { + to_string_bracketed(type_parameters, ('<', '>'), buf, options, local); + } buf.push_str(" = "); - self.type_expression.to_string_from_buffer(buf, options, local); + self.references.to_string_from_buffer(buf, options, local); } } - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } } diff --git a/parser/src/types/type_annotations.rs b/parser/src/types/type_annotations.rs index 5fdaf54a..251c5b26 100644 --- a/parser/src/types/type_annotations.rs +++ b/parser/src/types/type_annotations.rs @@ -1,6 +1,8 @@ +use std::ops::Neg; + use crate::{ derive_ASTNode, parse_bracketed, throw_unexpected_token_with_token, to_string_bracketed, - ListItem, Quoted, + ListItem, ParseErrors, Quoted, }; use crate::{ errors::parse_lexing_error, expressions::TemplateLiteralPart, @@ -8,7 +10,7 @@ use crate::{ }; use derive_partial_eq_extras::PartialEqExtras; use iterator_endiate::EndiateIteratorExt; -use tokenizer_lib::sized_tokens::{TokenEnd, TokenReaderWithTokenEnds, TokenStart}; +use tokenizer_lib::sized_tokens::{SizedToken, TokenEnd, TokenReaderWithTokenEnds, TokenStart}; use super::{ interface::{parse_interface_members, InterfaceMember}, @@ -106,9 +108,9 @@ pub enum AnnotationWithBinder { } impl ASTNode for AnnotationWithBinder { - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { - AnnotationWithBinder::Annotated { position, .. } => position, + AnnotationWithBinder::Annotated { position, .. } => *position, AnnotationWithBinder::NoAnnotation(ty) => ty.get_position(), } } @@ -192,10 +194,10 @@ impl TypeCondition { } } - pub(crate) fn get_position(&self) -> &Span { + pub(crate) fn get_position(&self) -> Span { match self { TypeCondition::Extends { position, .. } | TypeCondition::Is { position, .. } => { - position + *position } } } @@ -211,9 +213,9 @@ pub enum TypeConditionResult { } impl ASTNode for TypeConditionResult { - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { - TypeConditionResult::Infer(_, pos) => pos, + TypeConditionResult::Infer(_, pos) => *pos, TypeConditionResult::Reference(reference) => reference.get_position(), } } @@ -403,8 +405,8 @@ impl ASTNode for TypeAnnotation { } } - fn get_position(&self) -> &Span { - get_field_by_type::GetFieldByType::get(self) + fn get_position(&self) -> Span { + *get_field_by_type::GetFieldByType::get(self) } } @@ -518,6 +520,20 @@ impl TypeAnnotation { let pos = start.with_length(num.len()); Self::NumberLiteral(num.parse::().unwrap(), pos) } + Token(TSXToken::Subtract, start) => { + let Token(token, pos) = reader.next().ok_or_else(parse_lexing_error)?; + if let TSXToken::NumberLiteral(num) = token { + let pos = pos.union(start.with_length(num.len())); + let number_representation = num.parse::().unwrap(); + // important negation here + Self::NumberLiteral(number_representation.neg(), pos) + } else { + return Err(ParseError::new( + ParseErrors::ExpectedNumberLiteral, + start.with_length(token.length() as usize + 1), + )); + } + } Token(TSXToken::StringLiteral(content, quoted), start) => { let pos = start.with_length(content.len() + 2); Self::StringLiteral(content, quoted, pos) @@ -696,8 +712,8 @@ impl TypeAnnotation { }; // Namespaced name if let Some(Token(TSXToken::Dot, _)) = reader.peek() { + let Self::Name(name, start) = reference else { return Ok(reference) }; reader.next(); - let Self::Name(name, start) = reference else { panic!() }; let (namespace_member, end) = token_as_identifier(reader.next().unwrap(), "namespace name")?; let position = start.union(end); @@ -861,7 +877,7 @@ impl TypeAnnotation { Some(TypeOperatorKind::Function), start, )?; - let parameters_position = *reference.get_position(); + let parameters_position = reference.get_position(); let position = parameters_position.union(return_type.get_position()); Ok(Self::FunctionLiteral { position, @@ -944,8 +960,8 @@ pub struct TypeAnnotationFunctionParameters { } impl ASTNode for TypeAnnotationFunctionParameters { - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } fn from_reader( @@ -1052,7 +1068,7 @@ impl TypeAnnotationFunctionParameters { let type_annotation = TypeAnnotation::from_reader(reader, state, options)?; let position = name .as_ref() - .map_or(type_annotation.get_position(), |name| name.get_position()) + .map_or(type_annotation.get_position(), ASTNode::get_position) .union(type_annotation.get_position()); parameters.push(TypeAnnotationFunctionParameter { diff --git a/parser/src/types/type_declarations.rs b/parser/src/types/type_declarations.rs index 1dae9669..fb6d9070 100644 --- a/parser/src/types/type_declarations.rs +++ b/parser/src/types/type_declarations.rs @@ -1,60 +1,9 @@ use crate::{ - derive_ASTNode, errors::parse_lexing_error, parse_bracketed, to_string_bracketed, - tokens::token_as_identifier, ASTNode, ListItem, ParseOptions, ParseResult, Span, TSXKeyword, - TSXToken, TypeAnnotation, + derive_ASTNode, errors::parse_lexing_error, tokens::token_as_identifier, ASTNode, ListItem, + ParseOptions, ParseResult, Span, TSXKeyword, TSXToken, TypeAnnotation, }; use tokenizer_lib::TokenReader; -/// Similar to type reference but no unions or intersections AND includes generic constraints. -/// Used for declaring classes, interfaces and functions -#[derive(Debug, Clone, PartialEq, Eq)] -#[apply(derive_ASTNode)] -pub struct TypeDeclaration { - pub name: String, - pub type_parameters: Option>, - pub position: Span, -} - -impl ASTNode for TypeDeclaration { - fn from_reader( - reader: &mut impl TokenReader, - state: &mut crate::ParsingState, - options: &ParseOptions, - ) -> ParseResult { - // Get initial name - let (name, position) = token_as_identifier( - reader.next().ok_or_else(parse_lexing_error)?, - "type declaration name", - )?; - - let type_parameters = reader - .conditional_next(|token| *token == TSXToken::OpenChevron) - .is_some() - .then(|| { - parse_bracketed(reader, state, options, None, TSXToken::CloseChevron) - .map(|(params, _)| params) - }) - .transpose()?; - Ok(Self { name, type_parameters, position }) - } - - fn to_string_from_buffer( - &self, - buf: &mut T, - options: &crate::ToStringOptions, - local: crate::LocalToStringInformation, - ) { - buf.push_str(&self.name); - if let Some(ref type_parameters) = self.type_parameters { - to_string_bracketed(type_parameters, ('<', '>'), buf, options, local); - } - } - - fn get_position(&self) -> &Span { - &self.position - } -} - /// Represents a generic parameter. Can have default or constraint to extend a type or a key of a type /// /// TODO is default and extends mut ex @@ -129,7 +78,7 @@ impl ASTNode for TypeParameter { } } - fn get_position(&self) -> &Span { - &self.position + fn get_position(&self) -> Span { + self.position } } diff --git a/parser/src/variable_fields.rs b/parser/src/variable_fields.rs index 680e5d09..981f1db3 100644 --- a/parser/src/variable_fields.rs +++ b/parser/src/variable_fields.rs @@ -65,8 +65,18 @@ impl ASTNode for VariableIdentifier { } } - fn get_position(&self) -> &Span { - self.get() + fn get_position(&self) -> Span { + *self.get() + } +} + +impl VariableIdentifier { + #[must_use] + pub fn as_option_str(&self) -> Option<&str> { + match self { + VariableIdentifier::Standard(s, _) => Some(s.as_str()), + VariableIdentifier::Marker(_, _) => None, + } } } @@ -127,9 +137,7 @@ impl ASTNode for VariableField { member.to_string_from_buffer(buf, options, local); if !at_end { buf.push(','); - if !matches!(member.get_ast_ref(), ArrayDestructuringField::None) { - options.push_gap_optionally(buf); - } + options.push_gap_optionally(buf); } } buf.push(']'); @@ -150,9 +158,9 @@ impl ASTNode for VariableField { } } - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { - VariableField::Array(_, position) | VariableField::Object(_, position) => position, + VariableField::Array(_, position) | VariableField::Object(_, position) => *position, VariableField::Name(id) => id.get_position(), } } @@ -161,7 +169,7 @@ impl ASTNode for VariableField { #[derive(Debug, Clone, PartialEq, Eq)] #[apply(derive_ASTNode)] pub enum ArrayDestructuringField { - Spread(VariableIdentifier, Span), + Spread(VariableField, Span), Name(VariableField, Option>), Comment { content: String, is_multiline: bool, position: Span }, None, @@ -184,10 +192,9 @@ impl ASTNode for ArrayDestructuringField { let Token(token, _start) = reader.peek().ok_or_else(parse_lexing_error)?; if let TSXToken::Spread = token { let token = reader.next().unwrap(); - Ok(Self::Spread( - VariableIdentifier::from_reader(reader, state, options)?, - token.get_span(), - )) + let variable_field = VariableField::from_reader(reader, state, options)?; + let position = token.get_span().union(variable_field.get_position()); + Ok(Self::Spread(variable_field, position)) } else if matches!(token, TSXToken::Comma | TSXToken::CloseBracket) { Ok(Self::None) } else { @@ -222,7 +229,9 @@ impl ASTNode for ArrayDestructuringField { Self::Name(name, default_value) => { name.to_string_from_buffer(buf, options, local); if let Some(default_value) = default_value { + options.push_gap_optionally(buf); buf.push('='); + options.push_gap_optionally(buf); default_value.to_string_from_buffer(buf, options, local); } } @@ -237,13 +246,13 @@ impl ASTNode for ArrayDestructuringField { } } - fn get_position(&self) -> &Span { + fn get_position(&self) -> Span { match self { ArrayDestructuringField::Comment { position, .. } - | ArrayDestructuringField::Spread(_, position) => position, + | ArrayDestructuringField::Spread(_, position) => *position, // TODO misses out optional expression ArrayDestructuringField::Name(vf, _) => vf.get_position(), - ArrayDestructuringField::None => &source_map::Nullable::NULL, + ArrayDestructuringField::None => source_map::Nullable::NULL, } } } @@ -253,7 +262,7 @@ impl ASTNode for ArrayDestructuringField { #[get_field_by_type_target(Span)] #[partial_eq_ignore_types(Span)] pub enum ObjectDestructuringField { - /// `{ x }` + /// `{ x }` and (annoyingly) `{ x = 2 }` Name(VariableIdentifier, Option>, Span), /// `{ ...x }` Spread(VariableIdentifier, Span), @@ -293,7 +302,7 @@ impl ASTNode for ObjectDestructuringField { let position = if let Some(ref dv) = default_value { key.get_position().union(dv.get_position()) } else { - *key.get_position() + key.get_position() }; Ok(Self::Map { from: key, name: variable_name, default_value, position }) @@ -310,6 +319,7 @@ impl ASTNode for ObjectDestructuringField { } else { key_pos }; + Ok(Self::Name(standard, default_value, position)) } else { let token = reader.next().ok_or_else(parse_lexing_error)?; @@ -329,27 +339,32 @@ impl ASTNode for ObjectDestructuringField { buf.push_str("..."); name.to_string_from_buffer(buf, options, local); } - Self::Name(name, default_value, _) => { + Self::Name(name, default_value, ..) => { name.to_string_from_buffer(buf, options, local); if let Some(default_value) = default_value { + options.push_gap_optionally(buf); buf.push('='); + options.push_gap_optionally(buf); default_value.to_string_from_buffer(buf, options, local); } } Self::Map { from, name: variable_name, default_value, .. } => { from.to_string_from_buffer(buf, options, local); buf.push(':'); + options.push_gap_optionally(buf); variable_name.to_string_from_buffer(buf, options, local); if let Some(default_value) = default_value { + options.push_gap_optionally(buf); buf.push('='); + options.push_gap_optionally(buf); default_value.to_string_from_buffer(buf, options, local); } } } } - fn get_position(&self) -> &Span { - self.get() + fn get_position(&self) -> Span { + *self.get() } } @@ -604,8 +619,8 @@ mod tests { VariableField::Name(VariableIdentifier::Standard(Deref @ "x", span!(1, 2))), None, )), WithComment::None(ArrayDestructuringField::Spread( - VariableIdentifier::Standard(Deref @ "y", span!(7, 8)), - span!(4, 7), + VariableField::Name(VariableIdentifier::Standard(Deref @ "y", span!(7, 8))), + span!(4, 8), ))], span!(0, 9), ) diff --git a/parser/src/visiting.rs b/parser/src/visiting.rs index 98251e23..4b9f0556 100644 --- a/parser/src/visiting.rs +++ b/parser/src/visiting.rs @@ -242,8 +242,7 @@ mod ast { crate::expressions::operators::UnaryPostfixAssignmentOperator, crate::types::InterfaceDeclaration, crate::types::type_alias::TypeAlias, - crate::types::declares::DeclareFunctionDeclaration, - crate::types::declares::DeclareVariableDeclaration, + crate::types::declare_variable::DeclareVariableDeclaration, crate::VariableIdentifier, crate::PropertyReference, crate::Quoted, @@ -366,12 +365,7 @@ mod structures { pub fn get_variable_name(&self) -> Option<&'a str> { match self { ImmutableVariableOrProperty::VariableFieldName(name, _) => Some(name), - ImmutableVariableOrProperty::ArrayDestructuringMember(a) => match a { - ArrayDestructuringField::Spread(VariableIdentifier::Standard(a, _), _) => { - Some(a.as_str()) - } - _ => None, - }, + ImmutableVariableOrProperty::ArrayDestructuringMember(_) => None, ImmutableVariableOrProperty::ObjectDestructuringMember(o) => { match o.get_ast_ref() { ObjectDestructuringField::Spread(VariableIdentifier::Standard(a, _), _) @@ -413,15 +407,15 @@ mod structures { } #[must_use] - pub fn get_position(&self) -> &Span { + pub fn get_position(&self) -> Span { use crate::ASTNode; match self { ImmutableVariableOrProperty::FunctionName(pos) | ImmutableVariableOrProperty::ClassName(pos) => match pos { Some(p) => p.get_position(), - None => &source_map::Nullable::NULL, + None => source_map::Nullable::NULL, }, - ImmutableVariableOrProperty::VariableFieldName(_, pos) => pos, + ImmutableVariableOrProperty::VariableFieldName(_, pos) => **pos, ImmutableVariableOrProperty::ArrayDestructuringMember(m) => m.get_position(), ImmutableVariableOrProperty::ObjectDestructuringMember(m) => m.get_position(), ImmutableVariableOrProperty::ClassPropertyKey(k) => k.get_position(), diff --git a/parser/tests/statements_and_declarations.rs b/parser/tests/statements_and_declarations.rs index 8398e99f..2f4d0b3a 100644 --- a/parser/tests/statements_and_declarations.rs +++ b/parser/tests/statements_and_declarations.rs @@ -250,3 +250,44 @@ worker function a() {} // let output = module.to_string(&ezno_parser::ToStringOptions::typescript()); // assert_eq!(output, input); } + +#[test] +fn destructuring() { + // from: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#syntax + let input = r" +const [a, b] = array; +const [a, , b] = array; +const [a = aDefault, b] = array; +const [a, b, ...rest] = array; +const [a, , b, ...rest] = array; +const [a, b, ...{ pop, push }] = array; +const [a, b, ...[c, d]] = array; +const { a, b } = obj; +const { a: a1, b: b1 } = obj; +const { a: a1 = aDefault, b = bDefault } = obj; +const { a, b, ...rest } = obj; +const { a: a1, b: b1, ...rest } = obj; +const { [key]: a } = obj; +let a, b, a1, b1, c, d, rest, pop, push; +[a, b] = array; +[a, , b] = array; +[a = aDefault, b] = array; +[a, b, ...rest] = array; +[a, , b, ...rest] = array; +[a, b, ...{ pop, push }] = array; +[a, b, ...[c, d]] = array; +({ a, b } = obj); +({ a: a1, b: b1 } = obj); +({ a: a1 = aDefault, b = bDefault } = obj); +({ a, b, ...rest } = obj); +({ a: a1, b: b1, ...rest } = obj) + " + .trim(); + + let module = Module::from_string(input.to_owned(), Default::default()).unwrap(); + + eprintln!("Module: {module:#?}"); + + let output = module.to_string(&ezno_parser::ToStringOptions::typescript()); + assert_eq!(output, input); +} diff --git a/src/repl.rs b/src/repl.rs index e6f6fe18..be65696a 100644 --- a/src/repl.rs +++ b/src/repl.rs @@ -82,7 +82,7 @@ pub(crate) fn run_repl( let result = if input.trim_start().starts_with('{') { Expression::from_string_with_options(input, options, offset).map(|(expression, _)| { Module { - span: *expression.get_position(), + span: expression.get_position(), items: vec![Statement::Expression(expression.into()).into()], } }) diff --git a/src/transformers/optimisations.rs b/src/transformers/optimisations.rs index 0030163d..f52dc330 100644 --- a/src/transformers/optimisations.rs +++ b/src/transformers/optimisations.rs @@ -29,16 +29,17 @@ impl VisitorMut for ExpressionOpti // TODO properties and even entire object for item in literal.members.iter_mut() { if let ObjectLiteralMember::Method(method) = item { - let position = *method.get_position(); + let position = method.get_position(); let function_id = FunctionId(chain.get_module(), position.start); if !data.is_function_called(function_id) { // Make it null for now to not break `Object.keys` - let name = method.name.clone(); - *item = ObjectLiteralMember::Property( - name, - Expression::Null(position), + let key = method.name.clone(); + *item = ObjectLiteralMember::Property { + key, + assignment: false, + value: Expression::Null(position), position, - ); + }; } } } @@ -47,14 +48,14 @@ impl VisitorMut for ExpressionOpti if !data .is_function_called(FunctionId(chain.get_module(), func.get_position().start)) { - *item = Expression::Null(*func.get_position()); + *item = Expression::Null(func.get_position()); } } Expression::ExpressionFunction(func) => { if !data .is_function_called(FunctionId(chain.get_module(), func.get_position().start)) { - *item = Expression::Null(*func.get_position()); + *item = Expression::Null(func.get_position()); } } Expression::ClassExpression(cls) => { @@ -95,7 +96,7 @@ impl VisitorMut, CheckingOutputWithoutDiagnostics> for Statemen *declaration = parser::Declaration::Variable( parser::declarations::VariableDeclaration::LetDeclaration { declarations: Vec::new(), - position: *func.get_position(), + position: func.get_position(), }, ) } @@ -106,14 +107,14 @@ impl VisitorMut, CheckingOutputWithoutDiagnostics> for Statemen parser::Declaration::Import(_) => { // TODO imported items } + parser::Declaration::Export(_) => { + // TODO exported items + } parser::Declaration::Enum(_) | parser::Declaration::Interface(_) | parser::Declaration::TypeAlias(_) | parser::Declaration::DeclareVariable(_) - | parser::Declaration::DeclareFunction(_) - | parser::Declaration::DeclareInterface(_) - | parser::Declaration::Namespace(_) - | parser::Declaration::Export(_) => {} + | parser::Declaration::Namespace(_) => {} } } }