chore(xtask): Check for sub unions overlap
Adds a BFS search implementation to check if unions trees contain circular dependencies or reuse types.
The goal is to prevent potential union circular dependencies as well as unions having the same type in multiple branches.
MODIFY
crates/rome_js_formatter/src/js/any/array_assignment_pattern_element.rs
crates/rome_js_formatter/src/js/any/array_assignment_pattern_element.rs
@@ -9,7 +9,6 @@ impl ToFormatElement for JsAnyArrayAssignmentPatternElement {
Self::JsAnyAssignmentPattern(node) => node.to_format_element(formatter),
Self::JsArrayAssignmentPatternRestElement(node) => node.to_format_element(formatter),
Self::JsArrayHole(node) => node.to_format_element(formatter),
- Self::JsUnknownAssignment(node) => node.to_format_element(formatter),
}
}
}

MODIFY
crates/rome_js_syntax/src/generated/nodes.rs
crates/rome_js_syntax/src/generated/nodes.rs
@@ -9132,7 +9132,6 @@ pub enum JsAnyArrayAssignmentPatternElement {
JsArrayAssignmentPatternRestElement(JsArrayAssignmentPatternRestElement),
JsArrayHole(JsArrayHole),
JsAssignmentWithDefault(JsAssignmentWithDefault),
- JsUnknownAssignment(JsUnknownAssignment),
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub enum JsAnyArrayBindingPatternElement {
@@ -18341,18 +18340,12 @@ impl From<JsAssignmentWithDefault> for JsAnyArrayAssignmentPatternElement {
JsAnyArrayAssignmentPatternElement::JsAssignmentWithDefault(node)
}
}
-impl From<JsUnknownAssignment> for JsAnyArrayAssignmentPatternElement {
- fn from(node: JsUnknownAssignment) -> JsAnyArrayAssignmentPatternElement {
- JsAnyArrayAssignmentPatternElement::JsUnknownAssignment(node)
- }
-}
impl AstNode<Language> for JsAnyArrayAssignmentPatternElement {
fn can_cast(kind: SyntaxKind) -> bool {
match kind {
JS_ARRAY_ASSIGNMENT_PATTERN_REST_ELEMENT
| JS_ARRAY_HOLE
- | JS_ASSIGNMENT_WITH_DEFAULT
- | JS_UNKNOWN_ASSIGNMENT => true,
+ | JS_ASSIGNMENT_WITH_DEFAULT => true,
k if JsAnyAssignmentPattern::can_cast(k) => true,
_ => false,
}
@@ -18372,11 +18365,6 @@ impl AstNode<Language> for JsAnyArrayAssignmentPatternElement {
JsAssignmentWithDefault { syntax },
)
}
- JS_UNKNOWN_ASSIGNMENT => {
- JsAnyArrayAssignmentPatternElement::JsUnknownAssignment(JsUnknownAssignment {
- syntax,
- })
- }
_ => {
if let Some(js_any_assignment_pattern) = JsAnyAssignmentPattern::cast(syntax) {
return Some(JsAnyArrayAssignmentPatternElement::JsAnyAssignmentPattern(
@@ -18395,7 +18383,6 @@ impl AstNode<Language> for JsAnyArrayAssignmentPatternElement {
}
JsAnyArrayAssignmentPatternElement::JsArrayHole(it) => &it.syntax,
JsAnyArrayAssignmentPatternElement::JsAssignmentWithDefault(it) => &it.syntax,
- JsAnyArrayAssignmentPatternElement::JsUnknownAssignment(it) => &it.syntax,
JsAnyArrayAssignmentPatternElement::JsAnyAssignmentPattern(it) => it.syntax(),
}
}
@@ -18413,9 +18400,6 @@ impl std::fmt::Debug for JsAnyArrayAssignmentPatternElement {
JsAnyArrayAssignmentPatternElement::JsAssignmentWithDefault(it) => {
std::fmt::Debug::fmt(it, f)
}
- JsAnyArrayAssignmentPatternElement::JsUnknownAssignment(it) => {
- std::fmt::Debug::fmt(it, f)
- }
}
}
}
@@ -18428,7 +18412,6 @@ impl From<JsAnyArrayAssignmentPatternElement> for SyntaxNode {
}
JsAnyArrayAssignmentPatternElement::JsArrayHole(it) => it.into(),
JsAnyArrayAssignmentPatternElement::JsAssignmentWithDefault(it) => it.into(),
- JsAnyArrayAssignmentPatternElement::JsUnknownAssignment(it) => it.into(),
}
}
}

MODIFY
xtask/codegen/js.ungram
xtask/codegen/js.ungram
@@ -926,7 +926,6 @@ JsAnyArrayAssignmentPatternElement =
| JsAnyAssignmentPattern
| JsArrayAssignmentPatternRestElement
| JsArrayHole
- | JsUnknownAssignment
// [a, b, ...rest] = [];
// ^^^^^^^

MODIFY
xtask/codegen/src/ast.rs
xtask/codegen/src/ast.rs
//! Generate SyntaxKind definitions as well as typed AST definitions for nodes and tokens.
//! This is derived from rust-analyzer/xtask/codegen
+use std::collections::{HashMap, HashSet, VecDeque};
use std::vec;
use super::{
@@ -71,10 +72,60 @@ pub(crate) fn generate_syntax(ast: AstSrc, mode: &Mode, language_kind: LanguageK
Ok(())
}
+fn check_unions(unions: &[AstEnumSrc]) {
+ // Setup a map to find the unions quickly
+ let union_map: HashMap<_, _> = unions.iter().map(|en| (&en.name, en)).collect();
+
+ // Iterate over all unions
+ for union in unions {
+ let mut stack_string = format!(
+ "\n******** START ERROR STACK ********\nChecking {}, variants : {:?}",
+ union.name, union.variants
+ );
+ let mut union_set: HashSet<_> = HashSet::from([&union.name]);
+ let mut union_queue: VecDeque<_> = VecDeque::new();
+
+ // Init queue for BFS
+ union_queue.extend(&union.variants);
+
+ // Loop over the queue getting the first variant
+ while let Some(variant) = union_queue.pop_front() {
+ if union_map.contains_key(variant) {
+ // The variant is a compound variant
+ // Get the struct from the map
+ let current_union = union_map[variant];
+ stack_string.push_str(&format!(
+ "\nSUB-ENUM CHECK : {}, variants : {:?}",
+ current_union.name, current_union.variants
+ ));
+ // Try to insert the current variant into the set
+ if union_set.insert(&current_union.name) {
+ // Add all variants into the BFS queue
+ union_queue.extend(&current_union.variants);
+ } else {
+ // We either have a circular dependency or 2 variants referencing the same type
+ println!("{}", stack_string);
+ panic!("Variant '{variant}' used twice or circular dependency");
+ }
+ } else {
+ // The variant isn't another enum
+ stack_string.push_str(&format!("\nBASE-VAR CHECK : {}", variant));
+ if !union_set.insert(variant) {
+ // The variant already used
+ println!("{}", stack_string);
+ panic!("Variant '{variant}' used twice");
+ }
+ }
+ }
+ }
+}
+
pub(crate) fn load_js_ast() -> AstSrc {
let grammar_src = include_str!("../js.ungram");
let grammar: Grammar = grammar_src.parse().unwrap();
- make_ast(&grammar)
+ let ast: AstSrc = make_ast(&grammar);
+ check_unions(&ast.unions);
+ ast
}
pub(crate) fn load_css_ast() -> AstSrc {

chore(xtask): Check for sub unions overlap (#2385)

Adds a BFS search implementation to check if unions trees contain circular dependencies or reuse types.
The goal is to prevent potential union circular dependencies as well as unions having the same type in multiple branches.