mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
fix(ecmascript): allow getting PropName for object methods (#6967)
Working on some lint rules and noticed that PropName didn't seem to be working as intended. According to the spec, we should definitely allow get/set/methods to have prop names: https://tc39.es/ecma262/#sec-static-semantics-propname <img width="694" alt="image" src="https://github.com/user-attachments/assets/60fdfeec-2320-4cd9-a786-901728e459b2"> However, we still need to retain this logic for checking `__proto__` because it has special rules.
This commit is contained in:
parent
7aa496ab33
commit
da199c7476
4 changed files with 67 additions and 8 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -1603,7 +1603,9 @@ version = "0.34.0"
|
|||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-traits",
|
||||
"oxc_allocator",
|
||||
"oxc_ast",
|
||||
"oxc_parser",
|
||||
"oxc_span",
|
||||
"oxc_syntax",
|
||||
]
|
||||
|
|
|
|||
|
|
@ -28,6 +28,11 @@ oxc_syntax = { workspace = true, features = ["to_js_string"] }
|
|||
num-bigint = { workspace = true }
|
||||
num-traits = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
# Parser and allocator are only used in tests to make testing easier
|
||||
oxc_allocator = { workspace = true }
|
||||
oxc_parser = { workspace = true }
|
||||
|
||||
[features]
|
||||
default = []
|
||||
side_effects = []
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
use oxc_ast::ast::{
|
||||
ClassElement, MethodDefinition, ObjectProperty, ObjectPropertyKind, PropertyDefinition,
|
||||
PropertyKey, PropertyKind,
|
||||
PropertyKey,
|
||||
};
|
||||
use oxc_span::Span;
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ impl<'a> PropName for ObjectPropertyKind<'a> {
|
|||
|
||||
impl<'a> PropName for ObjectProperty<'a> {
|
||||
fn prop_name(&self) -> Option<(&str, Span)> {
|
||||
if self.kind != PropertyKind::Init || self.method || self.shorthand || self.computed {
|
||||
if self.shorthand || self.computed {
|
||||
return None;
|
||||
}
|
||||
self.key.prop_name()
|
||||
|
|
@ -65,3 +65,47 @@ impl<'a> PropName for PropertyDefinition<'a> {
|
|||
self.key.prop_name()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_ast::{ast::ObjectExpression, Visit};
|
||||
use oxc_parser::Parser;
|
||||
use oxc_span::SourceType;
|
||||
|
||||
use crate::PropName;
|
||||
|
||||
#[test]
|
||||
fn test_prop_name() {
|
||||
#[derive(Debug, Default)]
|
||||
struct TestVisitor;
|
||||
|
||||
impl<'a> Visit<'a> for TestVisitor {
|
||||
fn visit_object_expression(&mut self, obj_expr: &ObjectExpression<'a>) {
|
||||
assert_eq!("a", obj_expr.properties[0].prop_name().unwrap().0);
|
||||
assert_eq!("b", obj_expr.properties[1].prop_name().unwrap().0);
|
||||
assert_eq!("c", obj_expr.properties[2].prop_name().unwrap().0);
|
||||
assert_eq!("d", obj_expr.properties[3].prop_name().unwrap().0);
|
||||
assert_eq!(None, obj_expr.properties[4].prop_name());
|
||||
}
|
||||
}
|
||||
|
||||
let allocator = Allocator::default();
|
||||
let source_type = SourceType::default();
|
||||
let source = r"
|
||||
const obj = {
|
||||
a() {},
|
||||
get b() {},
|
||||
set c(_) {},
|
||||
d: 1,
|
||||
[e]() {},
|
||||
}
|
||||
";
|
||||
let ret = Parser::new(&allocator, source, source_type).parse();
|
||||
assert!(!ret.program.is_empty());
|
||||
assert!(ret.errors.is_empty());
|
||||
|
||||
let mut visitor = TestVisitor;
|
||||
visitor.visit_program(&ret.program);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -984,13 +984,21 @@ pub fn check_object_expression(obj_expr: &ObjectExpression, ctx: &SemanticBuilde
|
|||
// It is a Syntax Error if PropertyNameList of PropertyDefinitionList contains any duplicate entries for "__proto__"
|
||||
// and at least two of those entries were obtained from productions of the form PropertyDefinition : PropertyName : AssignmentExpression
|
||||
let mut prev_proto: Option<Span> = None;
|
||||
let prop_names = obj_expr.properties.iter().filter_map(PropName::prop_name);
|
||||
for prop_name in prop_names {
|
||||
if prop_name.0 == "__proto__" {
|
||||
if let Some(prev_span) = prev_proto {
|
||||
ctx.error(redeclaration("__proto__", prev_span, prop_name.1));
|
||||
for prop in &obj_expr.properties {
|
||||
if let ObjectPropertyKind::ObjectProperty(obj_prop) = prop {
|
||||
// Skip if not a property definition production:
|
||||
// PropertyDefinition : PropertyName : AssignmentExpression
|
||||
if obj_prop.kind != PropertyKind::Init || obj_prop.method {
|
||||
continue;
|
||||
}
|
||||
if let Some((prop_name, span)) = prop.prop_name() {
|
||||
if prop_name == "__proto__" {
|
||||
if let Some(prev_span) = prev_proto {
|
||||
ctx.error(redeclaration("__proto__", prev_span, span));
|
||||
}
|
||||
prev_proto = Some(span);
|
||||
}
|
||||
}
|
||||
prev_proto = Some(prop_name.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue