From b186666743ef9da901a85dfb67c5aaeaff28b303 Mon Sep 17 00:00:00 2001 From: Dunqing Date: Mon, 2 Sep 2024 23:57:10 +0800 Subject: [PATCH] fix(traverse): invalid variable name generated by generate_uid_based_on_node --- crates/oxc_traverse/src/context/identifier.rs | 58 +++++++++++++++++++ crates/oxc_traverse/src/context/mod.rs | 1 + crates/oxc_traverse/src/context/scoping.rs | 4 +- tasks/transform_conformance/oxc.snap.md | 11 +++- .../fixtures/invalid-variable-name/input.js | 1 + .../fixtures/invalid-variable-name/output.js | 2 + 6 files changed, 73 insertions(+), 4 deletions(-) create mode 100644 crates/oxc_traverse/src/context/identifier.rs create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/invalid-variable-name/input.js create mode 100644 tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/invalid-variable-name/output.js diff --git a/crates/oxc_traverse/src/context/identifier.rs b/crates/oxc_traverse/src/context/identifier.rs new file mode 100644 index 00000000000000..de8989021014be --- /dev/null +++ b/crates/oxc_traverse/src/context/identifier.rs @@ -0,0 +1,58 @@ +use std::borrow::Cow; + +use oxc_syntax::identifier::{is_identifier_name, is_identifier_part, is_identifier_start}; + +/// Convert a str to a valid identifier name. +/// +/// Based on Babel's [`toIdentifier`](https://github.com/babel/babel/blob/3bcfee232506a4cebe410f02042fb0f0adeeb0b1/packages/babel-types/src/converters/toIdentifier.ts#L4-L26) function. +pub fn to_identifier(input: &str) -> Cow { + if is_identifier_name(input) { + return Cow::Borrowed(input); + } + + let mut name = String::with_capacity(input.len()); + + let mut capitalize_next = false; + + let mut chars = input.chars(); + if let Some(first) = chars.next() { + if is_identifier_start(first) { + name.push(first); + } else { + capitalize_next = true; + } + } + + for c in chars { + if c == '-' || c.is_whitespace() || !is_identifier_part(c) { + capitalize_next = true; + } else if capitalize_next { + name.push(c.to_ascii_uppercase()); + capitalize_next = false; + } else { + name.push(c); + } + } + + if name.is_empty() { + return Cow::Borrowed("_"); + } + + Cow::Owned(name) +} + +#[test] +fn test() { + assert_eq!(to_identifier("foo"), "foo"); + assert_eq!(to_identifier("fooBar"), "fooBar"); + assert_eq!(to_identifier("fooBar1"), "fooBar1"); + + assert_eq!(to_identifier("foo-bar"), "fooBar"); + assert_eq!(to_identifier("foo bar"), "fooBar"); + assert_eq!(to_identifier("foo-bar-1"), "fooBar1"); + assert_eq!(to_identifier("1foo-bar"), "FooBar"); + assert_eq!(to_identifier("1-foo-bar"), "FooBar"); + assert_eq!(to_identifier("-- --"), "_"); + + assert_eq!(to_identifier("_output$headers$x-amzn-requestid"), "_output$headers$xAmznRequestid"); +} diff --git a/crates/oxc_traverse/src/context/mod.rs b/crates/oxc_traverse/src/context/mod.rs index 5714a0abe00f38..9ad60713c482bf 100644 --- a/crates/oxc_traverse/src/context/mod.rs +++ b/crates/oxc_traverse/src/context/mod.rs @@ -16,6 +16,7 @@ mod ancestry; mod ast_operations; use ancestry::PopToken; pub use ancestry::TraverseAncestry; +mod identifier; mod scoping; pub use scoping::TraverseScoping; diff --git a/crates/oxc_traverse/src/context/scoping.rs b/crates/oxc_traverse/src/context/scoping.rs index 05f54633b4cf8a..90421089b672db 100644 --- a/crates/oxc_traverse/src/context/scoping.rs +++ b/crates/oxc_traverse/src/context/scoping.rs @@ -11,7 +11,7 @@ use oxc_syntax::{ symbol::{SymbolFlags, SymbolId}, }; -use super::ast_operations::GatherNodeParts; +use super::{ast_operations::GatherNodeParts, identifier::to_identifier}; use crate::scopes_collector::ChildScopeCollector; /// Traverse scope context. @@ -242,7 +242,7 @@ impl TraverseScoping { parts.push_str(part); }); let name = if parts.is_empty() { "ref" } else { parts.trim_start_matches('_') }; - self.generate_uid(name, scope_id, flags) + self.generate_uid(&to_identifier(name.get(..20).unwrap_or(name)), scope_id, flags) } /// Generate UID in current scope based on node. diff --git a/tasks/transform_conformance/oxc.snap.md b/tasks/transform_conformance/oxc.snap.md index e7f97fc25cfeee..02ef86047f65a3 100644 --- a/tasks/transform_conformance/oxc.snap.md +++ b/tasks/transform_conformance/oxc.snap.md @@ -1,13 +1,20 @@ commit: 3bcfee23 -Passed: 10/38 +Passed: 10/39 # All Passed: * babel-plugin-transform-optional-catch-binding * babel-plugin-transform-arrow-functions -# babel-plugin-transform-nullish-coalescing-operator (0/1) +# babel-plugin-transform-nullish-coalescing-operator (0/2) +* invalid-variable-name/input.js + x Output mismatch + x Reference flags mismatch: + | after transform: ReferenceId(3): ReferenceFlags(Write) + | rebuilt : ReferenceId(0): ReferenceFlags(Read | Write) + + * transform-in-arrow-function-expression/input.js x Reference flags mismatch: | after transform: ReferenceId(3): ReferenceFlags(Write) diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/invalid-variable-name/input.js b/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/invalid-variable-name/input.js new file mode 100644 index 00000000000000..c5bfa77129a4ae --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/invalid-variable-name/input.js @@ -0,0 +1 @@ +output.headers["x-amzn-requestid"] ?? output.headers["x-amzn-request-id"] diff --git a/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/invalid-variable-name/output.js b/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/invalid-variable-name/output.js new file mode 100644 index 00000000000000..a6e5ac5db7c949 --- /dev/null +++ b/tasks/transform_conformance/tests/babel-plugin-transform-nullish-coalescing-operator/test/fixtures/invalid-variable-name/output.js @@ -0,0 +1,2 @@ +var _output$headers$xAmz; +(_output$headers$xAmz = output.headers["x-amzn-requestid"]) !== null && _output$headers$xAmz !== void 0 ? _output$headers$xAmz : output.headers["x-amzn-request-id"]; \ No newline at end of file