diff --git a/crates/oxc_ast/src/ast_impl/js.rs b/crates/oxc_ast/src/ast_impl/js.rs index b75751eeda784..b22183b868f83 100644 --- a/crates/oxc_ast/src/ast_impl/js.rs +++ b/crates/oxc_ast/src/ast_impl/js.rs @@ -594,7 +594,7 @@ impl<'a> MemberExpression<'a> { /// - `a.b` would return `Some("b")` /// - `a["b"]` would return `Some("b")` /// - `a[b]` would return `None` - /// - `a.#b` would return `Some("b")` + /// - `a.#b` would return `None` pub fn static_property_name(&self) -> Option<&'a str> { match self { MemberExpression::ComputedMemberExpression(expr) => { @@ -702,6 +702,12 @@ impl<'a> StaticMemberExpression<'a> { return object; } } + + /// Returns the static property name of this static member expression, if it has one, along with the source code [`Span`], + /// or `None` otherwise. + pub fn static_property_info(&self) -> (Span, &'a str) { + (self.property.span, self.property.name.as_str()) + } } impl<'a> ChainElement<'a> { diff --git a/crates/oxc_ast/src/ast_kind_impl.rs b/crates/oxc_ast/src/ast_kind_impl.rs index b8f9994c9f978..91c1f7140c4d6 100644 --- a/crates/oxc_ast/src/ast_kind_impl.rs +++ b/crates/oxc_ast/src/ast_kind_impl.rs @@ -1,5 +1,5 @@ #![expect(missing_docs)] // FIXME -use oxc_span::Atom; +use oxc_span::{Atom, GetSpan}; use super::{AstKind, ast::*}; @@ -81,6 +81,28 @@ impl<'a> AstKind<'a> { } } + /// Returns whether this expression is a member expression, such as `obj.prop`, `obj["prop"]`, or `obj.#prop`. + pub fn is_member_expression_kind(&self) -> bool { + self.as_member_expression_kind().is_some() + } + + /// If this is some kind of member expression, returns it as a + /// [`MemberExpressionKind`]. Otherwise, returns `None`. + pub fn as_member_expression_kind(&self) -> Option> { + match self { + Self::ComputedMemberExpression(member_expr) => { + Some(MemberExpressionKind::Computed(member_expr)) + } + Self::StaticMemberExpression(member_expr) => { + Some(MemberExpressionKind::Static(member_expr)) + } + Self::PrivateFieldExpression(member_expr) => { + Some(MemberExpressionKind::PrivateField(member_expr)) + } + _ => None, + } + } + pub fn from_expression(e: &'a Expression<'a>) -> Self { match e { Expression::BooleanLiteral(e) => Self::BooleanLiteral(e), @@ -101,16 +123,16 @@ impl<'a> AstKind<'a> { Expression::CallExpression(e) => Self::CallExpression(e), Expression::ChainExpression(e) => Self::ChainExpression(e), Expression::ClassExpression(e) => Self::Class(e), + Expression::ComputedMemberExpression(e) => Self::ComputedMemberExpression(e), Expression::ConditionalExpression(e) => Self::ConditionalExpression(e), Expression::FunctionExpression(e) => Self::Function(e), Expression::ImportExpression(e) => Self::ImportExpression(e), Expression::LogicalExpression(e) => Self::LogicalExpression(e), - match_member_expression!(Expression) => { - Self::MemberExpression(e.to_member_expression()) - } Expression::NewExpression(e) => Self::NewExpression(e), Expression::ObjectExpression(e) => Self::ObjectExpression(e), Expression::ParenthesizedExpression(e) => Self::ParenthesizedExpression(e), + Expression::PrivateFieldExpression(e) => Self::PrivateFieldExpression(e), + Expression::StaticMemberExpression(e) => Self::StaticMemberExpression(e), Expression::SequenceExpression(e) => Self::SequenceExpression(e), Expression::TaggedTemplateExpression(e) => Self::TaggedTemplateExpression(e), Expression::ThisExpression(e) => Self::ThisExpression(e), @@ -220,7 +242,6 @@ impl AstKind<'_> { Self::ComputedMemberExpression(_) => "ComputedMemberExpression".into(), Self::ConditionalExpression(_) => "ConditionalExpression".into(), Self::LogicalExpression(_) => "LogicalExpression".into(), - Self::MemberExpression(_) => "MemberExpression".into(), Self::NewExpression(n) => { let callee = match &n.callee { Expression::Identifier(id) => Some(id.name.as_str()), @@ -233,6 +254,8 @@ impl AstKind<'_> { } Self::ObjectExpression(_) => "ObjectExpression".into(), Self::ParenthesizedExpression(_) => "ParenthesizedExpression".into(), + Self::PrivateFieldExpression(_) => "PrivateFieldExpression".into(), + Self::StaticMemberExpression(_) => "StaticMemberExpression".into(), Self::SequenceExpression(_) => "SequenceExpression".into(), Self::TaggedTemplateExpression(_) => "TaggedTemplateExpression".into(), Self::ThisExpression(_) => "ThisExpression".into(), @@ -388,3 +411,64 @@ impl AstKind<'_> { } } } + +/// This is a subset of [`AstKind`] that represents member expressions. +/// +/// Having a separate enum for this allows us to implement helpful methods that are specific to member expressions, +/// such as getting the property name or the object of the member expression. +pub enum MemberExpressionKind<'a> { + /// A static member expression, such as `obj.prop`. + Static(&'a StaticMemberExpression<'a>), + /// A computed member expression, such as `obj["prop"]`. + Computed(&'a ComputedMemberExpression<'a>), + /// A private field expression, such as `obj.#field`. + PrivateField(&'a PrivateFieldExpression<'a>), +} + +impl<'a> MemberExpressionKind<'a> { + /// Returns the property name of the member expression, otherwise `None`. + /// + /// Example: returns the `prop` in `obj.prop` or `obj["prop"]`. + pub fn static_property_name(&self) -> Option> { + match self { + Self::Computed(member_expr) => member_expr.static_property_name(), + Self::Static(member_expr) => Some(member_expr.property.name), + Self::PrivateField(_) => None, + } + } + + /// Returns the object of the member expression, otherwise `None`. + /// + /// Example: returns the `obj` in `obj.prop` or `obj["prop"]`. + pub fn object(&self) -> &Expression<'a> { + match self { + Self::Computed(member_expr) => &member_expr.object, + Self::Static(member_expr) => &member_expr.object, + Self::PrivateField(member_expr) => &member_expr.object, + } + } + + /// Returns whether the member expression is optional, i.e. if it uses the + /// optional chaining operator (`?.`). + /// + /// Example: + /// - Returns `true` for `obj?.prop` or `obj?.["prop"]`. + /// - Returns `false` for `obj.prop` or `obj["prop"]`. + pub fn optional(&self) -> bool { + match self { + Self::Computed(member_expr) => member_expr.optional, + Self::Static(member_expr) => member_expr.optional, + Self::PrivateField(member_expr) => member_expr.optional, + } + } +} + +impl GetSpan for MemberExpressionKind<'_> { + fn span(&self) -> Span { + match self { + Self::Computed(member_expr) => member_expr.span, + Self::Static(member_expr) => member_expr.span, + Self::PrivateField(member_expr) => member_expr.span, + } + } +} diff --git a/crates/oxc_ast/src/generated/ast_kind.rs b/crates/oxc_ast/src/generated/ast_kind.rs index 0a22b04febc79..d6d44f216b7fb 100644 --- a/crates/oxc_ast/src/generated/ast_kind.rs +++ b/crates/oxc_ast/src/generated/ast_kind.rs @@ -27,175 +27,176 @@ pub enum AstType { PropertyKey = 10, TemplateLiteral = 11, TaggedTemplateExpression = 12, - MemberExpression = 13, - ComputedMemberExpression = 14, - CallExpression = 15, - NewExpression = 16, - MetaProperty = 17, - SpreadElement = 18, - Argument = 19, - UpdateExpression = 20, - UnaryExpression = 21, - BinaryExpression = 22, - PrivateInExpression = 23, - LogicalExpression = 24, - ConditionalExpression = 25, - AssignmentExpression = 26, - AssignmentTarget = 27, - SimpleAssignmentTarget = 28, - AssignmentTargetPattern = 29, - ArrayAssignmentTarget = 30, - ObjectAssignmentTarget = 31, - AssignmentTargetWithDefault = 32, - SequenceExpression = 33, - Super = 34, - AwaitExpression = 35, - ChainExpression = 36, - ParenthesizedExpression = 37, - Directive = 38, - Hashbang = 39, - BlockStatement = 40, - VariableDeclaration = 41, - VariableDeclarator = 42, - EmptyStatement = 43, - ExpressionStatement = 44, - IfStatement = 45, - DoWhileStatement = 46, - WhileStatement = 47, - ForStatement = 48, - ForInStatement = 49, - ForOfStatement = 50, - ContinueStatement = 51, - BreakStatement = 52, - ReturnStatement = 53, - WithStatement = 54, - SwitchStatement = 55, - SwitchCase = 56, - LabeledStatement = 57, - ThrowStatement = 58, - TryStatement = 59, - CatchClause = 60, - CatchParameter = 61, - DebuggerStatement = 62, - AssignmentPattern = 63, - ObjectPattern = 64, - ArrayPattern = 65, - BindingRestElement = 66, - Function = 67, - FormalParameters = 68, - FormalParameter = 69, - FunctionBody = 70, - ArrowFunctionExpression = 71, - YieldExpression = 72, - Class = 73, - ClassBody = 74, - MethodDefinition = 75, - PropertyDefinition = 76, - PrivateIdentifier = 77, - StaticBlock = 78, - ModuleDeclaration = 79, - AccessorProperty = 80, - ImportExpression = 81, - ImportDeclaration = 82, - ImportSpecifier = 83, - ImportDefaultSpecifier = 84, - ImportNamespaceSpecifier = 85, - WithClause = 86, - ImportAttribute = 87, - ExportNamedDeclaration = 88, - ExportDefaultDeclaration = 89, - ExportAllDeclaration = 90, - ExportSpecifier = 91, - V8IntrinsicExpression = 92, - BooleanLiteral = 93, - NullLiteral = 94, - NumericLiteral = 95, - StringLiteral = 96, - BigIntLiteral = 97, - RegExpLiteral = 98, - JSXElement = 99, - JSXOpeningElement = 100, - JSXClosingElement = 101, - JSXFragment = 102, - JSXOpeningFragment = 103, - JSXClosingFragment = 104, - JSXNamespacedName = 105, - JSXMemberExpression = 106, - JSXExpressionContainer = 107, - JSXEmptyExpression = 108, - JSXAttribute = 109, - JSXSpreadAttribute = 110, - JSXIdentifier = 111, - JSXSpreadChild = 112, - JSXText = 113, - TSThisParameter = 114, - TSEnumDeclaration = 115, - TSEnumBody = 116, - TSEnumMember = 117, - TSTypeAnnotation = 118, - TSLiteralType = 119, - TSConditionalType = 120, - TSUnionType = 121, - TSIntersectionType = 122, - TSParenthesizedType = 123, - TSTypeOperator = 124, - TSArrayType = 125, - TSIndexedAccessType = 126, - TSTupleType = 127, - TSNamedTupleMember = 128, - TSOptionalType = 129, - TSRestType = 130, - TSAnyKeyword = 131, - TSStringKeyword = 132, - TSBooleanKeyword = 133, - TSNumberKeyword = 134, - TSNeverKeyword = 135, - TSIntrinsicKeyword = 136, - TSUnknownKeyword = 137, - TSNullKeyword = 138, - TSUndefinedKeyword = 139, - TSVoidKeyword = 140, - TSSymbolKeyword = 141, - TSThisType = 142, - TSObjectKeyword = 143, - TSBigIntKeyword = 144, - TSTypeReference = 145, - TSTypeName = 146, - TSQualifiedName = 147, - TSTypeParameterInstantiation = 148, - TSTypeParameter = 149, - TSTypeParameterDeclaration = 150, - TSTypeAliasDeclaration = 151, - TSClassImplements = 152, - TSInterfaceDeclaration = 153, - TSPropertySignature = 154, - TSCallSignatureDeclaration = 155, - TSMethodSignature = 156, - TSConstructSignatureDeclaration = 157, - TSIndexSignatureName = 158, - TSInterfaceHeritage = 159, - TSTypePredicate = 160, - TSModuleDeclaration = 161, - TSModuleBlock = 162, - TSTypeLiteral = 163, - TSInferType = 164, - TSTypeQuery = 165, - TSImportType = 166, - TSMappedType = 167, - TSTemplateLiteralType = 168, - TSAsExpression = 169, - TSSatisfiesExpression = 170, - TSTypeAssertion = 171, - TSImportEqualsDeclaration = 172, - TSExternalModuleReference = 173, - TSNonNullExpression = 174, - Decorator = 175, - TSExportAssignment = 176, - TSNamespaceExportDeclaration = 177, - TSInstantiationExpression = 178, - JSDocNullableType = 179, - JSDocNonNullableType = 180, - JSDocUnknownType = 181, + ComputedMemberExpression = 13, + StaticMemberExpression = 14, + PrivateFieldExpression = 15, + CallExpression = 16, + NewExpression = 17, + MetaProperty = 18, + SpreadElement = 19, + Argument = 20, + UpdateExpression = 21, + UnaryExpression = 22, + BinaryExpression = 23, + PrivateInExpression = 24, + LogicalExpression = 25, + ConditionalExpression = 26, + AssignmentExpression = 27, + AssignmentTarget = 28, + SimpleAssignmentTarget = 29, + AssignmentTargetPattern = 30, + ArrayAssignmentTarget = 31, + ObjectAssignmentTarget = 32, + AssignmentTargetWithDefault = 33, + SequenceExpression = 34, + Super = 35, + AwaitExpression = 36, + ChainExpression = 37, + ParenthesizedExpression = 38, + Directive = 39, + Hashbang = 40, + BlockStatement = 41, + VariableDeclaration = 42, + VariableDeclarator = 43, + EmptyStatement = 44, + ExpressionStatement = 45, + IfStatement = 46, + DoWhileStatement = 47, + WhileStatement = 48, + ForStatement = 49, + ForInStatement = 50, + ForOfStatement = 51, + ContinueStatement = 52, + BreakStatement = 53, + ReturnStatement = 54, + WithStatement = 55, + SwitchStatement = 56, + SwitchCase = 57, + LabeledStatement = 58, + ThrowStatement = 59, + TryStatement = 60, + CatchClause = 61, + CatchParameter = 62, + DebuggerStatement = 63, + AssignmentPattern = 64, + ObjectPattern = 65, + ArrayPattern = 66, + BindingRestElement = 67, + Function = 68, + FormalParameters = 69, + FormalParameter = 70, + FunctionBody = 71, + ArrowFunctionExpression = 72, + YieldExpression = 73, + Class = 74, + ClassBody = 75, + MethodDefinition = 76, + PropertyDefinition = 77, + PrivateIdentifier = 78, + StaticBlock = 79, + ModuleDeclaration = 80, + AccessorProperty = 81, + ImportExpression = 82, + ImportDeclaration = 83, + ImportSpecifier = 84, + ImportDefaultSpecifier = 85, + ImportNamespaceSpecifier = 86, + WithClause = 87, + ImportAttribute = 88, + ExportNamedDeclaration = 89, + ExportDefaultDeclaration = 90, + ExportAllDeclaration = 91, + ExportSpecifier = 92, + V8IntrinsicExpression = 93, + BooleanLiteral = 94, + NullLiteral = 95, + NumericLiteral = 96, + StringLiteral = 97, + BigIntLiteral = 98, + RegExpLiteral = 99, + JSXElement = 100, + JSXOpeningElement = 101, + JSXClosingElement = 102, + JSXFragment = 103, + JSXOpeningFragment = 104, + JSXClosingFragment = 105, + JSXNamespacedName = 106, + JSXMemberExpression = 107, + JSXExpressionContainer = 108, + JSXEmptyExpression = 109, + JSXAttribute = 110, + JSXSpreadAttribute = 111, + JSXIdentifier = 112, + JSXSpreadChild = 113, + JSXText = 114, + TSThisParameter = 115, + TSEnumDeclaration = 116, + TSEnumBody = 117, + TSEnumMember = 118, + TSTypeAnnotation = 119, + TSLiteralType = 120, + TSConditionalType = 121, + TSUnionType = 122, + TSIntersectionType = 123, + TSParenthesizedType = 124, + TSTypeOperator = 125, + TSArrayType = 126, + TSIndexedAccessType = 127, + TSTupleType = 128, + TSNamedTupleMember = 129, + TSOptionalType = 130, + TSRestType = 131, + TSAnyKeyword = 132, + TSStringKeyword = 133, + TSBooleanKeyword = 134, + TSNumberKeyword = 135, + TSNeverKeyword = 136, + TSIntrinsicKeyword = 137, + TSUnknownKeyword = 138, + TSNullKeyword = 139, + TSUndefinedKeyword = 140, + TSVoidKeyword = 141, + TSSymbolKeyword = 142, + TSThisType = 143, + TSObjectKeyword = 144, + TSBigIntKeyword = 145, + TSTypeReference = 146, + TSTypeName = 147, + TSQualifiedName = 148, + TSTypeParameterInstantiation = 149, + TSTypeParameter = 150, + TSTypeParameterDeclaration = 151, + TSTypeAliasDeclaration = 152, + TSClassImplements = 153, + TSInterfaceDeclaration = 154, + TSPropertySignature = 155, + TSCallSignatureDeclaration = 156, + TSMethodSignature = 157, + TSConstructSignatureDeclaration = 158, + TSIndexSignatureName = 159, + TSInterfaceHeritage = 160, + TSTypePredicate = 161, + TSModuleDeclaration = 162, + TSModuleBlock = 163, + TSTypeLiteral = 164, + TSInferType = 165, + TSTypeQuery = 166, + TSImportType = 167, + TSMappedType = 168, + TSTemplateLiteralType = 169, + TSAsExpression = 170, + TSSatisfiesExpression = 171, + TSTypeAssertion = 172, + TSImportEqualsDeclaration = 173, + TSExternalModuleReference = 174, + TSNonNullExpression = 175, + Decorator = 176, + TSExportAssignment = 177, + TSNamespaceExportDeclaration = 178, + TSInstantiationExpression = 179, + JSDocNullableType = 180, + JSDocNonNullableType = 181, + JSDocUnknownType = 182, } /// Untyped AST Node Kind @@ -216,9 +217,10 @@ pub enum AstKind<'a> { TemplateLiteral(&'a TemplateLiteral<'a>) = AstType::TemplateLiteral as u8, TaggedTemplateExpression(&'a TaggedTemplateExpression<'a>) = AstType::TaggedTemplateExpression as u8, - MemberExpression(&'a MemberExpression<'a>) = AstType::MemberExpression as u8, ComputedMemberExpression(&'a ComputedMemberExpression<'a>) = AstType::ComputedMemberExpression as u8, + StaticMemberExpression(&'a StaticMemberExpression<'a>) = AstType::StaticMemberExpression as u8, + PrivateFieldExpression(&'a PrivateFieldExpression<'a>) = AstType::PrivateFieldExpression as u8, CallExpression(&'a CallExpression<'a>) = AstType::CallExpression as u8, NewExpression(&'a NewExpression<'a>) = AstType::NewExpression as u8, MetaProperty(&'a MetaProperty<'a>) = AstType::MetaProperty as u8, @@ -430,8 +432,9 @@ impl GetSpan for AstKind<'_> { Self::PropertyKey(it) => it.span(), Self::TemplateLiteral(it) => it.span(), Self::TaggedTemplateExpression(it) => it.span(), - Self::MemberExpression(it) => it.span(), Self::ComputedMemberExpression(it) => it.span(), + Self::StaticMemberExpression(it) => it.span(), + Self::PrivateFieldExpression(it) => it.span(), Self::CallExpression(it) => it.span(), Self::NewExpression(it) => it.span(), Self::MetaProperty(it) => it.span(), @@ -619,8 +622,9 @@ impl GetAddress for AstKind<'_> { Self::PropertyKey(it) => it.address(), Self::TemplateLiteral(it) => Address::from_ptr(it), Self::TaggedTemplateExpression(it) => Address::from_ptr(it), - Self::MemberExpression(it) => it.address(), Self::ComputedMemberExpression(it) => Address::from_ptr(it), + Self::StaticMemberExpression(it) => Address::from_ptr(it), + Self::PrivateFieldExpression(it) => Address::from_ptr(it), Self::CallExpression(it) => Address::from_ptr(it), Self::NewExpression(it) => Address::from_ptr(it), Self::MetaProperty(it) => Address::from_ptr(it), @@ -859,13 +863,18 @@ impl<'a> AstKind<'a> { } #[inline] - pub fn as_member_expression(self) -> Option<&'a MemberExpression<'a>> { - if let Self::MemberExpression(v) = self { Some(v) } else { None } + pub fn as_computed_member_expression(self) -> Option<&'a ComputedMemberExpression<'a>> { + if let Self::ComputedMemberExpression(v) = self { Some(v) } else { None } } #[inline] - pub fn as_computed_member_expression(self) -> Option<&'a ComputedMemberExpression<'a>> { - if let Self::ComputedMemberExpression(v) = self { Some(v) } else { None } + pub fn as_static_member_expression(self) -> Option<&'a StaticMemberExpression<'a>> { + if let Self::StaticMemberExpression(v) = self { Some(v) } else { None } + } + + #[inline] + pub fn as_private_field_expression(self) -> Option<&'a PrivateFieldExpression<'a>> { + if let Self::PrivateFieldExpression(v) = self { Some(v) } else { None } } #[inline] diff --git a/crates/oxc_ast/src/lib.rs b/crates/oxc_ast/src/lib.rs index 31d9e4fcf7473..7a0cb92a1c4be 100644 --- a/crates/oxc_ast/src/lib.rs +++ b/crates/oxc_ast/src/lib.rs @@ -76,6 +76,7 @@ pub use crate::{ ast::comment::{Comment, CommentContent, CommentKind, CommentPosition}, ast_builder_impl::{AstBuilder, NONE}, ast_kind::{AstKind, AstType}, + ast_kind_impl::MemberExpressionKind, trivia::{CommentsRange, comments_range, has_comments_between}, }; diff --git a/crates/oxc_ast_visit/src/generated/visit.rs b/crates/oxc_ast_visit/src/generated/visit.rs index a78974372f594..a5cafc6091a6b 100644 --- a/crates/oxc_ast_visit/src/generated/visit.rs +++ b/crates/oxc_ast_visit/src/generated/visit.rs @@ -1568,8 +1568,7 @@ pub mod walk { #[inline] pub fn walk_member_expression<'a, V: Visit<'a>>(visitor: &mut V, it: &MemberExpression<'a>) { - let kind = AstKind::MemberExpression(visitor.alloc(it)); - visitor.enter_node(kind); + // No `AstKind` for this type match it { MemberExpression::ComputedMemberExpression(it) => { visitor.visit_computed_member_expression(it) @@ -1581,7 +1580,6 @@ pub mod walk { visitor.visit_private_field_expression(it) } } - visitor.leave_node(kind); } #[inline] @@ -1602,10 +1600,12 @@ pub mod walk { visitor: &mut V, it: &StaticMemberExpression<'a>, ) { - // No `AstKind` for this type + let kind = AstKind::StaticMemberExpression(visitor.alloc(it)); + visitor.enter_node(kind); visitor.visit_span(&it.span); visitor.visit_expression(&it.object); visitor.visit_identifier_name(&it.property); + visitor.leave_node(kind); } #[inline] @@ -1613,10 +1613,12 @@ pub mod walk { visitor: &mut V, it: &PrivateFieldExpression<'a>, ) { - // No `AstKind` for this type + let kind = AstKind::PrivateFieldExpression(visitor.alloc(it)); + visitor.enter_node(kind); visitor.visit_span(&it.span); visitor.visit_expression(&it.object); visitor.visit_private_identifier(&it.field); + visitor.leave_node(kind); } #[inline] diff --git a/crates/oxc_ast_visit/src/generated/visit_mut.rs b/crates/oxc_ast_visit/src/generated/visit_mut.rs index f49472bc878f8..4790b0af5059c 100644 --- a/crates/oxc_ast_visit/src/generated/visit_mut.rs +++ b/crates/oxc_ast_visit/src/generated/visit_mut.rs @@ -1584,8 +1584,7 @@ pub mod walk_mut { visitor: &mut V, it: &mut MemberExpression<'a>, ) { - let kind = AstType::MemberExpression; - visitor.enter_node(kind); + // No `AstType` for this type match it { MemberExpression::ComputedMemberExpression(it) => { visitor.visit_computed_member_expression(it) @@ -1597,7 +1596,6 @@ pub mod walk_mut { visitor.visit_private_field_expression(it) } } - visitor.leave_node(kind); } #[inline] @@ -1618,10 +1616,12 @@ pub mod walk_mut { visitor: &mut V, it: &mut StaticMemberExpression<'a>, ) { - // No `AstType` for this type + let kind = AstType::StaticMemberExpression; + visitor.enter_node(kind); visitor.visit_span(&mut it.span); visitor.visit_expression(&mut it.object); visitor.visit_identifier_name(&mut it.property); + visitor.leave_node(kind); } #[inline] @@ -1629,10 +1629,12 @@ pub mod walk_mut { visitor: &mut V, it: &mut PrivateFieldExpression<'a>, ) { - // No `AstType` for this type + let kind = AstType::PrivateFieldExpression; + visitor.enter_node(kind); visitor.visit_span(&mut it.span); visitor.visit_expression(&mut it.object); visitor.visit_private_identifier(&mut it.field); + visitor.leave_node(kind); } #[inline] diff --git a/crates/oxc_formatter/src/generated/ast_nodes.rs b/crates/oxc_formatter/src/generated/ast_nodes.rs index 73e4fb8c87f74..f9d35a2ba34c4 100644 --- a/crates/oxc_formatter/src/generated/ast_nodes.rs +++ b/crates/oxc_formatter/src/generated/ast_nodes.rs @@ -40,8 +40,9 @@ pub enum AstNodes<'a> { PropertyKey(&'a AstNode<'a, PropertyKey<'a>>), TemplateLiteral(&'a AstNode<'a, TemplateLiteral<'a>>), TaggedTemplateExpression(&'a AstNode<'a, TaggedTemplateExpression<'a>>), - MemberExpression(&'a AstNode<'a, MemberExpression<'a>>), ComputedMemberExpression(&'a AstNode<'a, ComputedMemberExpression<'a>>), + StaticMemberExpression(&'a AstNode<'a, StaticMemberExpression<'a>>), + PrivateFieldExpression(&'a AstNode<'a, PrivateFieldExpression<'a>>), CallExpression(&'a AstNode<'a, CallExpression<'a>>), NewExpression(&'a AstNode<'a, NewExpression<'a>>), MetaProperty(&'a AstNode<'a, MetaProperty<'a>>), @@ -2322,8 +2323,9 @@ impl<'a> AstNodes<'a> { Self::PropertyKey(n) => n.span(), Self::TemplateLiteral(n) => n.span(), Self::TaggedTemplateExpression(n) => n.span(), - Self::MemberExpression(n) => n.span(), Self::ComputedMemberExpression(n) => n.span(), + Self::StaticMemberExpression(n) => n.span(), + Self::PrivateFieldExpression(n) => n.span(), Self::CallExpression(n) => n.span(), Self::NewExpression(n) => n.span(), Self::MetaProperty(n) => n.span(), @@ -2510,8 +2512,9 @@ impl<'a> AstNodes<'a> { Self::PropertyKey(n) => n.parent, Self::TemplateLiteral(n) => n.parent, Self::TaggedTemplateExpression(n) => n.parent, - Self::MemberExpression(n) => n.parent, Self::ComputedMemberExpression(n) => n.parent, + Self::StaticMemberExpression(n) => n.parent, + Self::PrivateFieldExpression(n) => n.parent, Self::CallExpression(n) => n.parent, Self::NewExpression(n) => n.parent, Self::MetaProperty(n) => n.parent, @@ -2698,8 +2701,9 @@ impl<'a> AstNodes<'a> { Self::PropertyKey(n) => n.parent.as_sibling_node(), Self::TemplateLiteral(n) => SiblingNode::from(n.inner), Self::TaggedTemplateExpression(n) => SiblingNode::from(n.inner), - Self::MemberExpression(n) => n.parent.as_sibling_node(), Self::ComputedMemberExpression(n) => SiblingNode::from(n.inner), + Self::StaticMemberExpression(n) => SiblingNode::from(n.inner), + Self::PrivateFieldExpression(n) => SiblingNode::from(n.inner), Self::CallExpression(n) => SiblingNode::from(n.inner), Self::NewExpression(n) => SiblingNode::from(n.inner), Self::MetaProperty(n) => SiblingNode::from(n.inner), @@ -2886,8 +2890,9 @@ impl<'a> AstNodes<'a> { Self::PropertyKey(_) => "PropertyKey", Self::TemplateLiteral(_) => "TemplateLiteral", Self::TaggedTemplateExpression(_) => "TaggedTemplateExpression", - Self::MemberExpression(_) => "MemberExpression", Self::ComputedMemberExpression(_) => "ComputedMemberExpression", + Self::StaticMemberExpression(_) => "StaticMemberExpression", + Self::PrivateFieldExpression(_) => "PrivateFieldExpression", Self::CallExpression(_) => "CallExpression", Self::NewExpression(_) => "NewExpression", Self::MetaProperty(_) => "MetaProperty", @@ -3493,12 +3498,15 @@ impl<'a> AstNode<'a, Expression<'a>> { })) } it @ match_member_expression!(Expression) => { - AstNodes::MemberExpression(self.allocator.alloc(AstNode { - inner: it.to_member_expression(), - parent, - allocator: self.allocator, - following_node: self.following_node, - })) + return self + .allocator + .alloc(AstNode { + inner: it.to_member_expression(), + parent, + allocator: self.allocator, + following_node: self.following_node, + }) + .as_ast_nodes(); } }; self.allocator.alloc(node) @@ -3889,7 +3897,7 @@ impl<'a> AstNode<'a, TemplateElement<'a>> { impl<'a> AstNode<'a, MemberExpression<'a>> { #[inline] pub fn as_ast_nodes(&self) -> &AstNodes<'a> { - let parent = self.allocator.alloc(AstNodes::MemberExpression(transmute_self(self))); + let parent = self.parent; let node = match self.inner { MemberExpression::ComputedMemberExpression(s) => { AstNodes::ComputedMemberExpression(self.allocator.alloc(AstNode { @@ -3900,14 +3908,20 @@ impl<'a> AstNode<'a, MemberExpression<'a>> { })) } MemberExpression::StaticMemberExpression(s) => { - panic!( - "No kind for current enum variant yet, please see `tasks/ast_tools/src/generators/ast_kind.rs`" - ) + AstNodes::StaticMemberExpression(self.allocator.alloc(AstNode { + inner: s.as_ref(), + parent, + allocator: self.allocator, + following_node: self.following_node, + })) } MemberExpression::PrivateFieldExpression(s) => { - panic!( - "No kind for current enum variant yet, please see `tasks/ast_tools/src/generators/ast_kind.rs`" - ) + AstNodes::PrivateFieldExpression(self.allocator.alloc(AstNode { + inner: s.as_ref(), + parent, + allocator: self.allocator, + following_node: self.following_node, + })) } }; self.allocator.alloc(node) @@ -3967,7 +3981,7 @@ impl<'a> AstNode<'a, StaticMemberExpression<'a>> { self.allocator.alloc(AstNode { inner: &self.inner.object, allocator: self.allocator, - parent: self.parent, + parent: self.allocator.alloc(AstNodes::StaticMemberExpression(transmute_self(self))), following_node, }) } @@ -3978,7 +3992,7 @@ impl<'a> AstNode<'a, StaticMemberExpression<'a>> { self.allocator.alloc(AstNode { inner: &self.inner.property, allocator: self.allocator, - parent: self.parent, + parent: self.allocator.alloc(AstNodes::StaticMemberExpression(transmute_self(self))), following_node, }) } @@ -4001,7 +4015,7 @@ impl<'a> AstNode<'a, PrivateFieldExpression<'a>> { self.allocator.alloc(AstNode { inner: &self.inner.object, allocator: self.allocator, - parent: self.parent, + parent: self.allocator.alloc(AstNodes::PrivateFieldExpression(transmute_self(self))), following_node, }) } @@ -4012,7 +4026,7 @@ impl<'a> AstNode<'a, PrivateFieldExpression<'a>> { self.allocator.alloc(AstNode { inner: &self.inner.field, allocator: self.allocator, - parent: self.parent, + parent: self.allocator.alloc(AstNodes::PrivateFieldExpression(transmute_self(self))), following_node, }) } @@ -4530,12 +4544,15 @@ impl<'a> AstNode<'a, SimpleAssignmentTarget<'a>> { })) } it @ match_member_expression!(SimpleAssignmentTarget) => { - AstNodes::MemberExpression(self.allocator.alloc(AstNode { - inner: it.to_member_expression(), - parent, - allocator: self.allocator, - following_node: self.following_node, - })) + return self + .allocator + .alloc(AstNode { + inner: it.to_member_expression(), + parent, + allocator: self.allocator, + following_node: self.following_node, + }) + .as_ast_nodes(); } }; self.allocator.alloc(node) @@ -4907,12 +4924,15 @@ impl<'a> AstNode<'a, ChainElement<'a>> { })) } it @ match_member_expression!(ChainElement) => { - AstNodes::MemberExpression(self.allocator.alloc(AstNode { - inner: it.to_member_expression(), - parent, - allocator: self.allocator, - following_node: self.following_node, - })) + return self + .allocator + .alloc(AstNode { + inner: it.to_member_expression(), + parent, + allocator: self.allocator, + following_node: self.following_node, + }) + .as_ast_nodes(); } }; self.allocator.alloc(node) diff --git a/crates/oxc_formatter/src/generated/format.rs b/crates/oxc_formatter/src/generated/format.rs index dcfd3e0ae49ac..4823f439f47ad 100644 --- a/crates/oxc_formatter/src/generated/format.rs +++ b/crates/oxc_formatter/src/generated/format.rs @@ -235,6 +235,13 @@ impl<'a> Format<'a> for AstNode<'a, TemplateElement<'a>> { impl<'a> Format<'a> for AstNode<'a, MemberExpression<'a>> { fn fmt(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> { + self.write(f) + } +} + +impl<'a> Format<'a> for AstNode<'a, ComputedMemberExpression<'a>> { + fn fmt(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> { + format_leading_comments(self.span).fmt(f)?; let needs_parentheses = self.needs_parentheses(f); if needs_parentheses { "(".fmt(f)?; @@ -243,11 +250,17 @@ impl<'a> Format<'a> for AstNode<'a, MemberExpression<'a>> { if needs_parentheses { ")".fmt(f)?; } + format_trailing_comments( + &self.parent.as_sibling_node(), + &SiblingNode::from(self.inner), + self.following_node.as_ref(), + ) + .fmt(f)?; result } } -impl<'a> Format<'a> for AstNode<'a, ComputedMemberExpression<'a>> { +impl<'a> Format<'a> for AstNode<'a, StaticMemberExpression<'a>> { fn fmt(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> { format_leading_comments(self.span).fmt(f)?; let needs_parentheses = self.needs_parentheses(f); @@ -268,15 +281,24 @@ impl<'a> Format<'a> for AstNode<'a, ComputedMemberExpression<'a>> { } } -impl<'a> Format<'a> for AstNode<'a, StaticMemberExpression<'a>> { - fn fmt(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> { - self.write(f) - } -} - impl<'a> Format<'a> for AstNode<'a, PrivateFieldExpression<'a>> { fn fmt(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> { - self.write(f) + format_leading_comments(self.span).fmt(f)?; + let needs_parentheses = self.needs_parentheses(f); + if needs_parentheses { + "(".fmt(f)?; + } + let result = self.write(f); + if needs_parentheses { + ")".fmt(f)?; + } + format_trailing_comments( + &self.parent.as_sibling_node(), + &SiblingNode::from(self.inner), + self.following_node.as_ref(), + ) + .fmt(f)?; + result } } diff --git a/crates/oxc_formatter/src/generated/format_write.rs b/crates/oxc_formatter/src/generated/format_write.rs index 408dd279cc42d..13fa1732242af 100644 --- a/crates/oxc_formatter/src/generated/format_write.rs +++ b/crates/oxc_formatter/src/generated/format_write.rs @@ -459,7 +459,7 @@ impl<'a> FormatWrite<'a> for AstNode<'a, MemberExpression<'a>> { #[inline] fn write(&self, f: &mut Formatter<'_, 'a>) -> FormatResult<()> { let allocator = self.allocator; - let parent = allocator.alloc(AstNodes::MemberExpression(transmute_self(self))); + let parent = self.parent; match self.inner { MemberExpression::ComputedMemberExpression(inner) => allocator .alloc(AstNode:: { diff --git a/crates/oxc_formatter/src/parentheses/expression.rs b/crates/oxc_formatter/src/parentheses/expression.rs index 7ab5b85b3a127..2a1e806cb56e2 100644 --- a/crates/oxc_formatter/src/parentheses/expression.rs +++ b/crates/oxc_formatter/src/parentheses/expression.rs @@ -59,8 +59,9 @@ impl<'a> NeedsParentheses<'a> for AstNode<'a, Expression<'a>> { AstNodes::TSNonNullExpression(it) => it.needs_parentheses(f), AstNodes::TSInstantiationExpression(it) => it.needs_parentheses(f), AstNodes::V8IntrinsicExpression(it) => it.needs_parentheses(f), - AstNodes::MemberExpression(it) => it.needs_parentheses(f), + AstNodes::StaticMemberExpression(it) => it.needs_parentheses(f), AstNodes::ComputedMemberExpression(it) => it.needs_parentheses(f), + AstNodes::PrivateFieldExpression(it) => it.needs_parentheses(f), _ => { // TODO: incomplete false @@ -71,10 +72,8 @@ impl<'a> NeedsParentheses<'a> for AstNode<'a, Expression<'a>> { impl<'a> NeedsParentheses<'a> for AstNode<'a, NumericLiteral<'a>> { fn needs_parentheses(&self, f: &Formatter<'_, 'a>) -> bool { - if let AstNodes::MemberExpression(member) = self.parent { - if let MemberExpression::StaticMemberExpression(e) = member.as_ref() { - return e.object.without_parentheses().span() == self.span(); - } + if let AstNodes::StaticMemberExpression(member) = self.parent { + return member.object.without_parentheses().span() == self.span(); } false } @@ -417,12 +416,15 @@ impl<'a> NeedsParentheses<'a> for AstNode<'a, TSNonNullExpression<'a>> { impl<'a> NeedsParentheses<'a> for AstNode<'a, TSInstantiationExpression<'a>> { fn needs_parentheses(&self, f: &Formatter<'_, 'a>) -> bool { - if let AstNodes::MemberExpression(e) = self.parent { - return e.object().without_parentheses().span() == self.span(); + if let AstNodes::StaticMemberExpression(e) = self.parent { + return e.object.without_parentheses().span() == self.span(); } if let AstNodes::ComputedMemberExpression(e) = self.parent { return e.object.without_parentheses().span() == self.span(); } + if let AstNodes::PrivateFieldExpression(e) = self.parent { + return e.object.without_parentheses().span() == self.span(); + } false } } @@ -438,8 +440,9 @@ fn binary_like_needs_parens(binary_like: BinaryLikeExpression<'_, '_>) -> bool { | AstNodes::SpreadElement(_) | AstNodes::CallExpression(_) | AstNodes::NewExpression(_) - | AstNodes::MemberExpression(_) + | AstNodes::StaticMemberExpression(_) | AstNodes::ComputedMemberExpression(_) + | AstNodes::PrivateFieldExpression(_) | AstNodes::TaggedTemplateExpression(_) => return true, AstNodes::BinaryExpression(binary) => BinaryLikeExpression::BinaryExpression(binary), AstNodes::LogicalExpression(logical) => BinaryLikeExpression::LogicalExpression(logical), @@ -533,8 +536,8 @@ fn update_or_lower_expression_needs_parens(span: Span, parent: &AstNodes<'_>) -> ) { return true; } - if let AstNodes::MemberExpression(member_expr) = parent { - return member_expr.object().get_inner_expression().span() == span; + if let AstNodes::StaticMemberExpression(member_expr) = parent { + return member_expr.object.get_inner_expression().span() == span; } if let AstNodes::ComputedMemberExpression(computed_member_expr) = parent { return computed_member_expr.object.get_inner_expression().span() == span; diff --git a/crates/oxc_formatter/src/write/binary_like_expression.rs b/crates/oxc_formatter/src/write/binary_like_expression.rs index b19112934090b..059331f8e5126 100644 --- a/crates/oxc_formatter/src/write/binary_like_expression.rs +++ b/crates/oxc_formatter/src/write/binary_like_expression.rs @@ -204,7 +204,8 @@ impl<'a> Format<'a> for BinaryLikeExpression<'a, '_> { // Add a group with a soft block indent in cases where it is necessary to parenthesize the binary expression. // For example, `(a+b)(call)`, `!(a + b)`, `(a + b).test`. let is_inside_parenthesis = match parent { - AstNodes::MemberExpression(_) + AstNodes::StaticMemberExpression(_) + | AstNodes::PrivateFieldExpression(_) | AstNodes::ComputedMemberExpression(_) | AstNodes::UnaryExpression(_) => true, AstNodes::CallExpression(call) => { diff --git a/crates/oxc_linter/src/ast_util.rs b/crates/oxc_linter/src/ast_util.rs index 5c6533d5cf9e9..58b949064ee8a 100644 --- a/crates/oxc_linter/src/ast_util.rs +++ b/crates/oxc_linter/src/ast_util.rs @@ -719,9 +719,14 @@ pub fn is_default_this_binding<'a>( return !is_constructor; } - AstKind::MemberExpression(mem_expr) => { - if mem_expr.object().span() == current_node.span() - && matches!(mem_expr.static_property_name(), Some("apply" | "bind" | "call")) + mem_expr if mem_expr.is_member_expression_kind() => { + let Some(member_expr_kind) = mem_expr.as_member_expression_kind() else { + return false; + }; + if member_expr_kind.object().span() == current_node.span() + && member_expr_kind + .static_property_name() + .is_some_and(|name| name == "apply" || name == "bind" || name == "call") { let node = outermost_paren_parent(parent, semantic).unwrap(); if let AstKind::CallExpression(call_expr) = node.kind() { diff --git a/crates/oxc_linter/src/rules/eslint/no_caller.rs b/crates/oxc_linter/src/rules/eslint/no_caller.rs index 5bb9c84bbb3dc..de626caacc73d 100644 --- a/crates/oxc_linter/src/rules/eslint/no_caller.rs +++ b/crates/oxc_linter/src/rules/eslint/no_caller.rs @@ -1,4 +1,4 @@ -use oxc_ast::{AstKind, ast::MemberExpression}; +use oxc_ast::AstKind; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::Span; @@ -74,9 +74,7 @@ declare_oxc_lint!( impl Rule for NoCaller { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - if let AstKind::MemberExpression(MemberExpression::StaticMemberExpression(expr)) = - node.kind() - { + if let AstKind::StaticMemberExpression(expr) = node.kind() { if (expr.property.name == "callee" || expr.property.name == "caller") && expr.object.is_specific_id("arguments") { diff --git a/crates/oxc_linter/src/rules/eslint/no_console.rs b/crates/oxc_linter/src/rules/eslint/no_console.rs index 175b1ccb1a8b5..3957c52a8984a 100644 --- a/crates/oxc_linter/src/rules/eslint/no_console.rs +++ b/crates/oxc_linter/src/rules/eslint/no_console.rs @@ -106,8 +106,13 @@ impl Rule for NoConsole { } fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::MemberExpression(member_expr) = node.kind() else { return }; - let Expression::Identifier(ident) = member_expr.object() else { + let object = match node.kind() { + AstKind::StaticMemberExpression(member_expr) => &member_expr.object, + AstKind::ComputedMemberExpression(member_expr) => &member_expr.object, + _ => return, + }; + + let Expression::Identifier(ident) = object else { return; }; @@ -117,25 +122,34 @@ impl Rule for NoConsole { return; } - if let Some((mem_span, prop_name)) = member_expr.static_property_info() { - if self.allow.iter().any(|allowed_name| allowed_name == prop_name) { - return; + let (mem_span, prop_name) = match node.kind() { + AstKind::StaticMemberExpression(member_expr) => member_expr.static_property_info(), + AstKind::ComputedMemberExpression(member_expr) => { + match member_expr.static_property_info() { + Some(info) => info, + None => return, + } } + _ => unreachable!(), + }; - let diagnostic_span = ident.span().merge(mem_span); + if self.allow.iter().any(|allowed_name| allowed_name == prop_name) { + return; + } + + let diagnostic_span = ident.span().merge(mem_span); - ctx.diagnostic_with_suggestion( - no_console_diagnostic(diagnostic_span, &self.allow), - |fixer| { - if let Some(parent) = ctx.nodes().parent_node(node.id()) { - if let AstKind::CallExpression(_) = parent.kind() { - return remove_console(fixer, ctx, parent); - } + ctx.diagnostic_with_suggestion( + no_console_diagnostic(diagnostic_span, &self.allow), + |fixer| { + if let Some(parent) = ctx.nodes().parent_node(node.id()) { + if let AstKind::CallExpression(_) = parent.kind() { + return remove_console(fixer, ctx, parent); } - fixer.noop() - }, - ); - } + } + fixer.noop() + }, + ); } } diff --git a/crates/oxc_linter/src/rules/eslint/no_eval.rs b/crates/oxc_linter/src/rules/eslint/no_eval.rs index cf4c0989e181a..79db1b0292679 100644 --- a/crates/oxc_linter/src/rules/eslint/no_eval.rs +++ b/crates/oxc_linter/src/rules/eslint/no_eval.rs @@ -138,21 +138,33 @@ impl Rule for NoEval { ctx.diagnostic(no_eval_diagnostic(node.span())); } } else { - while let Some(mem_expr) = parent.kind().as_member_expression() { - if mem_expr.static_property_name().is_some_and(|p| p == name) { - parent = Self::outermost_mem_expr(parent, ctx).unwrap(); - } else { - break; + loop { + match parent.kind() { + AstKind::StaticMemberExpression(mem_expr) => { + if mem_expr.property.name == name { + parent = Self::outermost_mem_expr(parent, ctx).unwrap(); + } else { + break; + } + } + AstKind::ComputedMemberExpression(computed_mem_expr) => { + if computed_mem_expr + .static_property_name() + .is_some_and(|p| p == name) + { + parent = Self::outermost_mem_expr(parent, ctx).unwrap(); + } else { + break; + } + } + _ => break, } } match parent.kind() { - AstKind::MemberExpression(mem_expr) => { - let Some((span, name)) = mem_expr.static_property_info() else { - continue; - }; - if name == "eval" { - ctx.diagnostic(no_eval_diagnostic(span)); + AstKind::StaticMemberExpression(mem_expr) => { + if mem_expr.property.name == "eval" { + ctx.diagnostic(no_eval_diagnostic(mem_expr.property.span)); } } AstKind::ComputedMemberExpression(comp_mem_expr) => { @@ -173,8 +185,19 @@ impl Rule for NoEval { } AstKind::ThisExpression(_) if !self.allow_indirect => { let parent = ctx.nodes().parent_node(node.id()).unwrap(); - let Some(mem_expr) = parent.kind().as_member_expression() else { return }; - let Some((span, name)) = mem_expr.static_property_info() else { return }; + let property_info = match parent.kind() { + AstKind::StaticMemberExpression(mem_expr) => { + Some(mem_expr.static_property_info()) + } + AstKind::ComputedMemberExpression(comp_mem_expr) => { + comp_mem_expr.static_property_info() + } + _ => None, + }; + + let Some((span, name)) = property_info else { + return; + }; if name == "eval" { let scope_id = diff --git a/crates/oxc_linter/src/rules/eslint/no_extend_native.rs b/crates/oxc_linter/src/rules/eslint/no_extend_native.rs index 956d87344a66a..88c0a9af063ec 100644 --- a/crates/oxc_linter/src/rules/eslint/no_extend_native.rs +++ b/crates/oxc_linter/src/rules/eslint/no_extend_native.rs @@ -210,25 +210,10 @@ fn get_prototype_property_accessed<'a>( let parent = ctx.nodes().parent_node(node.id())?; let mut prototype_node = Some(parent); match parent.kind() { - AstKind::ComputedMemberExpression(computed_prop_access_expr) => { - let prop_name = computed_prop_access_expr.static_property_name()?; - if prop_name != "prototype" { - return None; - } - - let grandparent_node = ctx.nodes().parent_node(parent.id())?; - - if let AstKind::ChainExpression(_) = grandparent_node.kind() { - prototype_node = Some(grandparent_node); - if let Some(grandparent_parent) = ctx.nodes().parent_node(grandparent_node.id()) { - prototype_node = Some(grandparent_parent); - } - } - - prototype_node - } - AstKind::MemberExpression(prop_access_expr) => { - let prop_name = prop_access_expr.static_property_name()?; + prop_access_expr if prop_access_expr.is_member_expression_kind() => { + let prop_name = prop_access_expr + .as_member_expression_kind() + .and_then(|m| m.static_property_name())?; if prop_name != "prototype" { return None; } diff --git a/crates/oxc_linter/src/rules/eslint/no_import_assign.rs b/crates/oxc_linter/src/rules/eslint/no_import_assign.rs index 742fd0902c47f..53a8b34523a81 100644 --- a/crates/oxc_linter/src/rules/eslint/no_import_assign.rs +++ b/crates/oxc_linter/src/rules/eslint/no_import_assign.rs @@ -61,7 +61,8 @@ impl Rule for NoImportAssign { let Some(parent_node) = ctx.nodes().parent_node(reference.node_id()) else { return; }; - if let AstKind::MemberExpression(expr) = parent_node.kind() { + if parent_node.kind().is_member_expression_kind() { + let expr = parent_node.kind(); let Some(parent_parent_node) = ctx.nodes().parent_node(parent_node.id()) else { return; @@ -74,7 +75,15 @@ impl Rule for NoImportAssign { // delete namespace?.module || matches!(parent_parent_kind, AstKind::ChainExpression(_) if ctx.nodes().parent_kind(parent_parent_node.id()).is_some_and(is_unary_expression_with_delete_operator)) { - if let Some((span, _)) = expr.static_property_info() { + if let Some((span, _)) = match expr { + AstKind::StaticMemberExpression(expr) => { + Some(expr.static_property_info()) + } + AstKind::ComputedMemberExpression(expr) => { + expr.static_property_info() + } + _ => return, + } { if span != ctx.semantic().reference_span(reference) { return ctx .diagnostic(no_import_assign_diagnostic(expr.span())); diff --git a/crates/oxc_linter/src/rules/eslint/no_iterator.rs b/crates/oxc_linter/src/rules/eslint/no_iterator.rs index 491f35a4037d6..e85de358dea7f 100644 --- a/crates/oxc_linter/src/rules/eslint/no_iterator.rs +++ b/crates/oxc_linter/src/rules/eslint/no_iterator.rs @@ -1,4 +1,3 @@ -use oxc_ast::AstKind; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; @@ -62,7 +61,7 @@ declare_oxc_lint!( impl Rule for NoIterator { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::MemberExpression(member_expression) = node.kind() else { + let Some(member_expression) = node.kind().as_member_expression_kind() else { return; }; if let Some(static_property_name) = member_expression.static_property_name() { diff --git a/crates/oxc_linter/src/rules/eslint/no_new_func.rs b/crates/oxc_linter/src/rules/eslint/no_new_func.rs index d9e57ce9bb6ee..19a904f48bb34 100644 --- a/crates/oxc_linter/src/rules/eslint/no_new_func.rs +++ b/crates/oxc_linter/src/rules/eslint/no_new_func.rs @@ -1,4 +1,4 @@ -use oxc_ast::AstKind; +use oxc_ast::{AstKind, ast::IdentifierReference}; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_semantic::IsGlobalReference; @@ -47,22 +47,26 @@ declare_oxc_lint!( impl Rule for NoNewFunc { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let id_and_span = match node.kind() { + match node.kind() { AstKind::NewExpression(new_expr) => { let Some(id) = new_expr.callee.get_identifier_reference() else { return; }; - Some((id, new_expr.span)) + check(id, new_expr.span, ctx); } AstKind::CallExpression(call_expr) => { let Some(obj_id) = call_expr.callee.get_identifier_reference() else { return; }; - Some((obj_id, call_expr.span)) + check(obj_id, call_expr.span, ctx); } - AstKind::MemberExpression(mem_expr) => { + member_expr if member_expr.is_member_expression_kind() => { + let Some(member_expr) = member_expr.as_member_expression_kind() else { + return; + }; + let parent: Option> = ctx.nodes().ancestor_kinds(node.id()).skip(1).find(|node| { !matches!( @@ -75,7 +79,9 @@ impl Rule for NoNewFunc { return; }; - let Some(static_property_name) = &mem_expr.static_property_name() else { + let Some(static_property_name) = + &member_expr.static_property_name().map(|s| s.as_str()) + else { return; }; @@ -83,23 +89,23 @@ impl Rule for NoNewFunc { return; } - let Some(obj_id) = mem_expr.object().get_identifier_reference() else { + let Some(obj_id) = member_expr.object().get_identifier_reference() else { return; }; - Some((obj_id, parent_call_expr.span)) - } - _ => None, - }; - - if let Some((id, span)) = id_and_span { - if id.is_global_reference_name("Function", ctx.scoping()) { - ctx.diagnostic(no_new_func(span)); + check(obj_id, parent_call_expr.span, ctx); } + _ => {} } } } +fn check(ident: &IdentifierReference, span: Span, ctx: &LintContext) { + if ident.is_global_reference_name("Function", ctx.scoping()) { + ctx.diagnostic(no_new_func(span)); + } +} + #[test] fn test() { use crate::tester::Tester; diff --git a/crates/oxc_linter/src/rules/eslint/no_proto.rs b/crates/oxc_linter/src/rules/eslint/no_proto.rs index cf21a756006f2..891041a41a83c 100644 --- a/crates/oxc_linter/src/rules/eslint/no_proto.rs +++ b/crates/oxc_linter/src/rules/eslint/no_proto.rs @@ -1,4 +1,3 @@ -use oxc_ast::AstKind; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; @@ -47,13 +46,12 @@ declare_oxc_lint!( impl Rule for NoProto { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::MemberExpression(member_expression) = node.kind() else { + let Some(member_expr) = node.kind().as_member_expression_kind() else { return; }; - if let Some(static_property_name) = member_expression.static_property_name() { - if static_property_name == "__proto__" { - ctx.diagnostic(no_proto_diagnostic(member_expression.span())); - } + + if member_expr.static_property_name().is_some_and(|name| name == "__proto__") { + ctx.diagnostic(no_proto_diagnostic(member_expr.span())); } } } diff --git a/crates/oxc_linter/src/rules/eslint/no_unexpected_multiline.rs b/crates/oxc_linter/src/rules/eslint/no_unexpected_multiline.rs index 58d08991f319a..96ae0f5328a46 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unexpected_multiline.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unexpected_multiline.rs @@ -135,12 +135,12 @@ impl Rule for NoUnexpectedMultiline { ); } } - AstKind::MemberExpression(member_expr) => { - if !member_expr.is_computed() || member_expr.optional() { + AstKind::ComputedMemberExpression(member_expr) => { + if member_expr.optional { return; } - let span = Span::new(member_expr.object().span().end, member_expr.span().end); + let span = Span::new(member_expr.object.span().end, member_expr.span().end); if let Some(open_bracket_pos) = has_newline_before(ctx, span, b'[') { let bracket_span = Span::sized(span.start + open_bracket_pos, 1); diff --git a/crates/oxc_linter/src/rules/eslint/no_unsafe_optional_chaining.rs b/crates/oxc_linter/src/rules/eslint/no_unsafe_optional_chaining.rs index 7f5da93ffcde8..f77f7428a7ed8 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unsafe_optional_chaining.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unsafe_optional_chaining.rs @@ -74,8 +74,14 @@ impl Rule for NoUnsafeOptionalChaining { AstKind::CallExpression(expr) if !expr.optional => { Self::check_unsafe_usage(&expr.callee, ctx); } - AstKind::MemberExpression(expr) if !expr.optional() => { - Self::check_unsafe_usage(expr.object(), ctx); + AstKind::StaticMemberExpression(expr) if !expr.optional => { + Self::check_unsafe_usage(&expr.object, ctx); + } + AstKind::ComputedMemberExpression(expr) if !expr.optional => { + Self::check_unsafe_usage(&expr.object, ctx); + } + AstKind::PrivateFieldExpression(expr) if !expr.optional => { + Self::check_unsafe_usage(&expr.object, ctx); } AstKind::TaggedTemplateExpression(expr) => { Self::check_unsafe_usage(&expr.tag, ctx); diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_private_class_members.rs b/crates/oxc_linter/src/rules/eslint/no_unused_private_class_members.rs index 709dd7d78f121..c5ecc76badc55 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_private_class_members.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_private_class_members.rs @@ -118,8 +118,10 @@ fn is_read(current_node_id: NodeId, nodes: &AstNodes) -> bool { .tuple_windows::<(&AstNode<'_>, &AstNode<'_>)>() { match (curr.kind(), parent.kind()) { + (member_expr, AstKind::AssignmentTarget(_) | AstKind::SimpleAssignmentTarget(_)) + if member_expr.is_member_expression_kind() => {} ( - AstKind::SimpleAssignmentTarget(_) | AstKind::MemberExpression(_), + AstKind::SimpleAssignmentTarget(_), AstKind::AssignmentTarget(_) | AstKind::SimpleAssignmentTarget(_), ) => {} ( diff --git a/crates/oxc_linter/src/rules/eslint/prefer_rest_params.rs b/crates/oxc_linter/src/rules/eslint/prefer_rest_params.rs index 4de39dfebe6f3..75468e8b7309d 100644 --- a/crates/oxc_linter/src/rules/eslint/prefer_rest_params.rs +++ b/crates/oxc_linter/src/rules/eslint/prefer_rest_params.rs @@ -95,8 +95,8 @@ fn is_inside_of_function(node: &AstNode, ctx: &LintContext) -> bool { fn is_not_normal_member_access(identifier: &AstNode, ctx: &LintContext) -> bool { let parent = ctx.nodes().parent_node(identifier.id()); if let Some(parent) = parent { - if let AstKind::MemberExpression(member) = parent.kind() { - return member.object().span() == identifier.span() && !member.is_computed(); + if let AstKind::StaticMemberExpression(member) = parent.kind() { + return member.object.span() == identifier.span(); } } false diff --git a/crates/oxc_linter/src/rules/import/namespace.rs b/crates/oxc_linter/src/rules/import/namespace.rs index 2896c0c5a1b49..3c336db97c3ef 100644 --- a/crates/oxc_linter/src/rules/import/namespace.rs +++ b/crates/oxc_linter/src/rules/import/namespace.rs @@ -170,7 +170,7 @@ impl Rule for Namespace { let name = entry.local_name.name(); match node.kind() { - AstKind::ComputedMemberExpression(member) => { + member if member.is_member_expression_kind() => { if matches!( ctx.nodes().parent_kind(node.id()), Some(AstKind::SimpleAssignmentTarget(_)) @@ -178,7 +178,9 @@ impl Rule for Namespace { ctx.diagnostic(assignment(member.span(), name)); } - if !self.allow_computed { + if !self.allow_computed + && matches!(member, AstKind::ComputedMemberExpression(_)) + { return ctx.diagnostic(computed_reference(member.span(), name)); } @@ -190,22 +192,6 @@ impl Rule for Namespace { ctx, ); } - AstKind::MemberExpression(member) => { - if matches!( - ctx.nodes().parent_kind(node.id()), - Some(AstKind::SimpleAssignmentTarget(_)) - ) { - ctx.diagnostic(assignment(member.span(), name)); - } - - check_deep_namespace_for_node( - node, - &source, - vec![entry.local_name.name().to_string()].as_slice(), - &module, - ctx, - ); - } AstKind::JSXMemberExpression(expr) => { check_binding_exported( &expr.property.name, @@ -284,7 +270,7 @@ fn check_deep_namespace_for_node( ctx: &LintContext<'_>, ) -> Option<()> { let (span, name) = match node.kind() { - AstKind::MemberExpression(mem_expr) => mem_expr.static_property_info()?, + AstKind::StaticMemberExpression(mem_expr) => mem_expr.static_property_info(), AstKind::ComputedMemberExpression(computed_expr) => computed_expr.static_property_info()?, _ => return None, }; diff --git a/crates/oxc_linter/src/rules/import/no_commonjs.rs b/crates/oxc_linter/src/rules/import/no_commonjs.rs index bfe9daffa2ea7..3bbbee9555346 100644 --- a/crates/oxc_linter/src/rules/import/no_commonjs.rs +++ b/crates/oxc_linter/src/rules/import/no_commonjs.rs @@ -146,20 +146,27 @@ impl Rule for NoCommonjs { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { match node.kind() { - AstKind::MemberExpression(member_expr) => { + member_expr if member_expr.is_member_expression_kind() => { // module.exports - let Some(property_name) = member_expr.static_property_name() else { + let Some(member_expr_kind) = member_expr.as_member_expression_kind() else { return; }; + let Some(property_name) = + member_expr_kind.static_property_name().map(|s| s.as_str()) + else { + return; + }; + + let object = member_expr_kind.object(); - if member_expr.object().is_specific_id("module") && property_name == "exports" { + if object.is_specific_id("module") && property_name == "exports" { let Some(parent_node) = ctx.nodes().ancestors(node.id()).nth(3) else { return; }; if !self.allow_primitive_modules { ctx.diagnostic(no_commonjs_diagnostic( - member_expr.span(), + member_expr_kind.span(), "export", property_name, )); @@ -170,7 +177,7 @@ impl Rule for NoCommonjs { &assignment_expr.right.without_parentheses() { ctx.diagnostic(no_commonjs_diagnostic( - member_expr.span(), + member_expr_kind.span(), "export", property_name, )); @@ -179,7 +186,7 @@ impl Rule for NoCommonjs { } } else { ctx.diagnostic(no_commonjs_diagnostic( - member_expr.span(), + member_expr_kind.span(), "export", property_name, )); @@ -188,13 +195,13 @@ impl Rule for NoCommonjs { } // exports. - if member_expr.object().is_specific_id("exports") { + if object.is_specific_id("exports") { if node.scope_id() != ctx.scoping().root_scope_id() { return; } ctx.diagnostic(no_commonjs_diagnostic( - member_expr.span(), + member_expr_kind.span(), "export", property_name, )); diff --git a/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs b/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs index b54c721172fa9..e73c98a03421f 100644 --- a/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs +++ b/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs @@ -2,12 +2,12 @@ use std::sync::Arc; use oxc_ast::{ AstKind, - ast::{BindingPatternKind, Expression, IdentifierReference, MemberExpression}, + ast::{BindingPatternKind, Expression, IdentifierReference}, }; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_semantic::SymbolId; -use oxc_span::Span; +use oxc_span::{GetSpan, Span}; use rustc_hash::FxHashMap; use crate::{context::LintContext, module_record::ImportImportName, rule::Rule}; @@ -123,22 +123,23 @@ impl Rule for NoNamedAsDefaultMember { for item in ctx.nodes() { match item.kind() { - AstKind::MemberExpression(member_expr) => { - let Expression::Identifier(ident) = member_expr.object() else { - return; + member_expr if member_expr.is_member_expression_kind() => { + let Some(member_expr_kind) = member_expr.as_member_expression_kind() else { + continue; + }; + let Expression::Identifier(ident) = member_expr_kind.object() else { + continue; }; - let Some(prop_str) = member_expr.static_property_name() else { - return; + let Some(prop_str) = + member_expr_kind.static_property_name().map(|n| n.as_str()) + else { + continue; }; if let Some(module_name) = get_external_module_name_if_has_entry(ident, prop_str) { ctx.diagnostic(no_named_as_default_member_dignostic( - match member_expr { - MemberExpression::ComputedMemberExpression(it) => it.span, - MemberExpression::StaticMemberExpression(it) => it.span, - MemberExpression::PrivateFieldExpression(it) => it.span, - }, + member_expr_kind.span(), &ident.name, prop_str, module_name.name(), diff --git a/crates/oxc_linter/src/rules/jest/no_confusing_set_timeout.rs b/crates/oxc_linter/src/rules/jest/no_confusing_set_timeout.rs index 53debb6f568cc..3ff11bc3066de 100644 --- a/crates/oxc_linter/src/rules/jest/no_confusing_set_timeout.rs +++ b/crates/oxc_linter/src/rules/jest/no_confusing_set_timeout.rs @@ -1,5 +1,5 @@ use cow_utils::CowUtils; -use oxc_ast::{AstKind, ast::MemberExpression}; +use oxc_ast::AstKind; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_semantic::{AstNode, NodeId, ReferenceId}; @@ -140,10 +140,10 @@ fn collect_jest_reference_id( let Some(parent_node) = nodes.parent_node(reference.node_id()) else { continue; }; - let AstKind::MemberExpression(member_expr) = parent_node.kind() else { + if !parent_node.kind().is_member_expression_kind() { continue; - }; - jest_reference_list.push((reference_id, member_expr.span())); + } + jest_reference_list.push((reference_id, parent_node.kind().span())); } } @@ -176,21 +176,17 @@ fn handle_jest_set_time_out<'a>( continue; } - let AstKind::MemberExpression(member_expr) = parent_node.kind() else { - continue; - }; - - let MemberExpression::StaticMemberExpression(expr) = member_expr else { + let AstKind::StaticMemberExpression(expr) = parent_node.kind() else { continue; }; if expr.property.name == "setTimeout" { if !scopes.scope_flags(parent_node.scope_id()).is_top() { - ctx.diagnostic(no_global_set_timeout_diagnostic(member_expr.span())); + ctx.diagnostic(no_global_set_timeout_diagnostic(expr.span)); } if *seen_jest_set_timeout { - ctx.diagnostic(no_multiple_set_timeouts_diagnostic(member_expr.span())); + ctx.diagnostic(no_multiple_set_timeouts_diagnostic(expr.span)); } else { *seen_jest_set_timeout = true; } @@ -210,10 +206,9 @@ fn is_jest_fn_call<'a>( let parent_kind = parent.kind(); if matches!( parent_kind, - AstKind::CallExpression(_) - | AstKind::MemberExpression(_) - | AstKind::TaggedTemplateExpression(_) - ) { + AstKind::CallExpression(_) | AstKind::TaggedTemplateExpression(_) + ) || parent_kind.is_member_expression_kind() + { id = parent.id(); } else { break; diff --git a/crates/oxc_linter/src/rules/jest/no_deprecated_functions.rs b/crates/oxc_linter/src/rules/jest/no_deprecated_functions.rs index 1b7d0469cfa2d..63ab9f18cf596 100644 --- a/crates/oxc_linter/src/rules/jest/no_deprecated_functions.rs +++ b/crates/oxc_linter/src/rules/jest/no_deprecated_functions.rs @@ -1,6 +1,6 @@ use std::borrow::Cow; -use oxc_ast::{AstKind, ast::Expression}; +use oxc_ast::ast::Expression; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; @@ -111,7 +111,7 @@ impl Rule for NoDeprecatedFunctions { } fn run<'a>(&self, node: &oxc_semantic::AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::MemberExpression(mem_expr) = node.kind() else { + let Some(mem_expr) = node.kind().as_member_expression_kind() else { return; }; let mut chain: Vec> = Vec::new(); @@ -120,7 +120,7 @@ impl Rule for NoDeprecatedFunctions { } if let Some(name) = mem_expr.static_property_name() { - chain.push(Cow::Borrowed(name)); + chain.push(Cow::Borrowed(name.as_str())); } let node_name = chain.join("."); diff --git a/crates/oxc_linter/src/rules/jest/valid_expect.rs b/crates/oxc_linter/src/rules/jest/valid_expect.rs index 16358f7032944..8821c70873f07 100644 --- a/crates/oxc_linter/src/rules/jest/valid_expect.rs +++ b/crates/oxc_linter/src/rules/jest/valid_expect.rs @@ -1,9 +1,6 @@ use std::borrow::Cow; -use oxc_ast::{ - AstKind, - ast::{Expression, MemberExpression}, -}; +use oxc_ast::{AstKind, ast::Expression}; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; @@ -147,7 +144,8 @@ impl ValidExpect { return; }; let reporting_span = jest_fn_call.expect_error.map_or(call_expr.span, |_| { - find_top_most_member_expression(node, ctx).map_or(call_expr.span, GetSpan::span) + find_top_most_member_expression(node, ctx) + .map_or(call_expr.span, |top_most_member_expr| top_most_member_expr.span()) }); match jest_fn_call.expect_error { @@ -246,18 +244,18 @@ impl ValidExpect { fn find_top_most_member_expression<'a, 'b>( node: &'b AstNode<'a>, ctx: &'b LintContext<'a>, -) -> Option<&'b MemberExpression<'a>> { +) -> Option> { let mut top_most_member_expression = None; let mut node = node; loop { let parent = ctx.nodes().parent_node(node.id())?; match node.kind() { - AstKind::MemberExpression(member_expr) => { + member_expr if member_expr.is_member_expression_kind() => { top_most_member_expression = Some(member_expr); } _ => { - if !matches!(parent.kind(), AstKind::MemberExpression(_)) { + if !parent.kind().is_member_expression_kind() { break; } } diff --git a/crates/oxc_linter/src/rules/oxc/bad_array_method_on_arguments.rs b/crates/oxc_linter/src/rules/oxc/bad_array_method_on_arguments.rs index 6a4582e635921..15fbd90150912 100644 --- a/crates/oxc_linter/src/rules/oxc/bad_array_method_on_arguments.rs +++ b/crates/oxc_linter/src/rules/oxc/bad_array_method_on_arguments.rs @@ -1,7 +1,7 @@ -use oxc_ast::{AstKind, ast::MemberExpression}; +use oxc_ast::AstKind; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; -use oxc_span::Span; +use oxc_span::{GetSpan, Span}; use crate::{AstNode, context::LintContext, rule::Rule}; @@ -60,47 +60,23 @@ impl Rule for BadArrayMethodOnArguments { let Some(parent) = ctx.nodes().parent_node(node.id()) else { return; }; - if !matches!( - parent.kind(), - AstKind::MemberExpression(_) | AstKind::ComputedMemberExpression(_) - ) { + let Some(member_expr) = parent.kind().as_member_expression_kind() else { return; - } - let member_expr = parent.kind(); + }; let Some(grandparent) = ctx.nodes().parent_node(parent.id()) else { return; }; - if matches!(member_expr, AstKind::ComputedMemberExpression(_)) { - let great_grandparent = ctx.nodes().parent_kind(grandparent.id()); - let Some(AstKind::CallExpression(_)) = great_grandparent else { - return; - }; - } else if !matches!(member_expr, AstKind::ComputedMemberExpression(_)) { - let AstKind::CallExpression(_) = grandparent.kind() else { - return; - }; - } - match member_expr { - AstKind::MemberExpression(MemberExpression::StaticMemberExpression(expr)) => { - if ARRAY_METHODS.binary_search(&expr.property.name.as_str()).is_ok() { - ctx.diagnostic(bad_array_method_on_arguments_diagnostic( - expr.property.name.as_str(), - expr.span, - )); - } - } - AstKind::ComputedMemberExpression(expr) => { - let Some(name) = expr.static_property_name() else { - return; - }; - if ARRAY_METHODS.binary_search(&name.as_str()).is_ok() { - ctx.diagnostic(bad_array_method_on_arguments_diagnostic( - name.as_str(), - expr.span, - )); - } - } - _ => {} + let AstKind::CallExpression(_) = grandparent.kind() else { + return; + }; + let Some(name) = member_expr.static_property_name() else { + return; + }; + if ARRAY_METHODS.binary_search(&name.as_str()).is_ok() { + ctx.diagnostic(bad_array_method_on_arguments_diagnostic( + name.as_str(), + member_expr.span(), + )); } } } diff --git a/crates/oxc_linter/src/rules/oxc/uninvoked_array_callback.rs b/crates/oxc_linter/src/rules/oxc/uninvoked_array_callback.rs index e850f4e690209..b507636fcb6c6 100644 --- a/crates/oxc_linter/src/rules/oxc/uninvoked_array_callback.rs +++ b/crates/oxc_linter/src/rules/oxc/uninvoked_array_callback.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - AstKind, - ast::{Argument, MemberExpression}, -}; +use oxc_ast::{AstKind, MemberExpressionKind, ast::Argument}; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; @@ -67,51 +64,28 @@ impl Rule for UninvokedArrayCallback { return; }; - match member_expr_node.kind() { - AstKind::MemberExpression(member_expr) => { - let Some(AstKind::CallExpression(call_expr)) = - ctx.nodes().parent_kind(member_expr_node.id()) - else { - return; - }; - if !matches!( - call_expr.arguments.first(), - Some(Argument::FunctionExpression(_) | Argument::ArrowFunctionExpression(_)) - ) { - return; - } - - let property_span = match member_expr { - MemberExpression::ComputedMemberExpression(expr) => expr.expression.span(), - MemberExpression::StaticMemberExpression(expr) => expr.property.span, - MemberExpression::PrivateFieldExpression(expr) => expr.field.span, - }; - ctx.diagnostic(uninvoked_array_callback_diagnostic(property_span, new_expr.span)); - } - AstKind::ComputedMemberExpression(computed_member_expr) => { - let Some(parent) = ctx.nodes().parent_node(member_expr_node.id()) else { - return; - }; - let Some(grandparent) = ctx.nodes().parent_kind(parent.id()) else { - return; - }; - let AstKind::CallExpression(call_expr) = grandparent else { - return; - }; - if !matches!( - call_expr.arguments.first(), - Some(Argument::FunctionExpression(_) | Argument::ArrowFunctionExpression(_)) - ) { - return; - } + let Some(member_expr) = member_expr_node.kind().as_member_expression_kind() else { + return; + }; - ctx.diagnostic(uninvoked_array_callback_diagnostic( - computed_member_expr.expression.span(), - new_expr.span, - )); - } - _ => {} + let Some(AstKind::CallExpression(call_expr)) = + ctx.nodes().parent_kind(member_expr_node.id()) + else { + return; + }; + if !matches!( + call_expr.arguments.first(), + Some(Argument::FunctionExpression(_) | Argument::ArrowFunctionExpression(_)) + ) { + return; } + + let property_span = match member_expr { + MemberExpressionKind::Computed(expr) => expr.expression.span(), + MemberExpressionKind::Static(expr) => expr.property.span, + MemberExpressionKind::PrivateField(expr) => expr.field.span, + }; + ctx.diagnostic(uninvoked_array_callback_diagnostic(property_span, new_expr.span)); } } diff --git a/crates/oxc_linter/src/rules/promise/spec_only.rs b/crates/oxc_linter/src/rules/promise/spec_only.rs index 1342f9a1720af..3accf970d3c4e 100644 --- a/crates/oxc_linter/src/rules/promise/spec_only.rs +++ b/crates/oxc_linter/src/rules/promise/spec_only.rs @@ -1,4 +1,3 @@ -use oxc_ast::AstKind; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{CompactStr, GetSpan, Span}; @@ -66,7 +65,7 @@ impl Rule for SpecOnly { } fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::MemberExpression(member_expr) = node.kind() else { + let Some(member_expr) = node.kind().as_member_expression_kind() else { return; }; @@ -74,10 +73,9 @@ impl Rule for SpecOnly { return; } - let Some(prop_name) = member_expr.static_property_name() else { + let Some(prop_name) = member_expr.static_property_name().map(|s| s.as_str()) else { return; }; - if PROMISE_STATIC_METHODS.contains(&prop_name) { return; } diff --git a/crates/oxc_linter/src/rules/react/exhaustive_deps.rs b/crates/oxc_linter/src/rules/react/exhaustive_deps.rs index 53729126ec11b..083e36eaeb5e1 100644 --- a/crates/oxc_linter/src/rules/react/exhaustive_deps.rs +++ b/crates/oxc_linter/src/rules/react/exhaustive_deps.rs @@ -10,9 +10,8 @@ use oxc_ast::{ ast::{ Argument, ArrayExpressionElement, ArrowFunctionExpression, BindingPattern, BindingPatternKind, CallExpression, ChainElement, Expression, FormalParameters, Function, - FunctionBody, IdentifierReference, MemberExpression, StaticMemberExpression, - TSTypeAnnotation, TSTypeParameterInstantiation, TSTypeReference, VariableDeclarationKind, - VariableDeclarator, + FunctionBody, IdentifierReference, StaticMemberExpression, TSTypeAnnotation, + TSTypeParameterInstantiation, TSTypeReference, VariableDeclarationKind, VariableDeclarator, }, match_expression, }; @@ -437,9 +436,7 @@ impl Rule for ExhaustiveDeps { let has_write_reference = reference.symbol_id().is_some_and(|symbol_id| { ctx.semantic().symbol_references(symbol_id).any(|reference| { ctx.nodes().parent_node(reference.node_id()).is_some_and(|parent| { - let AstKind::MemberExpression( - MemberExpression::StaticMemberExpression(member_expr), - ) = parent.kind() + let AstKind::StaticMemberExpression(member_expr) = parent.kind() else { return false; }; @@ -1254,7 +1251,7 @@ impl<'a> Visit<'a> for ExhaustiveDepsVisitor<'a, '_> { } let is_parent_call_expr = - self.stack.get(self.stack.len() - 2).is_some_and(|&ty| ty == AstType::CallExpression); + self.stack.last().is_some_and(|&ty| ty == AstType::CallExpression); match analyze_property_chain(&it.object, self.semantic) { Ok(source) => { diff --git a/crates/oxc_linter/src/rules/react/no_string_refs.rs b/crates/oxc_linter/src/rules/react/no_string_refs.rs index 135ea94fa14d6..8ed86340a0917 100644 --- a/crates/oxc_linter/src/rules/react/no_string_refs.rs +++ b/crates/oxc_linter/src/rules/react/no_string_refs.rs @@ -123,9 +123,12 @@ impl Rule for NoStringRefs { ctx.diagnostic(string_in_ref_deprecated(attr.span)); } } - AstKind::MemberExpression(member_expr) => { + member_expr if member_expr.is_member_expression_kind() => { + let Some(member_expr) = member_expr.as_member_expression_kind() else { + return; + }; if matches!(member_expr.object(), Expression::ThisExpression(_)) - && member_expr.static_property_name() == Some("refs") + && member_expr.static_property_name().is_some_and(|name| name == "refs") && get_parent_component(node, ctx).is_some() { ctx.diagnostic(this_refs_deprecated(member_expr.span())); diff --git a/crates/oxc_linter/src/rules/typescript/no_extra_non_null_assertion.rs b/crates/oxc_linter/src/rules/typescript/no_extra_non_null_assertion.rs index 0d4631f3b5fad..ea9bc23079d35 100644 --- a/crates/oxc_linter/src/rules/typescript/no_extra_non_null_assertion.rs +++ b/crates/oxc_linter/src/rules/typescript/no_extra_non_null_assertion.rs @@ -80,8 +80,15 @@ impl Rule for NoExtraNonNullAssertion { None } } - AstKind::MemberExpression(expr) if expr.optional() => { - if let Expression::TSNonNullExpression(expr) = expr.object().without_parentheses() { + AstKind::StaticMemberExpression(expr) if expr.optional => { + if let Expression::TSNonNullExpression(expr) = expr.object.without_parentheses() { + Some(expr) + } else { + None + } + } + AstKind::ComputedMemberExpression(expr) if expr.optional => { + if let Expression::TSNonNullExpression(expr) = expr.object.without_parentheses() { Some(expr) } else { None diff --git a/crates/oxc_linter/src/rules/typescript/no_non_null_asserted_optional_chain.rs b/crates/oxc_linter/src/rules/typescript/no_non_null_asserted_optional_chain.rs index e886a44ebb586..da232a6739d3a 100644 --- a/crates/oxc_linter/src/rules/typescript/no_non_null_asserted_optional_chain.rs +++ b/crates/oxc_linter/src/rules/typescript/no_non_null_asserted_optional_chain.rs @@ -124,10 +124,9 @@ impl Rule for NoNonNullAssertedOptionalChain { } fn is_parent_member_or_call(node: &AstNode<'_>, ctx: &LintContext<'_>) -> bool { - matches!( - ctx.nodes().parent_kind(node.id()), - Some(AstKind::MemberExpression(_) | AstKind::CallExpression(_)) - ) + let parent_kind = ctx.nodes().parent_kind(node.id()); + matches!(parent_kind, Some(AstKind::CallExpression(_))) + || parent_kind.is_some_and(|k| k.is_member_expression_kind()) } #[test] diff --git a/crates/oxc_linter/src/rules/typescript/prefer_for_of.rs b/crates/oxc_linter/src/rules/typescript/prefer_for_of.rs index e72cec358e566..133e62cd1e7e8 100644 --- a/crates/oxc_linter/src/rules/typescript/prefer_for_of.rs +++ b/crates/oxc_linter/src/rules/typescript/prefer_for_of.rs @@ -188,16 +188,10 @@ impl Rule for PreferForOf { return true; }; - let Some(mut ref_grand_parent) = nodes.parent_node(ref_parent.id()) else { + let Some(ref_grand_parent) = nodes.parent_node(ref_parent.id()) else { return true; }; - if matches!(ref_parent.kind(), AstKind::ComputedMemberExpression(_)) { - if let Some(parent) = nodes.parent_node(ref_grand_parent.id()) { - ref_grand_parent = parent; - } - } - match ref_grand_parent.kind() { AstKind::SimpleAssignmentTarget(_) => { return true; @@ -212,26 +206,21 @@ impl Rule for PreferForOf { let parent_kind = ref_parent.kind(); match parent_kind { - AstKind::MemberExpression(mem_expr) => match mem_expr.object() { - Expression::Identifier(id) => id.name.as_str() != array_name, - expr if expr.is_member_expression() => { - match expr.to_member_expression().static_property_name() { - Some(prop_name) => prop_name != array_name, - None => true, - } - } - _ => true, - }, - AstKind::ComputedMemberExpression(computed_expr) => match &computed_expr.object { - Expression::Identifier(id) => id.name.as_str() != array_name, - expr if expr.is_member_expression() => { - match expr.to_member_expression().static_property_name() { - Some(prop_name) => prop_name != array_name, - None => true, + mem_expr if mem_expr.is_member_expression_kind() => { + let Some(mem_expr) = mem_expr.as_member_expression_kind() else { + return true; + }; + match &mem_expr.object() { + Expression::Identifier(id) => id.name.as_str() != array_name, + expr if expr.is_member_expression() => { + match expr.to_member_expression().static_property_name() { + Some(prop_name) => prop_name != array_name, + None => true, + } } + _ => true, } - _ => true, - }, + } _ => true, } }) { diff --git a/crates/oxc_linter/src/rules/unicorn/explicit_length_check.rs b/crates/oxc_linter/src/rules/unicorn/explicit_length_check.rs index 6c9f4512b1fec..c94d419683758 100644 --- a/crates/oxc_linter/src/rules/unicorn/explicit_length_check.rs +++ b/crates/oxc_linter/src/rules/unicorn/explicit_length_check.rs @@ -1,8 +1,6 @@ use oxc_ast::{ AstKind, - ast::{ - BinaryExpression, Expression, LogicalExpression, MemberExpression, StaticMemberExpression, - }, + ast::{BinaryExpression, Expression, LogicalExpression, StaticMemberExpression}, }; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; @@ -245,11 +243,8 @@ impl ExplicitLengthCheck { } impl Rule for ExplicitLengthCheck { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - if let AstKind::MemberExpression(MemberExpression::StaticMemberExpression( - static_member_expr, - )) = node.kind() - { - let StaticMemberExpression { object, property, .. } = &**static_member_expr; + if let AstKind::StaticMemberExpression(static_member_expr) = node.kind() { + let StaticMemberExpression { object, property, .. } = static_member_expr; if property.name != "length" && property.name != "size" { return; } diff --git a/crates/oxc_linter/src/rules/unicorn/no_accessor_recursion.rs b/crates/oxc_linter/src/rules/unicorn/no_accessor_recursion.rs index b2f35fc8e64f1..8f86e5612d0db 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_accessor_recursion.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_accessor_recursion.rs @@ -1,8 +1,8 @@ use oxc_ast::{ - AstKind, + AstKind, MemberExpressionKind, ast::{ - BindingPatternKind, MemberExpression, MethodDefinition, MethodDefinitionKind, - ObjectProperty, PropertyKey, PropertyKind, UpdateExpression, + BindingPatternKind, MethodDefinition, MethodDefinitionKind, ObjectProperty, PropertyKey, + PropertyKind, UpdateExpression, }, }; use oxc_diagnostics::OxcDiagnostic; @@ -66,7 +66,10 @@ impl Rule for NoAccessorRecursion { return; }; let Some(target) = ctx.nodes().ancestors(node.id()).skip(1).find(|n| match n.kind() { - AstKind::MemberExpression(member_expr) => { + member_expr if member_expr.is_member_expression_kind() => { + let Some(member_expr) = member_expr.as_member_expression_kind() else { + return false; + }; member_expr.object().without_parentheses().span() == this_expr.span() } AstKind::VariableDeclarator(decl) => decl @@ -102,8 +105,11 @@ impl Rule for NoAccessorRecursion { } } } - AstKind::MemberExpression(member_expr) => { - let Some(expr_key_name) = get_member_expr_key_name(member_expr) else { + member_expr if member_expr.is_member_expression_kind() => { + let Some(member_expr) = member_expr.as_member_expression_kind() else { + return; + }; + let Some(expr_key_name) = get_member_expr_key_name(&member_expr) else { return; }; match func_parent.kind() { @@ -113,7 +119,7 @@ impl Rule for NoAccessorRecursion { return; }; let is_same_key = { - if matches!(member_expr, MemberExpression::PrivateFieldExpression(_)) { + if matches!(member_expr, MemberExpressionKind::PrivateField(_)) { matches!(&property.key, PropertyKey::PrivateIdentifier(_)) && prop_key_name.as_ref() == expr_key_name } else { @@ -142,7 +148,7 @@ impl Rule for NoAccessorRecursion { return; }; let is_same_key = { - if matches!(member_expr, MemberExpression::PrivateFieldExpression(_)) { + if matches!(member_expr, MemberExpressionKind::PrivateField(_)) { matches!(&method_def.key, PropertyKey::PrivateIdentifier(_)) && prop_key_name.as_ref() == expr_key_name } else { @@ -192,13 +198,13 @@ fn is_property_write<'a>(node: &AstNode<'a>, ctx: &LintContext<'a>) -> bool { } } -fn get_member_expr_key_name<'a>(expr: &'a MemberExpression) -> Option<&'a str> { +fn get_member_expr_key_name<'a>(expr: &'a MemberExpressionKind) -> Option<&'a str> { match expr { - MemberExpression::ComputedMemberExpression(_) - | MemberExpression::StaticMemberExpression(_) => expr.static_property_name(), - MemberExpression::PrivateFieldExpression(priv_field) => { - Some(priv_field.field.name.as_str()) + MemberExpressionKind::Computed(expr) => { + expr.static_property_name().map(|name| name.as_str()) } + MemberExpressionKind::Static(expr) => Some(expr.property.name.as_str()), + MemberExpressionKind::PrivateField(priv_field) => Some(priv_field.field.name.as_str()), } } diff --git a/crates/oxc_linter/src/rules/unicorn/no_await_expression_member.rs b/crates/oxc_linter/src/rules/unicorn/no_await_expression_member.rs index f99bd8d6f21b2..def58bf1765fe 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_await_expression_member.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_await_expression_member.rs @@ -1,6 +1,6 @@ use oxc_ast::{ - AstKind, - ast::{BindingPatternKind, Expression, MemberExpression}, + AstKind, MemberExpressionKind, + ast::{BindingPatternKind, Expression}, }; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; @@ -50,7 +50,7 @@ declare_oxc_lint!( impl Rule for NoAwaitExpressionMember { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::MemberExpression(member_expr) = node.kind() else { + let Some(member_expr) = node.kind().as_member_expression_kind() else { return; }; @@ -87,7 +87,7 @@ impl Rule for NoAwaitExpressionMember { match member_expr { // e.g. "const a = (await b())[0]" => "const {a} = await b()" - MemberExpression::ComputedMemberExpression(computed_member_expr) => { + MemberExpressionKind::Computed(computed_member_expr) => { let Expression::NumericLiteral(prop) = &computed_member_expr.expression else { return fixer.noop(); @@ -106,7 +106,7 @@ impl Rule for NoAwaitExpressionMember { }; rule_fixes.push(fixer.replace(id.span, replacement)); } - MemberExpression::StaticMemberExpression(static_member_expr) + MemberExpressionKind::Static(static_member_expr) if static_member_expr.property.name.as_str() == name => { // e.g. "const a = (await b()).a" => "const {a} = await b()" diff --git a/crates/oxc_linter/src/rules/unicorn/no_useless_promise_resolve_reject.rs b/crates/oxc_linter/src/rules/unicorn/no_useless_promise_resolve_reject.rs index ff3c7ef04f36c..a6346a6dadda2 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_useless_promise_resolve_reject.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_useless_promise_resolve_reject.rs @@ -261,11 +261,10 @@ fn traverse_bind_calls<'a, 'b>(node: &'a AstNode<'b>, ctx: &'a LintContext<'b>) } fn is_bind_member_expression(node: &AstNode) -> bool { - let AstKind::MemberExpression(member_expr) = node.kind() else { + let Some(member_expr) = node.kind().as_member_expression_kind() else { return false; }; - - member_expr.static_property_name() == Some("bind") + member_expr.static_property_name().is_some_and(|name| name == "bind") } fn match_arrow_function_body<'a>(ctx: &LintContext<'a>, parent: &AstNode<'a>) -> bool { diff --git a/crates/oxc_linter/src/rules/unicorn/no_zero_fractions.rs b/crates/oxc_linter/src/rules/unicorn/no_zero_fractions.rs index 18daa40aaed75..43b22043abb17 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_zero_fractions.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_zero_fractions.rs @@ -74,10 +74,10 @@ impl Rule for NoZeroFractions { |fixer| { let mut fixed = fmt.clone(); let is_decimal_integer = fmt.parse::().is_ok(); - let is_member_expression = - ctx.nodes().parent_node(node.id()).is_some_and(|parent_node| { - matches!(parent_node.kind(), AstKind::MemberExpression(_)) - }); + let is_member_expression = ctx + .nodes() + .parent_node(node.id()) + .is_some_and(|parent_node| parent_node.kind().is_member_expression_kind()); if is_member_expression && is_decimal_integer { fixed = format!("({fixed})"); diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_array_find.rs b/crates/oxc_linter/src/rules/unicorn/prefer_array_find.rs index 3b240a9ca5be2..24e4e2b317d85 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_array_find.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_array_find.rs @@ -61,10 +61,7 @@ impl Rule for PreferArrayFind { if let Expression::CallExpression(call_expr) = computed_member_expr.object.get_inner_expression() { - let Some(parent) = ctx.nodes().parent_node(node.id()) else { - return; - }; - if is_filter_call(call_expr) && !is_left_hand_side(parent, ctx) { + if is_filter_call(call_expr) && !is_left_hand_side(node, ctx) { ctx.diagnostic(prefer_array_find_diagnostic( call_expr_member_expr_property_span(call_expr), )); diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_text_content.rs b/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_text_content.rs index 29264f07c86e1..df1622bc1a49d 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_text_content.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_dom_node_text_content.rs @@ -46,14 +46,13 @@ declare_oxc_lint!( impl Rule for PreferDomNodeTextContent { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { match node.kind() { - AstKind::MemberExpression(member_expr) => { - if let Some((span, name)) = member_expr.static_property_info() { - if name == "innerText" && !member_expr.is_computed() { - ctx.diagnostic_with_fix( - prefer_dom_node_text_content_diagnostic(span), - |fixer| fixer.replace(span, "textContent"), - ); - } + AstKind::StaticMemberExpression(member_expr) => { + let (span, name) = member_expr.static_property_info(); + if name == "innerText" { + ctx.diagnostic_with_fix( + prefer_dom_node_text_content_diagnostic(span), + |fixer| fixer.replace(span, "textContent"), + ); } } // `const {innerText} = node` or `({innerText: text} = node)` diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_global_this.rs b/crates/oxc_linter/src/rules/unicorn/prefer_global_this.rs index c47ea3cfb2de2..c6e4bc959ae91 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_global_this.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_global_this.rs @@ -1,7 +1,4 @@ -use oxc_ast::{ - AstKind, - ast::{Expression, MemberExpression}, -}; +use oxc_ast::{AstKind, ast::Expression}; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; use oxc_span::{GetSpan, Span}; @@ -72,9 +69,7 @@ impl Rule for PreferGlobalThis { return; } - if let Some(AstKind::MemberExpression(MemberExpression::StaticMemberExpression(e))) = - ctx.nodes().parent_kind(node.id()) - { + if let Some(AstKind::StaticMemberExpression(e)) = ctx.nodes().parent_kind(node.id()) { if let Expression::Identifier(ident) = &e.object { if ident.name == "self" && WEB_WORKER_SPECIFIC_APIS.contains(&e.property.name.as_str()) diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_number_properties.rs b/crates/oxc_linter/src/rules/unicorn/prefer_number_properties.rs index b12addc6df64a..1a2e6a88de124 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_number_properties.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_number_properties.rs @@ -96,18 +96,23 @@ impl Rule for PreferNumberProperties { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { match node.kind() { - AstKind::MemberExpression(member_expr) => { + member_expr if member_expr.is_member_expression_kind() => { + let Some(member_expr) = member_expr.as_member_expression_kind() else { + return; + }; let Expression::Identifier(ident_name) = member_expr.object() else { return; }; if GLOBAL_OBJECT_NAMES.contains(&ident_name.name.as_str()) { - let Some(name) = member_expr.static_property_name() else { return }; + let Some(name) = member_expr.static_property_name() else { + return; + }; if (name == "NaN" && self.check_nan) || (name == "Infinity" && self.check_infinity) { ctx.diagnostic_with_fix( - prefer_number_properties_diagnostic(member_expr.span(), name), + prefer_number_properties_diagnostic(member_expr.span(), name.as_str()), |fixer| fixer.replace(ident_name.span, "Number"), ); } diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_set_has.rs b/crates/oxc_linter/src/rules/unicorn/prefer_set_has.rs index e76b15e7c5387..4b30b95b61a30 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_set_has.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_set_has.rs @@ -200,11 +200,12 @@ impl Rule for PreferSetHas { if arg.is_spread() { return true; } - let Some(AstKind::MemberExpression(member_expr)) = ctx.nodes().parent_kind(node.id()) + let Some(AstKind::StaticMemberExpression(member_expr)) = + ctx.nodes().parent_kind(node.id()) else { return true; }; - if member_expr.optional() || member_expr.is_computed() { + if member_expr.optional { return true; } let is_method = is_method_call( @@ -274,12 +275,10 @@ impl Rule for PreferSetHas { let Some(parent) = ctx.nodes().parent_node(node.id()) else { continue; }; - let AstKind::MemberExpression(member_expr) = parent.kind() else { - continue; - }; - let Some(property_info) = member_expr.static_property_info() else { + let AstKind::StaticMemberExpression(member_expr) = parent.kind() else { continue; }; + let property_info = member_expr.static_property_info(); references_fix.push(fixer.replace(property_info.0, "has")); } declaration_fix diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_set_size.rs b/crates/oxc_linter/src/rules/unicorn/prefer_set_size.rs index 081afac67f3de..6c54c4dcb2c1a 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_set_size.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_set_size.rs @@ -48,19 +48,16 @@ declare_oxc_lint!( impl Rule for PreferSetSize { fn run<'a>(&self, node: &AstNode<'a>, ctx: &LintContext<'a>) { - let AstKind::MemberExpression(member_expr) = node.kind() else { + let AstKind::StaticMemberExpression(member_expr) = node.kind() else { return; }; - let Some((span, property)) = member_expr.static_property_info() else { - return; - }; - - if property != "length" || member_expr.optional() || member_expr.is_computed() { + let (span, property) = member_expr.static_property_info(); + if property != "length" || member_expr.optional { return; } - let Expression::ArrayExpression(array_expr) = member_expr.object().without_parentheses() + let Expression::ArrayExpression(array_expr) = member_expr.object.without_parentheses() else { return; }; diff --git a/crates/oxc_linter/src/utils/jest.rs b/crates/oxc_linter/src/utils/jest.rs index 94af21c5b82b3..167be7563334c 100644 --- a/crates/oxc_linter/src/utils/jest.rs +++ b/crates/oxc_linter/src/utils/jest.rs @@ -187,7 +187,7 @@ pub fn iter_possible_jest_call_node<'a, 'c>( return Some(PossibleJestNode { node: parent, original }); } else if matches!( parent_kind, - AstKind::MemberExpression(_) + AstKind::StaticMemberExpression(_) | AstKind::TaggedTemplateExpression(_) | AstKind::ComputedMemberExpression(_) ) { diff --git a/crates/oxc_linter/src/utils/jest/parse_jest_fn.rs b/crates/oxc_linter/src/utils/jest/parse_jest_fn.rs index ecf5bf5358166..057eafbae2899 100644 --- a/crates/oxc_linter/src/utils/jest/parse_jest_fn.rs +++ b/crates/oxc_linter/src/utils/jest/parse_jest_fn.rs @@ -85,7 +85,12 @@ pub fn parse_jest_fn_call<'a>( // parsing e.g. x().y.z(), we'll incorrectly find & parse "x()" even though // the full chain is not a valid jest function call chain if ctx.nodes().parent_node(node.id()).is_some_and(|parent_node| { - matches!(parent_node.kind(), AstKind::CallExpression(_) | AstKind::MemberExpression(_)) + matches!( + parent_node.kind(), + AstKind::CallExpression(_) + | AstKind::StaticMemberExpression(_) + | AstKind::ComputedMemberExpression(_) + ) }) { return None; } @@ -142,7 +147,7 @@ fn parse_jest_expect_fn_call<'a>( // If the parent is a member expression, we can assume that the matcher // is not called, so we can set the error to `MatcherNotCalled`. match ctx.nodes().parent_kind(node.id())? { - AstKind::MemberExpression(_) | AstKind::ComputedMemberExpression(_) => { + AstKind::StaticMemberExpression(_) | AstKind::ComputedMemberExpression(_) => { expect_error = Some(ExpectError::MatcherNotCalled); } _ => {} @@ -245,7 +250,9 @@ fn is_top_most_call_expr<'a, 'b>(node: &'b AstNode<'a>, ctx: &'b LintContext<'a> match parent.kind() { AstKind::CallExpression(_) => return false, - AstKind::MemberExpression(_) | AstKind::ComputedMemberExpression(_) => node = parent, + AstKind::StaticMemberExpression(_) | AstKind::ComputedMemberExpression(_) => { + node = parent; + } _ => { return true; } diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index 0907b51fa426b..bb29921eab436 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -1781,9 +1781,6 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> { } fn visit_member_expression(&mut self, it: &MemberExpression<'a>) { - let kind = AstKind::MemberExpression(self.alloc(it)); - self.enter_node(kind); - // A.B = 1; // ^^^ Can't treat A as a Write reference since it's A's property(B) that changes. self.current_reference_flags -= ReferenceFlags::Write; @@ -1795,7 +1792,6 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> { MemberExpression::StaticMemberExpression(it) => self.visit_static_member_expression(it), MemberExpression::PrivateFieldExpression(it) => self.visit_private_field_expression(it), } - self.leave_node(kind); } fn visit_simple_assignment_target(&mut self, it: &SimpleAssignmentTarget<'a>) { diff --git a/crates/oxc_semantic/src/checker/javascript.rs b/crates/oxc_semantic/src/checker/javascript.rs index 492b279971597..3a8ae4c92d6ef 100644 --- a/crates/oxc_semantic/src/checker/javascript.rs +++ b/crates/oxc_semantic/src/checker/javascript.rs @@ -140,7 +140,7 @@ pub fn check_identifier_reference(ident: &IdentifierReference, ctx: &SemanticBui AstKind::AssignmentTarget(_) | AstKind::SimpleAssignmentTarget(_) => { return ctx.error(unexpected_identifier_assign(&ident.name, ident.span)); } - AstKind::MemberExpression(_) => break, + m if m.is_member_expression_kind() => break, _ => {} } } @@ -935,12 +935,13 @@ fn super_private(span: Span) -> OxcDiagnostic { OxcDiagnostic::error("Private fields cannot be accessed on super").with_label(span) } -pub fn check_member_expression(member_expr: &MemberExpression, ctx: &SemanticBuilder<'_>) { - if let MemberExpression::PrivateFieldExpression(private_expr) = member_expr { - // `super.#m` - if private_expr.object.is_super() { - ctx.error(super_private(private_expr.span)); - } +pub fn check_private_field_expression( + private_expr: &PrivateFieldExpression, + ctx: &SemanticBuilder<'_>, +) { + // `super.#m` + if private_expr.object.is_super() { + ctx.error(super_private(private_expr.span)); } } diff --git a/crates/oxc_semantic/src/checker/mod.rs b/crates/oxc_semantic/src/checker/mod.rs index c4681e59cc474..c0b1d8d7ce386 100644 --- a/crates/oxc_semantic/src/checker/mod.rs +++ b/crates/oxc_semantic/src/checker/mod.rs @@ -97,7 +97,7 @@ pub fn check<'a>(kind: AstKind<'a>, ctx: &SemanticBuilder<'a>) { AstKind::AssignmentExpression(expr) => js::check_assignment_expression(expr, ctx), AstKind::AwaitExpression(expr) => js::check_await_expression(expr, ctx), - AstKind::MemberExpression(expr) => js::check_member_expression(expr, ctx), + AstKind::PrivateFieldExpression(expr) => js::check_private_field_expression(expr, ctx), AstKind::ObjectExpression(expr) => js::check_object_expression(expr, ctx), AstKind::UnaryExpression(expr) => js::check_unary_expression(expr, ctx), AstKind::YieldExpression(expr) => js::check_yield_expression(expr, ctx), diff --git a/crates/oxc_semantic/src/class/builder.rs b/crates/oxc_semantic/src/class/builder.rs index e8e4bb4c2e114..985067bd60a5c 100644 --- a/crates/oxc_semantic/src/class/builder.rs +++ b/crates/oxc_semantic/src/class/builder.rs @@ -103,7 +103,8 @@ impl<'a> ClassTableBuilder<'a> { ) { let parent_kind = nodes.parent_kind(current_node_id); if let Some(parent_kind) = parent_kind { - if matches!(parent_kind, AstKind::PrivateInExpression(_) | AstKind::MemberExpression(_)) + if matches!(parent_kind, AstKind::PrivateInExpression(_)) + || parent_kind.is_member_expression_kind() { if let Some(class_id) = self.current_class_id { let element_ids = self.classes.get_element_ids( diff --git a/crates/oxc_semantic/tests/fixtures/typescript-eslint/jsx/namespaced-attribute.snap b/crates/oxc_semantic/tests/fixtures/typescript-eslint/jsx/namespaced-attribute.snap index 2b4e00a0f02b4..8123299e4c56b 100644 --- a/crates/oxc_semantic/tests/fixtures/typescript-eslint/jsx/namespaced-attribute.snap +++ b/crates/oxc_semantic/tests/fixtures/typescript-eslint/jsx/namespaced-attribute.snap @@ -28,7 +28,7 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/jsx/namespaced- "flags": "ReferenceFlags(Read)", "id": 3, "name": "props", - "node_id": 52 + "node_id": 51 } ] } diff --git a/crates/oxc_semantic/tests/fixtures/typescript-eslint/member-expression/member-expression.snap b/crates/oxc_semantic/tests/fixtures/typescript-eslint/member-expression/member-expression.snap index fa20c5fed3fb6..faf83b0938f4e 100644 --- a/crates/oxc_semantic/tests/fixtures/typescript-eslint/member-expression/member-expression.snap +++ b/crates/oxc_semantic/tests/fixtures/typescript-eslint/member-expression/member-expression.snap @@ -31,19 +31,19 @@ input_file: crates/oxc_semantic/tests/fixtures/typescript-eslint/member-expressi "flags": "ReferenceFlags(Read)", "id": 2, "name": "x", - "node_id": 22 + "node_id": 21 }, { "flags": "ReferenceFlags(Read)", "id": 3, "name": "x", - "node_id": 29 + "node_id": 28 }, { "flags": "ReferenceFlags(Read)", "id": 4, "name": "x", - "node_id": 37 + "node_id": 35 } ] } diff --git a/tasks/ast_tools/src/generators/ast_kind.rs b/tasks/ast_tools/src/generators/ast_kind.rs index af283dfee535d..9c6fb1b998c7f 100644 --- a/tasks/ast_tools/src/generators/ast_kind.rs +++ b/tasks/ast_tools/src/generators/ast_kind.rs @@ -28,8 +28,6 @@ use super::define_generator; /// Apart from this list, every struct with `#[ast(visit)]` attr gets an `AstKind`. const STRUCTS_BLACK_LIST: &[&str] = &[ "TemplateElement", - "StaticMemberExpression", - "PrivateFieldExpression", "AssignmentTargetRest", "AssignmentTargetPropertyIdentifier", "AssignmentTargetPropertyProperty", @@ -47,7 +45,6 @@ const STRUCTS_BLACK_LIST: &[&str] = &[ /// Apart from this list, enums don't have `AstKind`s. const ENUMS_WHITE_LIST: &[&str] = &[ "PropertyKey", - "MemberExpression", "Argument", "AssignmentTarget", "SimpleAssignmentTarget", diff --git a/tasks/prettier_conformance/snapshots/prettier.js.snap.md b/tasks/prettier_conformance/snapshots/prettier.js.snap.md index a1d27f7b6645a..da64b2764c735 100644 --- a/tasks/prettier_conformance/snapshots/prettier.js.snap.md +++ b/tasks/prettier_conformance/snapshots/prettier.js.snap.md @@ -1,4 +1,4 @@ -js compatibility: 324/699 (46.35%) +js compatibility: 323/699 (46.21%) # Failed @@ -96,6 +96,7 @@ js compatibility: 324/699 (46.35%) | js/comments/binary-expressions-block-comments.js | 💥💥 | 47.06% | | js/comments/binary-expressions-single-comments.js | 💥💥 | 29.41% | | js/comments/blank.js | 💥💥 | 95.24% | +| js/comments/call_comment.js | 💥💥 | 90.91% | | js/comments/dangling.js | 💥💥 | 59.26% | | js/comments/dangling_array.js | 💥💥 | 25.00% | | js/comments/dangling_for.js | 💥💥 | 22.22% | @@ -210,7 +211,7 @@ js compatibility: 324/699 (46.35%) | js/import-attributes/keyword-detect.js | 💥 | 28.57% | | js/import-attributes/long-sources.js | 💥 | 48.48% | | js/label/comment.js | 💥 | 53.33% | -| js/last-argument-expansion/arrow.js | 💥 | 10.53% | +| js/last-argument-expansion/arrow.js | 💥 | 21.05% | | js/last-argument-expansion/assignment-pattern.js | 💥 | 28.57% | | js/last-argument-expansion/break-parent.js | 💥 | 57.14% | | js/last-argument-expansion/dangling-comment-in-arrow-function.js | 💥 | 0.00% | @@ -231,7 +232,7 @@ js compatibility: 324/699 (46.35%) | js/method-chain/assignment-lhs.js | 💥 | 16.67% | | js/method-chain/bracket_0-1.js | 💥 | 0.00% | | js/method-chain/break-last-member.js | 💥 | 80.56% | -| js/method-chain/comment.js | 💥 | 33.61% | +| js/method-chain/comment.js | 💥 | 35.40% | | js/method-chain/computed-merge.js | 💥 | 29.27% | | js/method-chain/computed.js | 💥 | 0.00% | | js/method-chain/conditional.js | 💥 | 36.73% | @@ -241,7 +242,7 @@ js compatibility: 324/699 (46.35%) | js/method-chain/inline_merge.js | 💥 | 75.00% | | js/method-chain/issue-11298.js | 💥 | 20.00% | | js/method-chain/issue-3594.js | 💥 | 33.33% | -| js/method-chain/issue-4125.js | 💥 | 58.64% | +| js/method-chain/issue-4125.js | 💥 | 58.82% | | js/method-chain/logical.js | 💥 | 56.67% | | js/method-chain/multiple-members.js | 💥 | 45.65% | | js/method-chain/object-literal.js | 💥 | 7.69% | @@ -268,9 +269,9 @@ js compatibility: 324/699 (46.35%) | js/objects/assignment-expression/object-property.js | 💥 | 66.67% | | js/objects/assignment-expression/object-value.js | 💥 | 37.50% | | js/optional-chaining/chaining.js | 💥 | 59.77% | -| js/optional-chaining/comments.js | 💥 | 14.12% | +| js/optional-chaining/comments.js | 💥 | 14.29% | | js/preserve-line/argument-list.js | 💥 | 99.44% | -| js/preserve-line/member-chain.js | 💥 | 19.05% | +| js/preserve-line/member-chain.js | 💥 | 19.20% | | js/preserve-line/parameter-list.js | 💥 | 79.31% | | js/quote-props/classes.js | 💥💥✨✨ | 47.06% | | js/quote-props/objects.js | 💥💥✨✨ | 45.10% |