Skip to content

Commit

Permalink
Merge branch 'main' into 10-24-feat_traverse_implement_getaddress_for…
Browse files Browse the repository at this point in the history
…_ancestor_
  • Loading branch information
Dunqing authored Oct 25, 2024
2 parents 4459c95 + a73c5af commit 6e48978
Show file tree
Hide file tree
Showing 50 changed files with 1,193 additions and 307 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/release_types.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ jobs:
working-directory: npm/oxc-types
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
run: pnpm publish --provenance --access public
run: pnpm publish --tag latest --provenance --access public --no-git-checks
39 changes: 39 additions & 0 deletions crates/oxc_ast/src/ast_builder_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,45 @@ impl<'a> AstBuilder<'a> {
mem::replace(decl, empty_decl)
}

/// Move a formal parameters out by replacing it with an empty [`FormalParameters`].
#[inline]
pub fn move_formal_parameters(self, params: &mut FormalParameters<'a>) -> FormalParameters<'a> {
let empty_params = self.formal_parameters(Span::default(), params.kind, self.vec(), NONE);
mem::replace(params, empty_params)
}

/// Move a function body out by replacing it with an empty [`FunctionBody`].
#[inline]
pub fn move_function_body(self, body: &mut FunctionBody<'a>) -> FunctionBody<'a> {
let empty_body = self.function_body(Span::default(), self.vec(), self.vec());
mem::replace(body, empty_body)
}

/// Move a function out by replacing it with an empty [`Function`]
#[inline]
pub fn move_function(self, function: &mut Function<'a>) -> Function<'a> {
let params = self.formal_parameters(
Span::default(),
FormalParameterKind::FormalParameter,
self.vec(),
NONE,
);
let empty_function = self.function(
FunctionType::FunctionDeclaration,
Span::default(),
None,
false,
false,
false,
NONE,
NONE,
params,
NONE,
NONE,
);
mem::replace(function, empty_function)
}

/// Move an array element out by replacing it with an
/// [elision](ArrayExpressionElement::Elision).
pub fn move_array_expression_element(
Expand Down
7 changes: 4 additions & 3 deletions crates/oxc_ecmascript/src/constant_evaluation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use num_traits::{One, Zero};

use oxc_ast::ast::*;

use crate::{side_effects::MayHaveSideEffects, ToBigInt, ToInt32, ToJsString};
use crate::{side_effects::MayHaveSideEffects, ToBigInt, ToInt32, ToJsString, ToNumber};

pub use self::{is_litral_value::IsLiteralValue, value::ConstantValue, value_type::ValueType};

Expand Down Expand Up @@ -179,6 +179,7 @@ pub trait ConstantEvaluation<'a> {
Expression::UnaryExpression(e) => self.eval_unary_expression(e),
Expression::Identifier(ident) => self.resolve_binding(ident),
Expression::NumericLiteral(lit) => Some(ConstantValue::Number(lit.value)),
Expression::NullLiteral(_) => Some(ConstantValue::Null),
Expression::StringLiteral(lit) => {
Some(ConstantValue::String(Cow::Borrowed(lit.value.as_str())))
}
Expand Down Expand Up @@ -206,8 +207,8 @@ pub trait ConstantEvaluation<'a> {
if left_type.is_number() || right_type.is_number() {
let lval = self.eval_expression(left)?;
let rval = self.eval_expression(right)?;
let lnum = lval.into_number()?;
let rnum = rval.into_number()?;
let lnum = lval.to_number()?;
let rnum = rval.to_number()?;
return Some(ConstantValue::Number(lnum + rnum));
}
None
Expand Down
19 changes: 18 additions & 1 deletion crates/oxc_ecmascript/src/constant_evaluation/value.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
use core::f64;
use std::borrow::Cow;

use num_bigint::BigInt;

use crate::ToJsString;
use crate::{ToJsString, ToNumber};

#[derive(Debug, PartialEq)]
pub enum ConstantValue<'a> {
Expand All @@ -11,6 +12,7 @@ pub enum ConstantValue<'a> {
String(Cow<'a, str>),
Boolean(bool),
Undefined,
Null,
}

impl<'a> ConstantValue<'a> {
Expand Down Expand Up @@ -68,6 +70,21 @@ impl<'a> ToJsString<'a> for ConstantValue<'a> {
Self::String(s) => Some(s.clone()),
Self::Boolean(b) => Some(Cow::Borrowed(if *b { "true" } else { "false" })),
Self::Undefined => Some(Cow::Borrowed("undefined")),
Self::Null => Some(Cow::Borrowed("null")),
}
}
}

impl<'a> ToNumber<'a> for ConstantValue<'a> {
fn to_number(&self) -> Option<f64> {
use crate::StringToNumber;
match self {
Self::Number(n) => Some(*n),
Self::BigInt(_) => None,
Self::String(s) => Some(s.as_ref().string_to_number()),
Self::Boolean(true) => Some(1.0),
Self::Boolean(false) | Self::Null => Some(0.0),
Self::Undefined => Some(f64::NAN),
}
}
}
6 changes: 4 additions & 2 deletions crates/oxc_ecmascript/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ mod string_char_at;
mod string_char_code_at;
mod string_index_of;
mod string_last_index_of;
mod string_substring;
mod string_to_big_int;
mod string_to_number;
mod to_big_int;
Expand All @@ -30,6 +31,7 @@ pub use self::{
private_bound_identifiers::PrivateBoundIdentifiers, prop_name::PropName,
string_char_at::StringCharAt, string_char_code_at::StringCharCodeAt,
string_index_of::StringIndexOf, string_last_index_of::StringLastIndexOf,
string_to_big_int::StringToBigInt, string_to_number::StringToNumber, to_big_int::ToBigInt,
to_boolean::ToBoolean, to_int_32::ToInt32, to_number::ToNumber, to_string::ToJsString,
string_substring::StringSubstring, string_to_big_int::StringToBigInt,
string_to_number::StringToNumber, to_big_int::ToBigInt, to_boolean::ToBoolean,
to_int_32::ToInt32, to_number::ToNumber, to_string::ToJsString,
};
37 changes: 37 additions & 0 deletions crates/oxc_ecmascript/src/string_substring.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use crate::ToInt32;

pub trait StringSubstring {
/// `String.prototype.substring ( start , end ] )`
/// <https://tc39.es/ecma262/#sec-string.prototype.substring>
fn substring(&self, start: Option<f64>, end: Option<f64>) -> String;
}

impl StringSubstring for &str {
#[expect(clippy::cast_sign_loss)]
fn substring(&self, start: Option<f64>, end: Option<f64>) -> String {
let start = start.map_or(0, |x| x.to_int_32().max(0) as usize);
let end = end.map_or(usize::MAX, |x| x.to_int_32().max(0) as usize);
let start = start.min(self.len());
let end = end.min(self.len());
if start > end {
return String::new();
}
self.chars().skip(start).take(end - start).collect()
}
}

#[cfg(test)]
mod test {
#[test]
fn test_prototype_last_index_of() {
use super::StringSubstring;
assert_eq!("foo".substring(Some(1.0), None), "oo");
assert_eq!("foo".substring(Some(1.0), Some(2.0)), "o");
assert_eq!("foo".substring(Some(1.0), Some(1.0)), "");
assert_eq!("foo".substring(Some(1.0), Some(0.0)), "");
assert_eq!("foo".substring(Some(0.0), Some(0.0)), "");
assert_eq!("foo".substring(Some(0.0), Some(1.0)), "f");
assert_eq!("abc".substring(Some(0.0), Some(2.0)), "ab");
assert_eq!("abcde".substring(Some(0.0), Some(2.0)), "ab");
}
}
28 changes: 24 additions & 4 deletions crates/oxc_linter/src/rules/jsx_a11y/no_access_key.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ declare_oxc_lint!(
/// <div />
/// ```
NoAccessKey,
correctness
correctness,
suggestion,
);

impl Rule for NoAccessKey {
Expand All @@ -50,12 +51,17 @@ impl Rule for NoAccessKey {
{
match attr.value.as_ref() {
Some(JSXAttributeValue::StringLiteral(_)) => {
ctx.diagnostic(no_access_key_diagnostic(attr.span));
ctx.diagnostic_with_suggestion(no_access_key_diagnostic(attr.span), |fixer| {
fixer.delete(&attr.span)
});
}
Some(JSXAttributeValue::ExpressionContainer(container)) => {
if container.expression.is_expression() && !container.expression.is_undefined()
{
ctx.diagnostic(no_access_key_diagnostic(attr.span));
ctx.diagnostic_with_suggestion(
no_access_key_diagnostic(attr.span),
|fixer| fixer.delete(&attr.span),
);
}
}
_ => {}
Expand Down Expand Up @@ -84,5 +90,19 @@ fn test() {
r"<div accessKey={`${undefined}${undefined}`} />",
];

Tester::new(NoAccessKey::NAME, pass, fail).test_and_snapshot();
let fix = vec![
(r#"<div accesskey="h" />"#, r"<div />"),
(r#"<div accessKey="h" />"#, r"<div />"),
(r#"<div accessKey="h" {...props} />"#, r"<div {...props} />"),
(r#"<div acCesSKeY="y" />"#, r"<div />"),
(r#"<div accessKey={"y"} />"#, r"<div />"),
(r"<div accessKey={`${y}`} />", r"<div />"),
(r"<div accessKey={`${undefined}y${undefined}`} />", r"<div />"),
(r"<div accessKey={`This is ${bad}`} />", r"<div />"),
(r"<div accessKey={accessKey} />", r"<div />"),
(r"<div accessKey={`${undefined}`} />", r"<div />"),
(r"<div accessKey={`${undefined}${undefined}`} />", r"<div />"),
];

Tester::new(NoAccessKey::NAME, pass, fail).expect_fix(fix).test_and_snapshot();
}
36 changes: 30 additions & 6 deletions crates/oxc_linter/src/rules/react/iframe_missing_sandbox.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use oxc_macros::declare_oxc_lint;
use oxc_span::Span;
use phf::{phf_set, Set};

use crate::ast_util::is_method_call;
use crate::utils::{get_prop_value, has_jsx_prop_ignore_case, is_create_element_call};
use crate::{context::LintContext, rule::Rule, AstNode};

Expand Down Expand Up @@ -57,12 +58,19 @@ declare_oxc_lint!(
///
/// ### Why is this bad?
///
/// The sandbox attribute enables an extra set of restrictions for the content in the iframe. Using sandbox attribute is considered a good security practice.
/// To learn more about sandboxing, see [MDN's documentation on the `sandbox` attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox).
/// The sandbox attribute enables an extra set of restrictions for the
/// content in the iframe. Using sandbox attribute is considered a good
/// security practice. To learn more about sandboxing, see [MDN's
/// documentation on the `sandbox`
/// attribute](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox).
///
/// This rule checks all React `<iframe>` elements and verifies that there
/// is `sandbox` attribute and that it's value is valid. In addition to that
/// it also reports cases where attribute contains `allow-scripts` and
/// `allow-same-origin` at the same time as this combination allows the
/// embedded document to remove the sandbox attribute and bypass the
/// restrictions.
///
/// This rule checks all React `<iframe>` elements and verifies that there is `sandbox` attribute and that it's value is valid. In addition to that it also reports cases where attribute contains `allow-scripts` and `allow-same-origin` at the same time as this combination allows the embedded document to remove the sandbox attribute and bypass the restrictions.
/// ### Examples
///
/// Examples of **incorrect** code for this rule:
Expand All @@ -78,7 +86,7 @@ declare_oxc_lint!(
/// <iframe sandbox="allow-origin" />;
/// ```
IframeMissingSandbox,
correctness,
suspicious,
pending
);

Expand Down Expand Up @@ -113,6 +121,19 @@ impl Rule for IframeMissingSandbox {
return;
}

// ignore document.createElement, since sandbox attributes
// cannot be set here.
// NOTE: should come after cheaper checks
if is_method_call(
call_expr,
Some(&["document"]),
Some(&["createElement"]),
Some(1), // (el, options?)
Some(2),
) {
return;
}

if let Some(Argument::ObjectExpression(obj_expr)) = call_expr.arguments.get(1) {
obj_expr
.properties
Expand Down Expand Up @@ -212,6 +233,9 @@ fn test() {
r#"React.createElement("iframe", { sandbox: "allow-top-navigation-by-user-activation" })"#,
r#"React.createElement("iframe", { sandbox: "allow-forms allow-modals" })"#,
r#"React.createElement("iframe", { sandbox: "allow-popups allow-popups-to-escape-sandbox allow-pointer-lock allow-same-origin allow-top-navigation" })"#,
// not react
r#"const iframe = document.createElement("iframe");"#,
r#"let expandingList = document.createElement("ul", { is: "expanding-list" });"#,
];

let fail = vec![
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_linter/src/snapshots/no_invalid_regexp.snap
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ source: crates/oxc_linter/src/tester.rs
╰────

eslint(no-invalid-regexp): Invalid regular expression: Duplicated capturing group names
╭─[no_invalid_regexp.tsx:1:23]
╭─[no_invalid_regexp.tsx:1:16]
1new RegExp('(?<k>a)(?<k>b)')
·
·
╰────
9 changes: 5 additions & 4 deletions crates/oxc_minifier/src/ast_passes/peephole_fold_constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1174,26 +1174,27 @@ mod test {
}

#[test]
#[ignore]
fn test_fold_arithmetic2() {
test_same("x = y + 10 + 20");
test_same("x = y / 2 / 4");
test("x = y * 2.25 * 3", "x = y * 6.75");
// test("x = y * 2.25 * 3", "x = y * 6.75");
test_same("x = y * 2.25 * z * 3");
test_same("z = x * y");
test_same("x = y * 5");
test("x = y + (z * 24 * 60 * 60 * 1000)", "x = y + z * 864E5");
// test("x = y + (z * 24 * 60 * 60 * 1000)", "x = y + z * 864E5");
}

#[test]
#[ignore]
fn test_fold_arithmetic3() {
test("x = null * undefined", "x = NaN");
test("x = null * 1", "x = 0");
test("x = (null - 1) * 2", "x = -2");
test("x = (null + 1) * 2", "x = 2");
test("x = null ** 0", "x = 1");
test("x = (-0) ** 3", "x = -0");

test("x = 1 + null", "x = 1");
test("x = null + 1", "x = 1");
}

#[test]
Expand Down
Loading

0 comments on commit 6e48978

Please sign in to comment.