fix(swc): Fixes for typescript type checker (#1146)

swc_ecma_codegen:
 - Fix codegen of `TsConstructorSignature`.
 - Fix codegen of `TsIndexSignature`.
 - Fix codegen of type parameters in arrow expressions.
 - No panic on dummy span.

swc_ecma_parser:
 - Parse optoinal method correctly.

swc_ecma_transforms:
 - resolver: Handle type parameters in arrow expressions.
This commit is contained in:
강동윤 2020-12-27 18:18:30 +09:00 committed by GitHub
parent 08c5d83d20
commit 6941f29943
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 113 additions and 44 deletions

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_ecma_ast"
repository = "https://github.com/swc-project/swc.git"
version = "0.36.1"
version = "0.36.2"
[features]
default = []

View File

@ -21,12 +21,11 @@ pub struct Ident {
impl arbitrary::Arbitrary for Ident {
fn arbitrary(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<Self> {
let span = u.arbitrary()?;
let sym = loop {
let v = u.arbitrary::<String>()?;
if !v.is_empty() {
break v.into();
}
};
let sym = u.arbitrary::<String>()?;
if sym.is_empty() {
return Err(arbitrary::Error::NotEnoughData);
}
let sym = sym.into();
let type_ann = u.arbitrary()?;
let optional = u.arbitrary()?;

View File

@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs"]
license = "Apache-2.0/MIT"
name = "swc_ecma_codegen"
repository = "https://github.com/swc-project/swc.git"
version = "0.42.0"
version = "0.42.1"
[dependencies]
bitflags = "1"

View File

@ -663,9 +663,12 @@ impl<'a> Emitter<'a> {
_ => true,
};
emit!(node.type_params);
if parens {
punct!("(");
}
self.emit_list(node.span, Some(&node.params), ListFormat::CommaListElements)?;
if parens {
punct!(")");
@ -1614,15 +1617,19 @@ impl<'a> Emitter<'a> {
// Write a trailing comma, if requested.
let has_trailing_comma = format.contains(ListFormat::AllowTrailingComma) && {
match self.cm.span_to_snippet(parent_node) {
Ok(snippet) => {
if snippet.len() < 3 {
false
} else {
snippet[..snippet.len() - 1].trim().ends_with(',')
if parent_node.is_dummy() {
false
} else {
match self.cm.span_to_snippet(parent_node) {
Ok(snippet) => {
if snippet.len() < 3 {
false
} else {
snippet[..snippet.len() - 1].trim().ends_with(',')
}
}
_ => false,
}
_ => false,
}
};

View File

@ -82,7 +82,11 @@ impl<'a> Emitter<'a> {
fn emit_ts_constructor_signature_decl(&mut self, n: &TsConstructSignatureDecl) -> Result {
self.emit_leading_comments_of_pos(n.span().lo())?;
unimplemented!("emit_ts_constructor_signature_decl")
keyword!("constructor");
punct!("(");
self.emit_list(n.span, Some(&n.params), ListFormat::Parameters)?;
punct!(")");
}
#[emitter]
@ -171,7 +175,11 @@ impl<'a> Emitter<'a> {
fn emit_ts_export_assignment(&mut self, n: &TsExportAssignment) -> Result {
self.emit_leading_comments_of_pos(n.span().lo())?;
unimplemented!("emit_ts_export_assignment")
keyword!("export");
formatting_space!();
punct!("=");
formatting_space!();
emit!(n.expr);
}
#[emitter]
@ -252,10 +260,11 @@ impl<'a> Emitter<'a> {
self.emit_list(n.span, Some(&n.params), ListFormat::Parameters)?;
punct!("]");
punct!(":");
formatting_space!();
emit!(n.type_ann);
semi!();
if let Some(type_ann) = &n.type_ann {
punct!(":");
formatting_space!();
emit!(type_ann);
}
}
#[emitter]
@ -572,7 +581,8 @@ impl<'a> Emitter<'a> {
fn emit_ts_non_null_expr(&mut self, n: &TsNonNullExpr) -> Result {
self.emit_leading_comments_of_pos(n.span().lo())?;
unimplemented!("emit_ts_non_null_expr")
emit!(n.expr);
punct!("!")
}
#[emitter]

View File

@ -7,7 +7,7 @@ include = ["Cargo.toml", "src/**/*.rs", "examples/**/*.rs"]
license = "Apache-2.0/MIT"
name = "swc_ecma_parser"
repository = "https://github.com/swc-project/swc.git"
version = "0.44.0"
version = "0.44.1"
[features]
default = []

View File

@ -477,17 +477,22 @@ impl<'a, I: Tokens> Parser<I> {
);
}
let key = self.parse_class_prop_name()?;
let mut key = self.parse_class_prop_name()?;
let is_optional = self.input.syntax().typescript() && eat!('?');
let is_private = match key {
Either::Left(PrivateName { .. }) => true,
_ => false,
};
let is_simple = match key {
Either::Right(PropName::Ident(..)) => true,
_ => false,
let is_simple = {
match &mut key {
Either::Right(PropName::Ident(i)) => {
i.optional = is_optional;
true
}
_ => false,
}
};
let is_optional = self.input.syntax().typescript() && eat!('?');
if self.is_class_method()? {
// handle a(){} / get(){} / set(){} / async(){}

View File

@ -43,7 +43,7 @@
},
"value": "m",
"typeAnnotation": null,
"optional": false
"optional": true
},
"function": {
"params": [],

View File

@ -73,7 +73,7 @@
},
"value": "r2",
"typeAnnotation": null,
"optional": false
"optional": true
},
"value": null,
"typeAnnotation": {

View File

@ -73,7 +73,7 @@
},
"value": "x",
"typeAnnotation": null,
"optional": false
"optional": true
},
"value": null,
"typeAnnotation": null,

View File

@ -6,7 +6,7 @@ edition = "2018"
license = "Apache-2.0/MIT"
name = "swc_ecma_transforms"
repository = "https://github.com/swc-project/swc.git"
version = "0.31.0"
version = "0.31.1"
[features]
const-modules = ["dashmap"]

View File

@ -356,6 +356,18 @@ macro_rules! typed_ref {
};
}
macro_rules! typed_decl {
($name:ident, $T:ty) => {
fn $name(&mut self, node: &mut $T) {
if self.handle_types {
self.ident_type = IdentType::Binding;
self.in_type = true;
node.visit_mut_children_with(self)
}
}
};
}
macro_rules! noop {
($name:ident, $T:ty) => {
#[inline]
@ -401,7 +413,7 @@ impl<'a> VisitMut for Resolver<'a> {
typed_ref!(visit_mut_ts_tuple_type, TsTupleType);
typed_ref!(visit_mut_ts_intersection_type, TsIntersectionType);
typed_ref!(visit_mut_ts_type_ref, TsTypeRef);
typed!(visit_mut_ts_type_param_decl, TsTypeParamDecl);
typed_decl!(visit_mut_ts_type_param_decl, TsTypeParamDecl);
typed!(visit_mut_ts_enum_member, TsEnumMember);
typed!(visit_mut_ts_fn_param, TsFnParam);
typed!(visit_mut_ts_indexed_access_type, TsIndexedAccessType);
@ -434,9 +446,12 @@ impl<'a> VisitMut for Resolver<'a> {
return;
}
self.in_type = true;
self.visit_mut_binding_ident(&mut param.name, None);
param.name.visit_mut_with(self);
let ident_type = self.ident_type;
param.default.visit_mut_with(self);
param.constraint.visit_mut_with(self);
self.ident_type = ident_type;
}
fn visit_mut_ts_construct_signature_decl(&mut self, decl: &mut TsConstructSignatureDecl) {
@ -663,6 +678,8 @@ impl<'a> VisitMut for Resolver<'a> {
self.handle_types,
);
e.type_params.visit_mut_with(&mut folder);
let old_hoist = self.hoist;
let old = folder.ident_type;
folder.ident_type = IdentType::Binding;
@ -673,6 +690,8 @@ impl<'a> VisitMut for Resolver<'a> {
e.body.visit_mut_with(&mut folder);
e.return_type.visit_mut_with(&mut folder);
self.cur_defining = folder.cur_defining;
}

View File

@ -16,7 +16,10 @@ struct TsHygiene {
impl VisitMut for TsHygiene {
fn visit_mut_ident(&mut self, i: &mut Ident) {
i.type_ann.visit_mut_with(self);
if SyntaxContext::empty().apply_mark(self.top_level_mark) == i.span.ctxt {
println!("ts_hygiene: {} is top-level", i.sym);
return;
}
@ -32,12 +35,11 @@ impl VisitMut for TsHygiene {
}
}
fn visit_mut_ts_enum_member_id(&mut self, _: &mut TsEnumMemberId) {}
/// TODO: Handle tyep parameter correctly
fn visit_mut_ts_type_param(&mut self, _: &mut TsTypeParam) {}
fn visit_mut_prop_name(&mut self, _: &mut PropName) {}
fn visit_mut_ts_qualified_name(&mut self, q: &mut TsQualifiedName) {
q.left.visit_mut_with(self);
}
}
fn tr() -> impl Fold {
@ -1352,13 +1354,13 @@ const bar = {} as Foo;
",
"
enum Foo {
name,
string
name__0,
string__0
}
function foo() {
enum Foo__2 {
name,
string
name__0,
string__0
}
const foo__2 = {
} as Foo__2;
@ -1534,7 +1536,7 @@ to_ts!(
"#,
r#"
class PartWriter {
constructor(private writer__2: Deno.Writer., readonly boundary__2: string, public headers__2: Headers, isFirstBoundary__2: boolean){
constructor(private writer__2: Deno.Writer, readonly boundary__2: string, public headers__2: Headers, isFirstBoundary__2: boolean){
let buf__2 = "";
if (isFirstBoundary__2) {
buf__2 += `--${boundary__2}\r\n`;
@ -1770,6 +1772,33 @@ to!(
"#
);
to_ts!(
type_checker_001,
"
const assign = <T, K1 extends keyof T, K2 extends keyof T[K1]>(object: T, key1: K1, key2: K2) \
=> (value: T[K1][K2]) => object[key1][key2] = value;
",
"const assign = <T__2, K1__2 extends keyof T__2, K2__2 extends keyof T__2[K1__2]>(object__2: \
T__2, key1__2: K1__2, key2__2: K2__2)=>(value__3: \
T__2[K1__2][K2__2])=>object__2[key1__2][key2__2] = value__3"
);
to_ts!(
type_checker_002,
"
export declare function foo<T>(obj: T): T extends () => infer P ? P : never;
export function bar<T>(obj: T) {
return foo(obj);
}
",
"
export declare function foo<T__2>(obj__2: T__2): T__2 extends () => infer P ? P : never;
export function bar<T__3>(obj__3: T__3) {
return foo(obj__3);
}
"
);
to_ts!(
deno_lint_486,
"