Skip to content

Commit

Permalink
feat(transformer): warn BigInt when targeting < ES2020 (#7184)
Browse files Browse the repository at this point in the history
closes #5822
  • Loading branch information
Boshen committed Nov 7, 2024
1 parent a579011 commit 22898c8
Show file tree
Hide file tree
Showing 13 changed files with 136 additions and 25 deletions.
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

0 comments on commit 22898c8

Please sign in to comment.