Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(transformer): warn BigInt when targeting < ES2020 #7184

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions crates/oxc_diagnostics/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ pub use miette::{GraphicalReportHandler, GraphicalTheme, LabeledSpan, NamedSourc
/// Describes an error or warning that occurred.
///
/// Used by all oxc tools.
#[derive(Debug, Clone)]
#[derive(Debug, Clone, Eq, PartialEq)]
#[must_use]
pub struct OxcDiagnostic {
// `Box` the data to make `OxcDiagnostic` 8 bytes so that `Result` is small.
Expand All @@ -92,7 +92,7 @@ impl DerefMut for OxcDiagnostic {
}
}

#[derive(Debug, Default, Clone)]
#[derive(Debug, Default, Clone, Eq, PartialEq)]
pub struct OxcCode {
pub scope: Option<Cow<'static, str>>,
pub number: Option<Cow<'static, str>>,
Expand All @@ -114,7 +114,7 @@ impl fmt::Display for OxcCode {
}
}

#[derive(Debug, Clone)]
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct OxcDiagnosticInner {
pub message: Cow<'static, str>,
pub labels: Option<Vec<LabeledSpan>>,
Expand Down
15 changes: 14 additions & 1 deletion crates/oxc_transformer/src/es2020/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use oxc_ast::ast::*;
use oxc_diagnostics::OxcDiagnostic;
use oxc_traverse::{Traverse, TraverseCtx};

use crate::TransformCtx;
Expand All @@ -10,6 +11,8 @@ pub use nullish_coalescing_operator::NullishCoalescingOperator;
pub use options::ES2020Options;

pub struct ES2020<'a, 'ctx> {
ctx: &'ctx TransformCtx<'a>,

options: ES2020Options,

// Plugins
Expand All @@ -18,7 +21,7 @@ pub struct ES2020<'a, 'ctx> {

impl<'a, 'ctx> ES2020<'a, 'ctx> {
pub fn new(options: ES2020Options, ctx: &'ctx TransformCtx<'a>) -> Self {
Self { nullish_coalescing_operator: NullishCoalescingOperator::new(ctx), options }
Self { ctx, nullish_coalescing_operator: NullishCoalescingOperator::new(ctx), options }
}
}

Expand All @@ -28,4 +31,14 @@ impl<'a, 'ctx> Traverse<'a> for ES2020<'a, 'ctx> {
self.nullish_coalescing_operator.enter_expression(expr, ctx);
}
}

fn enter_big_int_literal(&mut self, node: &mut BigIntLiteral<'a>, _ctx: &mut TraverseCtx<'a>) {
if self.options.big_int {
let warning = OxcDiagnostic::warn(
"Big integer literals are not available in the configured target environment.",
)
.with_label(node.span);
self.ctx.error(warning);
}
}
}
3 changes: 3 additions & 0 deletions crates/oxc_transformer/src/es2020/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,7 @@ use serde::Deserialize;
pub struct ES2020Options {
#[serde(skip)]
pub nullish_coalescing_operator: bool,

#[serde(skip)]
pub big_int: bool,
}
4 changes: 4 additions & 0 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,10 @@ impl<'a, 'ctx> Traverse<'a> for TransformerImpl<'a, 'ctx> {
}
}

fn enter_big_int_literal(&mut self, node: &mut BigIntLiteral<'a>, ctx: &mut TraverseCtx<'a>) {
self.x2_es2020.enter_big_int_literal(node, ctx);
}

fn enter_binding_pattern(&mut self, pat: &mut BindingPattern<'a>, ctx: &mut TraverseCtx<'a>) {
if let Some(typescript) = self.x0_typescript.as_mut() {
typescript.enter_binding_pattern(pat, ctx);
Expand Down
12 changes: 10 additions & 2 deletions crates/oxc_transformer/src/options/env.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,11 @@ impl EnvOptions {
async_generator_functions: true,
},
es2019: ES2019Options { optional_catch_binding: true },
es2020: ES2020Options { nullish_coalescing_operator: true },
es2020: ES2020Options {
nullish_coalescing_operator: true,
// Turn this on would throw error for all bigints.
big_int: false,
},
es2021: ES2021Options { logical_assignment_operators: true },
es2022: ES2022Options {
class_static_block: true,
Expand Down Expand Up @@ -166,7 +170,10 @@ impl From<ESTarget> for EnvOptions {
async_generator_functions: target < ESTarget::ES2018,
},
es2019: ES2019Options { optional_catch_binding: target < ESTarget::ES2019 },
es2020: ES2020Options { nullish_coalescing_operator: target < ESTarget::ES2020 },
es2020: ES2020Options {
nullish_coalescing_operator: target < ESTarget::ES2020,
big_int: target < ESTarget::ES2020,
},
es2021: ES2021Options { logical_assignment_operators: target < ESTarget::ES2021 },
es2022: ES2022Options {
class_static_block: target < ESTarget::ES2022,
Expand Down Expand Up @@ -210,6 +217,7 @@ impl TryFrom<BabelEnvOptions> for EnvOptions {
},
es2020: ES2020Options {
nullish_coalescing_operator: o.can_enable(ES2020NullishCoalescingOperator),
big_int: o.can_enable(ES2020BigInt),
},
es2021: ES2021Options {
logical_assignment_operators: o.can_enable(ES2020LogicalAssignmentOperators),
Expand Down
18 changes: 18 additions & 0 deletions crates/oxc_transformer/src/options/es_features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ pub enum ESFeature {
ES2019OptionalChaining,
ES2020NullishCoalescingOperator,
ES2020LogicalAssignmentOperators,
ES2020BigInt,
ES2021NumericSeparator,
ES2022PrivateMethods,
ES2022ClassProperties,
Expand Down Expand Up @@ -670,6 +671,23 @@ pub fn features() -> &'static FxHashMap<ESFeature, EngineTargets> {
(Edge, Version(85u32, 0u32, 0u32)),
])),
),
(
ES2020BigInt,
EngineTargets::new(FxHashMap::from_iter([
(Chrome, Version(67u32, 0u32, 0u32)),
(Safari, Version(14u32, 0u32, 0u32)),
(OperaMobile, Version(48u32, 0u32, 0u32)),
(Samsung, Version(9u32, 0u32, 0u32)),
(Node, Version(10u32, 4u32, 0u32)),
(Rhino, Version(1u32, 7u32, 14u32)),
(Firefox, Version(68u32, 0u32, 0u32)),
(Deno, Version(1u32, 0u32, 0u32)),
(Electron, Version(4u32, 0u32, 0u32)),
(Opera, Version(54u32, 0u32, 0u32)),
(Ios, Version(14u32, 0u32, 0u32)),
(Edge, Version(79u32, 0u32, 0u32)),
])),
),
(
ES2021NumericSeparator,
EngineTargets::new(FxHashMap::from_iter([
Expand Down
1 change: 1 addition & 0 deletions crates/oxc_transformer/src/options/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ impl TryFrom<&BabelOptions> for TransformOptions {
let es2020 = ES2020Options {
nullish_coalescing_operator: options.plugins.nullish_coalescing_operator
|| env.es2020.nullish_coalescing_operator,
big_int: env.es2020.big_int,
};

let es2021 = ES2021Options {
Expand Down
23 changes: 16 additions & 7 deletions crates/oxc_transformer/tests/integrations/es_target.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,30 @@ fn es_target() {
("es2018", "try {} catch {}"),
("es2019", "a ?? b"),
("es2020", "a ||= b"),
("es2019", "1n ** 2n"), // test target error
("es2021", "class foo { static {} }"),
];

// Test no transformation for esnext.
for (_, case) in cases {
let options = TransformOptions::from(ESTarget::from_str("esnext").unwrap());
assert_eq!(codegen(case, SourceType::mjs()), test(case, options));
assert_eq!(Ok(codegen(case, SourceType::mjs())), test(case, options));
}

let snapshot = cases.iter().enumerate().fold(String::new(), |mut w, (i, (target, case))| {
let options = TransformOptions::from(ESTarget::from_str(target).unwrap());
let result = test(case, options);
write!(w, "########## {i} {target}\n{case}\n----------\n{result}\n").unwrap();
w
});
let snapshot =
cases.into_iter().enumerate().fold(String::new(), |mut w, (i, (target, case))| {
let options = TransformOptions::from(ESTarget::from_str(target).unwrap());
let result = match test(case, options) {
Ok(code) => code,
Err(errors) => errors
.into_iter()
.map(|err| format!("{:?}", err.with_source_code(case.to_string())))
.collect::<Vec<_>>()
.join("\n"),
};
write!(w, "########## {i} {target}\n{case}\n----------\n{result}\n").unwrap();
w
});

#[cfg(not(miri))]
{
Expand Down
16 changes: 12 additions & 4 deletions crates/oxc_transformer/tests/integrations/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use std::path::Path;

use oxc_allocator::Allocator;
use oxc_codegen::{CodeGenerator, CodegenOptions};
use oxc_diagnostics::OxcDiagnostic;
use oxc_parser::Parser;
use oxc_semantic::SemanticBuilder;
use oxc_span::SourceType;
Expand All @@ -20,20 +21,27 @@ pub fn codegen(source_text: &str, source_type: SourceType) -> String {
.code
}

pub(crate) fn test(source_text: &str, options: TransformOptions) -> String {
pub(crate) fn test(
source_text: &str,
options: TransformOptions,
) -> Result<String, Vec<OxcDiagnostic>> {
let source_type = SourceType::default();
let allocator = Allocator::default();
let ret = Parser::new(&allocator, source_text, source_type).parse();
let mut program = ret.program;
let (symbols, scopes) =
SemanticBuilder::new().build(&program).semantic.into_symbol_table_and_scope_tree();
Transformer::new(&allocator, Path::new(""), options).build_with_symbols_and_scopes(
let ret = Transformer::new(&allocator, Path::new(""), options).build_with_symbols_and_scopes(
symbols,
scopes,
&mut program,
);
CodeGenerator::new()
if !ret.errors.is_empty() {
return Err(ret.errors);
}
let code = CodeGenerator::new()
.with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() })
.build(&program)
.code
.code;
Ok(code)
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,26 @@ a ||= b
----------
a || (a = b);

########## 7 es2021
########## 7 es2019
1n ** 2n
----------

! Big integer literals are not available in the configured target
| environment.
,----
1 | 1n ** 2n
: ^^
`----


! Big integer literals are not available in the configured target
| environment.
,----
1 | 1n ** 2n
: ^^
`----

########## 8 es2021
class foo { static {} }
----------
class foo {
Expand Down
15 changes: 8 additions & 7 deletions crates/oxc_transformer/tests/integrations/targets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,15 @@ use oxc_transformer::{ESTarget, EnvOptions, TransformOptions};
#[test]
fn targets() {
let cases = [
("() => {}"),
("a ** b"),
// ("() => {}"),
// ("a ** b"),
// ("async function foo() {}"),
("({ ...x })"),
("try {} catch {}"),
("a ?? b"),
("a ||= b"),
// ("({ ...x })"),
// ("try {} catch {}"),
// ("a ?? b"),
// ("a ||= b"),
// ("class foo { static {} }"),
"1n ** 2n",
];

// Test no transformation for default targets.
Expand All @@ -21,7 +22,7 @@ fn targets() {
env: EnvOptions::from_browserslist_query("defaults").unwrap(),
..TransformOptions::default()
};
assert_eq!(codegen(case, SourceType::mjs()), test(case, options));
assert_eq!(Ok(codegen(case, SourceType::mjs())), test(case, options));
}

// Test transformation for very low targets.
Expand Down
22 changes: 22 additions & 0 deletions tasks/compat_data/data.json
Original file line number Diff line number Diff line change
Expand Up @@ -821,6 +821,28 @@
"electron": "10.0"
}
},
{
"name": "BigInt",
"babel": null,
"features": [
"BigInt / basic functionality"
],
"es": "ES2020",
"targets": {
"chrome": "67",
"opera": "54",
"edge": "79",
"firefox": "68",
"safari": "14",
"node": "10.4",
"deno": "1",
"ios": "14",
"samsung": "9",
"rhino": "1.7.14",
"opera_mobile": "48",
"electron": "4.0"
}
},
{
"name": "NumericSeparator",
"babel": "transform-numeric-separator",
Expand Down
5 changes: 5 additions & 0 deletions tasks/compat_data/es-features.js
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,11 @@ const es2020 = [
babel: 'transform-logical-assignment-operators',
features: ['Logical Assignment'],
},
{
name: 'BigInt',
babel: null,
features: ['BigInt / basic functionality'],
},
].map(f('ES2020'));

const es2021 = [
Expand Down
Loading