From 0903501a98475632dbbe69d4b3a5af721f57232f Mon Sep 17 00:00:00 2001 From: Dunqing <29533304+Dunqing@users.noreply.github.com> Date: Thu, 9 Jan 2025 06:38:09 +0000 Subject: [PATCH] refactor(semantic): check super usage based on scope (#8377) No stronger opinion on both implementations, I just think the scope-based is clearer, and iterating over scope is always faster than node. The only bad point in this implementation we don't have specific ScopeFlags for class methods, thus we need to check if the parent of the function is a `MethodDefinition` --- crates/oxc_semantic/src/checker/javascript.rs | 95 +++++++++---------- 1 file changed, 47 insertions(+), 48 deletions(-) diff --git a/crates/oxc_semantic/src/checker/javascript.rs b/crates/oxc_semantic/src/checker/javascript.rs index 630f3c77bc465..d33204a32ccda 100644 --- a/crates/oxc_semantic/src/checker/javascript.rs +++ b/crates/oxc_semantic/src/checker/javascript.rs @@ -790,63 +790,62 @@ pub fn check_super<'a>(sup: &Super, node: &AstNode<'a>, ctx: &SemanticBuilder<'a ); }; - // skip(1) is the self `Super` - // skip(2) is the parent `CallExpression` or `NewExpression` - for node_id in ctx.nodes.ancestor_ids(node.id()).skip(2) { - match ctx.nodes.kind(node_id) { - AstKind::MethodDefinition(def) => { - // ClassElement : MethodDefinition - // It is a Syntax Error if PropName of MethodDefinition is not "constructor" and HasDirectSuper of MethodDefinition is true. - if let Some(super_call_span) = super_call_span { - if def.kind == MethodDefinitionKind::Constructor { - // It is a Syntax Error if SuperCall in nested set/get function - if ctx.scope.get_flags(node.scope_id()).is_set_or_get_accessor() { - return ctx.error(unexpected_super_call(super_call_span)); - } - - // check ClassHeritage - if let AstKind::Class(class) = - ctx.nodes.kind(ctx.class_table_builder.classes.get_node_id(class_id)) - { - // ClassTail : ClassHeritageopt { ClassBody } - // It is a Syntax Error if ClassHeritage is not present and the following algorithm returns true: - // 1. Let constructor be ConstructorMethod of ClassBody. - // 2. If constructor is empty, return false. - // 3. Return HasDirectSuper of constructor. - if class.super_class.is_none() { - return ctx.error(super_without_derived_class(sup.span, class.span)); - } - } - break; - } - return ctx.error(unexpected_super_call(super_call_span)); - } - // super references are allowed in method - break; + let class_node_id = ctx.class_table_builder.classes.get_node_id(class_id); + let AstKind::Class(class) = ctx.nodes.kind(class_node_id) else { unreachable!() }; + let class_scope_id = class.scope_id(); + + for scope_id in ctx.scope.ancestors(ctx.current_scope_id) { + let flags = ctx.scope.get_flags(scope_id); + + if flags.intersects(ScopeFlags::Constructor) { + // ClassTail : ClassHeritageopt { ClassBody } + // * It is a Syntax Error if ClassHeritage is not present and the following algorithm returns true: + // 1. Let constructor be ConstructorMethod of ClassBody. + // 2. If constructor is empty, return false. + // 3. Return HasDirectSuper of constructor. + if class.super_class.is_none() && super_call_span.is_some() { + ctx.error(super_without_derived_class(sup.span, class.span)); } - // * It is a Syntax Error if FunctionBody Contains SuperProperty is true. - AstKind::Function(_) => { - if !matches!(ctx.nodes.parent_kind(node_id), Some(AstKind::MethodDefinition(_))) { - return super_call_span.map_or_else( - || ctx.error(unexpected_super_reference(sup.span)), - |super_call_span| ctx.error(unexpected_super_call(super_call_span)), - ); - } + return; + } + + if let Some(super_call_span) = super_call_span { + // ClassElement : MethodDefinition + // * It is a Syntax Error if PropName of MethodDefinition is not "constructor" and HasDirectSuper of MethodDefinition is true. + // * It is a Syntax Error if SuperCall in nested set/get function + if flags.is_function() && !flags.is_arrow() { + return ctx.error(unexpected_super_call(super_call_span)); } + // FieldDefinition : ClassElementName Initializer opt // * It is a Syntax Error if Initializer is present and Initializer Contains SuperCall is true. // PropertyDefinition : MethodDefinition // * It is a Syntax Error if HasDirectSuper of MethodDefinition is true. - AstKind::PropertyDefinition(_) + let is_class_scope = class_scope_id == scope_id; // ClassStaticBlockBody : ClassStaticBlockStatementList // * It is a Syntax Error if ClassStaticBlockStatementList Contains SuperCall is true. - | AstKind::StaticBlock(_) => { - if let Some(super_call_span) = super_call_span { - return ctx.error(unexpected_super_call(super_call_span)); - } - break; + let is_class_static_block_scope = flags.is_class_static_block(); + if is_class_scope || is_class_static_block_scope { + return ctx.error(unexpected_super_call(super_call_span)); } - _ => {} + } + + if class_scope_id == scope_id { + break; + } + + if flags.is_function() && !flags.is_arrow() { + // * It is a Syntax Error if FunctionBody Contains SuperProperty is true. + // Check this function if is a class method, if it isn't, then it a plain function + let function_node_id = ctx.scope.get_node_id(scope_id); + let is_class_method = matches!( + ctx.nodes.parent_kind(function_node_id), + Some(AstKind::MethodDefinition(_)) + ); + if !is_class_method { + ctx.error(unexpected_super_reference(sup.span)); + } + return; } } }