-
Notifications
You must be signed in to change notification settings - Fork 649
feat(rome_js_analyze): noDuplicateClassMembers
#4137
Changes from 1 commit
71f2a58
2cc6076
70c1865
c0a5ee5
87949bd
81feb1b
ed8e2e5
76b69cb
92e9a93
c6d9d10
be2fc37
1bcf95a
25b2c5b
bb83917
35d2980
87cc268
42b27eb
24626be
732ec84
a43d6c9
6905d87
bbabf68
aa6e194
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
- Loading branch information
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -1,8 +1,16 @@ | ||||
| use crate::semantic_services::Semantic; | ||||
| use rome_analyze::{context::RuleContext, declare_rule, Rule, RuleDiagnostic}; | ||||
| use rome_console::markup; | ||||
| use rome_js_semantic::{Reference, ReferencesExtensions}; | ||||
| use rome_js_syntax::JsIdentifierBinding; | ||||
| use std::collections::HashMap; | ||||
|
|
||||
| use rome_analyze::{context::RuleContext, declare_rule, Ast, Rule, RuleDiagnostic}; | ||||
| use rome_js_syntax::{ | ||||
| AnyJsClassMember, AnyJsClassMemberName, AnyJsMethodModifier, AnyJsPropertyModifier, | ||||
| AnyTsMethodSignatureModifier, AnyTsPropertySignatureModifier, JsClassDeclaration, | ||||
| JsClassExpression, JsClassMemberList, JsGetterClassMember, JsMethodClassMember, | ||||
| JsMethodModifierList, JsPropertyClassMember, JsPropertyModifierList, JsSetterClassMember, | ||||
| TextRange, TsGetterSignatureClassMember, TsMethodSignatureClassMember, | ||||
| TsMethodSignatureModifierList, TsPropertySignatureClassMember, TsPropertySignatureModifierList, | ||||
| TsSetterSignatureClassMember, | ||||
| }; | ||||
| use rome_rowan::{declare_node_union, AstNode}; | ||||
|
|
||||
| declare_rule! { | ||||
| /// Disallow duplicate class members. | ||||
|
|
@@ -87,31 +95,311 @@ declare_rule! { | |||
| } | ||||
| } | ||||
|
|
||||
| fn get_member_name_string(member_name_node: AnyJsClassMemberName) -> Option<String> { | ||||
| match member_name_node { | ||||
| AnyJsClassMemberName::JsLiteralMemberName(node) => node.name().ok(), | ||||
| AnyJsClassMemberName::JsPrivateClassMemberName(node) => Some(node.text()), | ||||
| _ => None, | ||||
| } | ||||
| } | ||||
|
|
||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||||
| enum AccessType { | ||||
| Public, | ||||
| Private, | ||||
| } | ||||
|
|
||||
| fn get_access_type(member_name_node: AnyJsClassMemberName) -> Option<AccessType> { | ||||
| match member_name_node { | ||||
| AnyJsClassMemberName::JsPrivateClassMemberName(_) => Some(AccessType::Private), | ||||
| _ => Some(AccessType::Public), | ||||
| } | ||||
| } | ||||
|
|
||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||||
| enum StaticType { | ||||
| Static, | ||||
| NonStatic, | ||||
| } | ||||
|
|
||||
| #[allow(clippy::enum_variant_names)] | ||||
| enum AnyModifierList { | ||||
| JsPropertyModifierList(JsPropertyModifierList), | ||||
| JsMethodModifierList(JsMethodModifierList), | ||||
| TsPropertySignatureModifierList(TsPropertySignatureModifierList), | ||||
| TsMethodSignatureModifierList(TsMethodSignatureModifierList), | ||||
| } | ||||
|
|
||||
| fn get_static_type(modifier_list: AnyModifierList) -> StaticType { | ||||
ematipico marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||
| match modifier_list { | ||||
| AnyModifierList::JsPropertyModifierList(node) => { | ||||
| if node | ||||
| .into_iter() | ||||
| .any(|m| matches!(m, AnyJsPropertyModifier::JsStaticModifier(_))) | ||||
| { | ||||
| StaticType::Static | ||||
| } else { | ||||
| StaticType::NonStatic | ||||
| } | ||||
| } | ||||
| AnyModifierList::JsMethodModifierList(node) => { | ||||
| if node | ||||
| .into_iter() | ||||
| .any(|m| matches!(m, AnyJsMethodModifier::JsStaticModifier(_))) | ||||
| { | ||||
| StaticType::Static | ||||
| } else { | ||||
| StaticType::NonStatic | ||||
| } | ||||
| } | ||||
| AnyModifierList::TsPropertySignatureModifierList(node) => { | ||||
| if node | ||||
| .into_iter() | ||||
| .any(|m| matches!(m, AnyTsPropertySignatureModifier::JsStaticModifier(_))) | ||||
| { | ||||
| StaticType::Static | ||||
| } else { | ||||
| StaticType::NonStatic | ||||
| } | ||||
| } | ||||
| AnyModifierList::TsMethodSignatureModifierList(node) => { | ||||
| if node | ||||
| .into_iter() | ||||
| .any(|m| matches!(m, AnyTsMethodSignatureModifier::JsStaticModifier(_))) | ||||
| { | ||||
| StaticType::Static | ||||
| } else { | ||||
| StaticType::NonStatic | ||||
| } | ||||
| } | ||||
| } | ||||
| } | ||||
|
|
||||
| declare_node_union! { | ||||
| pub(crate) ClassMemberDefinition = JsGetterClassMember | JsMethodClassMember | JsPropertyClassMember | JsSetterClassMember | TsGetterSignatureClassMember | TsMethodSignatureClassMember | TsPropertySignatureClassMember | TsSetterSignatureClassMember | ||||
| } | ||||
|
|
||||
| #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] | ||||
| pub(crate) enum MemberType { | ||||
| Normal, | ||||
| Getter, | ||||
| Setter, | ||||
| } | ||||
|
|
||||
| impl ClassMemberDefinition { | ||||
| fn member_name(&self) -> Option<String> { | ||||
| match self { | ||||
| ClassMemberDefinition::JsGetterClassMember(node) => { | ||||
| get_member_name_string(node.name().ok()?) | ||||
| } | ||||
| ClassMemberDefinition::JsMethodClassMember(node) => { | ||||
| get_member_name_string(node.name().ok()?) | ||||
| } | ||||
| ClassMemberDefinition::JsPropertyClassMember(node) => { | ||||
| get_member_name_string(node.name().ok()?) | ||||
| } | ||||
| ClassMemberDefinition::JsSetterClassMember(node) => { | ||||
| get_member_name_string(node.name().ok()?) | ||||
| } | ||||
| ClassMemberDefinition::TsGetterSignatureClassMember(node) => { | ||||
| get_member_name_string(node.name().ok()?) | ||||
| } | ||||
| ClassMemberDefinition::TsMethodSignatureClassMember(node) => { | ||||
| get_member_name_string(node.name().ok()?) | ||||
| } | ||||
| ClassMemberDefinition::TsPropertySignatureClassMember(node) => { | ||||
| get_member_name_string(node.name().ok()?) | ||||
| } | ||||
| ClassMemberDefinition::TsSetterSignatureClassMember(node) => { | ||||
| get_member_name_string(node.name().ok()?) | ||||
| } | ||||
| } | ||||
| } | ||||
|
|
||||
| fn member_type(&self) -> MemberType { | ||||
| match self { | ||||
| ClassMemberDefinition::JsGetterClassMember(_) | ||||
| | ClassMemberDefinition::TsGetterSignatureClassMember(_) => MemberType::Getter, | ||||
| ClassMemberDefinition::JsSetterClassMember(_) | ||||
| | ClassMemberDefinition::TsSetterSignatureClassMember(_) => MemberType::Setter, | ||||
| _ => MemberType::Normal, | ||||
| } | ||||
| } | ||||
|
|
||||
| fn access_type(&self) -> Option<AccessType> { | ||||
| match self { | ||||
| ClassMemberDefinition::JsGetterClassMember(node) => get_access_type(node.name().ok()?), | ||||
| ClassMemberDefinition::JsMethodClassMember(node) => get_access_type(node.name().ok()?), | ||||
| ClassMemberDefinition::JsPropertyClassMember(node) => { | ||||
| get_access_type(node.name().ok()?) | ||||
| } | ||||
| ClassMemberDefinition::JsSetterClassMember(node) => get_access_type(node.name().ok()?), | ||||
| ClassMemberDefinition::TsGetterSignatureClassMember(node) => { | ||||
| get_access_type(node.name().ok()?) | ||||
| } | ||||
| ClassMemberDefinition::TsMethodSignatureClassMember(node) => { | ||||
| get_access_type(node.name().ok()?) | ||||
| } | ||||
| ClassMemberDefinition::TsPropertySignatureClassMember(node) => { | ||||
| get_access_type(node.name().ok()?) | ||||
| } | ||||
| ClassMemberDefinition::TsSetterSignatureClassMember(node) => { | ||||
| get_access_type(node.name().ok()?) | ||||
| } | ||||
| } | ||||
| } | ||||
|
|
||||
| fn static_type(&self) -> StaticType { | ||||
| match self { | ||||
| ClassMemberDefinition::JsGetterClassMember(node) => { | ||||
| get_static_type(AnyModifierList::JsMethodModifierList(node.modifiers())) | ||||
| } | ||||
| ClassMemberDefinition::JsMethodClassMember(node) => { | ||||
| get_static_type(AnyModifierList::JsMethodModifierList(node.modifiers())) | ||||
| } | ||||
| ClassMemberDefinition::JsPropertyClassMember(node) => { | ||||
| get_static_type(AnyModifierList::JsPropertyModifierList(node.modifiers())) | ||||
| } | ||||
| ClassMemberDefinition::JsSetterClassMember(node) => { | ||||
| get_static_type(AnyModifierList::JsMethodModifierList(node.modifiers())) | ||||
| } | ||||
| ClassMemberDefinition::TsGetterSignatureClassMember(node) => get_static_type( | ||||
| AnyModifierList::TsMethodSignatureModifierList(node.modifiers()), | ||||
| ), | ||||
| ClassMemberDefinition::TsMethodSignatureClassMember(node) => get_static_type( | ||||
| AnyModifierList::TsMethodSignatureModifierList(node.modifiers()), | ||||
| ), | ||||
| ClassMemberDefinition::TsPropertySignatureClassMember(node) => get_static_type( | ||||
| AnyModifierList::TsPropertySignatureModifierList(node.modifiers()), | ||||
| ), | ||||
| ClassMemberDefinition::TsSetterSignatureClassMember(node) => get_static_type( | ||||
| AnyModifierList::TsMethodSignatureModifierList(node.modifiers()), | ||||
| ), | ||||
| } | ||||
| } | ||||
|
|
||||
| fn range(&self) -> TextRange { | ||||
| match self { | ||||
| ClassMemberDefinition::JsGetterClassMember(node) => node.range(), | ||||
| ClassMemberDefinition::JsMethodClassMember(node) => node.range(), | ||||
| ClassMemberDefinition::JsPropertyClassMember(node) => node.range(), | ||||
| ClassMemberDefinition::JsSetterClassMember(node) => node.range(), | ||||
| ClassMemberDefinition::TsGetterSignatureClassMember(node) => node.range(), | ||||
| ClassMemberDefinition::TsMethodSignatureClassMember(node) => node.range(), | ||||
| ClassMemberDefinition::TsPropertySignatureClassMember(node) => node.range(), | ||||
| ClassMemberDefinition::TsSetterSignatureClassMember(node) => node.range(), | ||||
| } | ||||
| } | ||||
| } | ||||
|
|
||||
| impl TryFrom<AnyJsClassMember> for ClassMemberDefinition { | ||||
| type Error = AnyJsClassMember; | ||||
|
|
||||
| fn try_from(member: AnyJsClassMember) -> Result<Self, Self::Error> { | ||||
| match member { | ||||
| AnyJsClassMember::JsGetterClassMember(node) => { | ||||
| Ok(ClassMemberDefinition::JsGetterClassMember(node)) | ||||
| } | ||||
| AnyJsClassMember::JsMethodClassMember(node) => { | ||||
| Ok(ClassMemberDefinition::JsMethodClassMember(node)) | ||||
| } | ||||
| AnyJsClassMember::JsPropertyClassMember(node) => { | ||||
| Ok(ClassMemberDefinition::JsPropertyClassMember(node)) | ||||
| } | ||||
| AnyJsClassMember::JsSetterClassMember(node) => { | ||||
| Ok(ClassMemberDefinition::JsSetterClassMember(node)) | ||||
| } | ||||
| AnyJsClassMember::TsGetterSignatureClassMember(node) => { | ||||
| Ok(ClassMemberDefinition::TsGetterSignatureClassMember(node)) | ||||
| } | ||||
| AnyJsClassMember::TsMethodSignatureClassMember(node) => { | ||||
| Ok(ClassMemberDefinition::TsMethodSignatureClassMember(node)) | ||||
| } | ||||
| AnyJsClassMember::TsPropertySignatureClassMember(node) => { | ||||
| Ok(ClassMemberDefinition::TsPropertySignatureClassMember(node)) | ||||
| } | ||||
| AnyJsClassMember::TsSetterSignatureClassMember(node) => { | ||||
| Ok(ClassMemberDefinition::TsSetterSignatureClassMember(node)) | ||||
| } | ||||
| _ => Err(member), | ||||
| } | ||||
| } | ||||
| } | ||||
|
|
||||
| declare_node_union! { | ||||
| pub(crate) ClassDefinition = JsClassExpression | JsClassDeclaration | ||||
| } | ||||
|
|
||||
| impl ClassDefinition { | ||||
| fn members(&self) -> JsClassMemberList { | ||||
| match self { | ||||
| ClassDefinition::JsClassExpression(node) => node.members(), | ||||
| ClassDefinition::JsClassDeclaration(node) => node.members(), | ||||
| } | ||||
| } | ||||
| } | ||||
|
|
||||
| impl Rule for NoDuplicateClassMembers { | ||||
| type Query = Semantic<JsIdentifierBinding>; | ||||
| type State = Reference; | ||||
| type Query = Ast<ClassDefinition>; | ||||
| type State = ClassMemberDefinition; | ||||
| type Signals = Vec<Self::State>; | ||||
| type Options = (); | ||||
|
|
||||
| fn run(ctx: &RuleContext<Self>) -> Vec<Self::State> { | ||||
| let binding = ctx.query(); | ||||
| let model = ctx.model(); | ||||
| fn run(ctx: &RuleContext<Self>) -> Self::Signals { | ||||
| let mut defined_members: HashMap< | ||||
| (String, StaticType, AccessType), | ||||
| HashMap<MemberType, ClassMemberDefinition>, | ||||
| > = HashMap::new(); | ||||
| let mut signals: Self::Signals = Vec::new(); | ||||
|
|
||||
| let node = ctx.query(); | ||||
| for member in node.members() { | ||||
| if let Ok(member_def) = ClassMemberDefinition::try_from(member) { | ||||
ematipico marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||
| if let (Some(member_name), Some(access_type)) = | ||||
| (member_def.member_name(), member_def.access_type()) | ||||
| { | ||||
| let static_type = member_def.static_type(); | ||||
| let member_type = member_def.member_type(); | ||||
| defined_members | ||||
| .entry((member_name, static_type, access_type)) | ||||
|
||||
| #[derive(Clone, PartialEq, Eq, Hash)] |
How do I resolve this error?
error[E0119]: conflicting implementations of trait `std::cmp::PartialEq` for type `analyzers::nursery::no_duplicate_class_members::AnyClassMemberDefinition`
--> crates/rome_js_analyze/src/analyzers/nursery/no_duplicate_class_members.rs:144:1
|
144 | / declare_node_union! {
145 | | pub(crate) AnyClassMemberDefinition = JsGetterClassMember | JsMethodClassMember | JsPropertyClassMember | JsSetterClassMember
146 | | }
| |_^ conflicting implementation for `analyzers::nursery::no_duplicate_class_members::AnyClassMemberDefinition`
...
174 | impl PartialEq for AnyClassMemberDefinition {
| ------------------------------------------- first implementation here
|
= note: this error originates in the derive macro `PartialEq` which comes from the expansion of the macro `declare_node_union` (in Nightly builds, run with -Z macro-backtrace for more info)
For more information about this error, try `rustc --explain E0119`.
error: could not compile `rome_js_analyze` due to previous error
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh sorry, then there's no need to manually implement it :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What's the point of saving static_type, access_type and member_name if they are all computed from member_def?
These values are necessary for an unique key about judging whether the class member has already defined or not. I seem AnyClassMemberDefinition is not able to used as a key. If you think AnyClassMemberDefinition could be used as a key of HashMap, can you show more details?
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,11 @@ | ||
|
|
||
|
|
||
| var a = 1; | ||
| a = 2; | ||
| a = 3; | ||
| class A { foo() {} foo() {} } | ||
| !class A { foo() {} foo() {} }; | ||
| class A { foo() {} foo() {} foo() {} } | ||
| class A { static foo() {} static foo() {} } | ||
| class A { foo() {} get foo() {} } | ||
| class A { set foo(value) {} foo() {} } | ||
| class A { foo; foo; } | ||
| class A { #foo; #foo; } | ||
| class A { 'foo'() {} 'foo'() {} } | ||
| class A { foo() {} 'foo'() {} } | ||
| class A { static constructor() {} static 'constructor'() {} } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can avoid the enum
AnyModifierListby accepting aJsSyntaxListinstead. From a typed list - for example,JsPropertyModifierList- you can retrieve a generic list usingsyntax_list.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I refactored
get_static_typetois_static_memberwhich acceptsJsSyntaxList.