@openrewrite/rewrite 8.66.0 → 8.66.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/dist/javascript/comparator.d.ts +67 -4
  2. package/dist/javascript/comparator.d.ts.map +1 -1
  3. package/dist/javascript/comparator.js +523 -2794
  4. package/dist/javascript/comparator.js.map +1 -1
  5. package/dist/javascript/format.d.ts.map +1 -1
  6. package/dist/javascript/format.js +4 -3
  7. package/dist/javascript/format.js.map +1 -1
  8. package/dist/javascript/index.d.ts +1 -1
  9. package/dist/javascript/index.d.ts.map +1 -1
  10. package/dist/javascript/index.js +1 -1
  11. package/dist/javascript/index.js.map +1 -1
  12. package/dist/javascript/parser.d.ts.map +1 -1
  13. package/dist/javascript/parser.js +18 -16
  14. package/dist/javascript/parser.js.map +1 -1
  15. package/dist/javascript/templating/capture.d.ts +226 -0
  16. package/dist/javascript/templating/capture.d.ts.map +1 -0
  17. package/dist/javascript/templating/capture.js +371 -0
  18. package/dist/javascript/templating/capture.js.map +1 -0
  19. package/dist/javascript/templating/comparator.d.ts +61 -0
  20. package/dist/javascript/templating/comparator.d.ts.map +1 -0
  21. package/dist/javascript/templating/comparator.js +393 -0
  22. package/dist/javascript/templating/comparator.js.map +1 -0
  23. package/dist/javascript/templating/engine.d.ts +75 -0
  24. package/dist/javascript/templating/engine.d.ts.map +1 -0
  25. package/dist/javascript/templating/engine.js +228 -0
  26. package/dist/javascript/templating/engine.js.map +1 -0
  27. package/dist/javascript/templating/index.d.ts +6 -0
  28. package/dist/javascript/templating/index.d.ts.map +1 -0
  29. package/dist/javascript/templating/index.js +42 -0
  30. package/dist/javascript/templating/index.js.map +1 -0
  31. package/dist/javascript/templating/pattern.d.ts +171 -0
  32. package/dist/javascript/templating/pattern.d.ts.map +1 -0
  33. package/dist/javascript/templating/pattern.js +681 -0
  34. package/dist/javascript/templating/pattern.js.map +1 -0
  35. package/dist/javascript/templating/placeholder-replacement.d.ts +58 -0
  36. package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -0
  37. package/dist/javascript/templating/placeholder-replacement.js +365 -0
  38. package/dist/javascript/templating/placeholder-replacement.js.map +1 -0
  39. package/dist/javascript/templating/rewrite.d.ts +39 -0
  40. package/dist/javascript/templating/rewrite.d.ts.map +1 -0
  41. package/dist/javascript/templating/rewrite.js +81 -0
  42. package/dist/javascript/templating/rewrite.js.map +1 -0
  43. package/dist/javascript/templating/template.d.ts +204 -0
  44. package/dist/javascript/templating/template.d.ts.map +1 -0
  45. package/dist/javascript/templating/template.js +293 -0
  46. package/dist/javascript/templating/template.js.map +1 -0
  47. package/dist/javascript/templating/types.d.ts +263 -0
  48. package/dist/javascript/templating/types.d.ts.map +1 -0
  49. package/dist/javascript/templating/types.js +3 -0
  50. package/dist/javascript/templating/types.js.map +1 -0
  51. package/dist/javascript/templating/utils.d.ts +118 -0
  52. package/dist/javascript/templating/utils.d.ts.map +1 -0
  53. package/dist/javascript/templating/utils.js +253 -0
  54. package/dist/javascript/templating/utils.js.map +1 -0
  55. package/dist/version.txt +1 -1
  56. package/package.json +2 -1
  57. package/src/javascript/comparator.ts +554 -3323
  58. package/src/javascript/format.ts +3 -2
  59. package/src/javascript/index.ts +1 -1
  60. package/src/javascript/parser.ts +19 -17
  61. package/src/javascript/templating/capture.ts +503 -0
  62. package/src/javascript/templating/comparator.ts +430 -0
  63. package/src/javascript/templating/engine.ts +252 -0
  64. package/src/javascript/templating/index.ts +60 -0
  65. package/src/javascript/templating/pattern.ts +727 -0
  66. package/src/javascript/templating/placeholder-replacement.ts +372 -0
  67. package/src/javascript/templating/rewrite.ts +95 -0
  68. package/src/javascript/templating/template.ts +326 -0
  69. package/src/javascript/templating/types.ts +300 -0
  70. package/src/javascript/templating/utils.ts +284 -0
  71. package/dist/javascript/templating.d.ts +0 -265
  72. package/dist/javascript/templating.d.ts.map +0 -1
  73. package/dist/javascript/templating.js +0 -1027
  74. package/dist/javascript/templating.js.map +0 -1
  75. package/src/javascript/templating.ts +0 -1226
@@ -29,13 +29,6 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
29
29
  */
30
30
  protected match: boolean = true;
31
31
 
32
- /**
33
- * Creates a new comparator visitor.
34
- */
35
- constructor() {
36
- super();
37
- }
38
-
39
32
  /**
40
33
  * Compares two AST trees.
41
34
  *
@@ -68,6 +61,113 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
68
61
  return t;
69
62
  }
70
63
 
64
+ /**
65
+ * Generic method to visit a property value using the appropriate visitor method.
66
+ * This ensures wrappers (RightPadded, LeftPadded, Container) are properly tracked on the cursor.
67
+ *
68
+ * @param j The property value from the first tree
69
+ * @param other The corresponding property value from the second tree
70
+ * @returns The visited property value from the first tree
71
+ */
72
+ protected async visitProperty(j: any, other: any): Promise<any> {
73
+ // Handle null/undefined (but not other falsy values like 0, false, '')
74
+ if (j == null || other == null) {
75
+ if (j !== other) {
76
+ this.abort(j);
77
+ }
78
+ return j;
79
+ }
80
+
81
+ const kind = (j as any).kind;
82
+
83
+ // Check wrappers by kind
84
+ if (kind === J.Kind.RightPadded) {
85
+ return await this.visitRightPadded(j, other);
86
+ }
87
+
88
+ if (kind === J.Kind.LeftPadded) {
89
+ return await this.visitLeftPadded(j, other);
90
+ }
91
+
92
+ if (kind === J.Kind.Container) {
93
+ return await this.visitContainer(j, other);
94
+ }
95
+
96
+ // Check if it's a Space (skip comparison)
97
+ if (kind === J.Kind.Space) {
98
+ return j;
99
+ }
100
+
101
+ // Check if it's a Tree node (has a kind property with a string value)
102
+ if (kind !== undefined && typeof kind === 'string') {
103
+ // Check if it's a Type node (starts with "org.openrewrite.java.tree.JavaType$")
104
+ if (kind.startsWith('org.openrewrite.java.tree.JavaType$')) {
105
+ return await this.visitType(j, other);
106
+ }
107
+ return await this.visit(j, other);
108
+ }
109
+
110
+ // For primitive values, compare directly
111
+ if (j !== other) {
112
+ this.abort(j);
113
+ }
114
+ return j;
115
+ }
116
+
117
+ /**
118
+ * Generic method to visit all properties of an element, calling visitProperty for each.
119
+ * This automatically handles wrappers and ensures proper cursor tracking.
120
+ * Also checks that both elements have the same kind.
121
+ *
122
+ * @param j The element from the first tree
123
+ * @param other The corresponding element from the second tree
124
+ * @returns The visited element from the first tree
125
+ */
126
+ protected async visitElement<T extends J>(j: T, other: T): Promise<T> {
127
+ if (!this.match) {
128
+ return j;
129
+ }
130
+
131
+ // Check if kinds match
132
+ if (j.kind !== other.kind) {
133
+ return this.abort(j);
134
+ }
135
+
136
+ // Iterate over all properties
137
+ for (const key of Object.keys(j)) {
138
+ // Skip internal/private properties, id property, and markers property
139
+ if (key.startsWith('_') || key === 'id' || key === 'markers') {
140
+ continue;
141
+ }
142
+
143
+ const jValue = (j as any)[key];
144
+ const otherValue = (other as any)[key];
145
+
146
+ // Handle arrays - compare element by element
147
+ if (Array.isArray(jValue)) {
148
+ if (!Array.isArray(otherValue) || jValue.length !== otherValue.length) {
149
+ return this.abort(j);
150
+ }
151
+
152
+ for (let i = 0; i < jValue.length; i++) {
153
+ await this.visitProperty(jValue[i], otherValue[i]);
154
+ if (!this.match) {
155
+ return j;
156
+ }
157
+ }
158
+ } else {
159
+ // Visit the property (which will handle wrappers, trees, primitives, etc.)
160
+ await this.visitProperty(jValue, otherValue);
161
+
162
+ if (!this.match) {
163
+ return j;
164
+ }
165
+ }
166
+ }
167
+
168
+ return j;
169
+ }
170
+
71
171
  override async visit<R extends J>(j: Tree, p: J, parent?: Cursor): Promise<R | undefined> {
72
172
  // If we've already found a mismatch, abort further processing
73
173
  if (!this.match) {
@@ -83,6 +183,97 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
83
183
  return super.visit(j, p);
84
184
  }
85
185
 
186
+ /**
187
+ * Override visitRightPadded to compare only the elements, not markers or spacing.
188
+ * The context parameter p contains the corresponding element from the other tree.
189
+ * Pushes the wrapper onto the cursor stack so captures can access it.
190
+ */
191
+ public async visitRightPadded<T extends J | boolean>(right: J.RightPadded<T>, p: J): Promise<J.RightPadded<T>> {
192
+ if (!this.match) {
193
+ return right;
194
+ }
195
+
196
+ // Extract the other element if it's also a RightPadded
197
+ const otherElement = (p as any).kind === J.Kind.RightPadded
198
+ ? ((p as unknown) as J.RightPadded<T>).element
199
+ : p;
200
+
201
+ // Push wrapper onto cursor, then compare only the elements, not markers or spacing
202
+ const savedCursor = this.cursor;
203
+ this.cursor = new Cursor(right, this.cursor);
204
+ try {
205
+ await this.visitProperty(right.element, otherElement);
206
+ } finally {
207
+ this.cursor = savedCursor;
208
+ }
209
+
210
+ return right;
211
+ }
212
+
213
+ /**
214
+ * Override visitLeftPadded to compare only the elements, not markers or spacing.
215
+ * The context parameter p contains the corresponding element from the other tree.
216
+ * Pushes the wrapper onto the cursor stack so captures can access it.
217
+ */
218
+ public async visitLeftPadded<T extends J | J.Space | number | string | boolean>(left: J.LeftPadded<T>, p: J): Promise<J.LeftPadded<T>> {
219
+ if (!this.match) {
220
+ return left;
221
+ }
222
+
223
+ // Extract the other element if it's also a LeftPadded
224
+ const otherElement = (p as any).kind === J.Kind.LeftPadded
225
+ ? ((p as unknown) as J.LeftPadded<T>).element
226
+ : p;
227
+
228
+ // Push wrapper onto cursor, then compare only the elements, not markers or spacing
229
+ const savedCursor = this.cursor;
230
+ this.cursor = new Cursor(left, this.cursor);
231
+ try {
232
+ await this.visitProperty(left.element, otherElement);
233
+ } finally {
234
+ this.cursor = savedCursor;
235
+ }
236
+
237
+ return left;
238
+ }
239
+
240
+ /**
241
+ * Override visitContainer to compare only the elements, not markers or spacing.
242
+ * The context parameter p contains the corresponding element from the other tree.
243
+ * Pushes the wrapper onto the cursor stack so captures can access it.
244
+ */
245
+ public async visitContainer<T extends J>(container: J.Container<T>, p: J): Promise<J.Container<T>> {
246
+ if (!this.match) {
247
+ return container;
248
+ }
249
+
250
+ // Extract the other elements if it's also a Container
251
+ const otherElements: J.RightPadded<T>[] = (p as any).kind === J.Kind.Container
252
+ ? ((p as unknown) as J.Container<T>).elements
253
+ : (p as any);
254
+
255
+ // Compare elements array length
256
+ if (container.elements.length !== otherElements.length) {
257
+ return this.abort(container);
258
+ }
259
+
260
+ // Push wrapper onto cursor, then compare each element
261
+ const savedCursor = this.cursor;
262
+ this.cursor = new Cursor(container, this.cursor);
263
+ try {
264
+ for (let i = 0; i < container.elements.length; i++) {
265
+ await this.visitProperty(container.elements[i], otherElements[i]);
266
+ if (!this.match) {
267
+ return this.abort(container);
268
+ }
269
+ }
270
+ } finally {
271
+ this.cursor = savedCursor;
272
+ }
273
+
274
+ return container;
275
+ }
276
+
86
277
  /**
87
278
  * Overrides the visitBinary method to compare binary expressions.
88
279
  *
@@ -91,21 +282,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
91
282
  * @returns The visited binary expression, or undefined if the visit was aborted
92
283
  */
93
284
  override async visitBinary(binary: J.Binary, other: J): Promise<J | undefined> {
94
- if (!this.match || other.kind !== J.Kind.Binary) {
95
- return this.abort(binary);
96
- }
97
-
98
- const otherBinary = other as J.Binary;
99
- if (binary.operator.element !== otherBinary.operator.element) {
100
- return this.abort(binary);
101
- }
102
-
103
- // Visit left and right operands in lock step
104
- await this.visit(binary.left, otherBinary.left);
105
- if (!this.match) return this.abort(binary);
106
-
107
- await this.visit(binary.right, otherBinary.right);
108
- return binary;
285
+ return this.visitElement(binary, other as J.Binary);
109
286
  }
110
287
 
111
288
  /**
@@ -116,16 +293,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
116
293
  * @returns The visited identifier, or undefined if the visit was aborted
117
294
  */
118
295
  override async visitIdentifier(identifier: J.Identifier, other: J): Promise<J | undefined> {
119
- if (!this.match || other.kind !== J.Kind.Identifier) {
120
- return this.abort(identifier);
121
- }
122
-
123
- const otherIdentifier = other as J.Identifier;
124
- if (identifier.simpleName !== otherIdentifier.simpleName) {
125
- return this.abort(identifier);
126
- }
127
-
128
- return identifier;
296
+ return this.visitElement(identifier, other as J.Identifier);
129
297
  }
130
298
 
131
299
  /**
@@ -136,16 +304,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
136
304
  * @returns The visited literal, or undefined if the visit was aborted
137
305
  */
138
306
  override async visitLiteral(literal: J.Literal, other: J): Promise<J | undefined> {
139
- if (!this.match || other.kind !== J.Kind.Literal) {
140
- return this.abort(literal);
141
- }
142
-
143
- const otherLiteral = other as J.Literal;
144
- if (literal.valueSource !== otherLiteral.valueSource) {
145
- return this.abort(literal);
146
- }
147
-
148
- return literal;
307
+ return this.visitElement(literal, other as J.Literal);
149
308
  }
150
309
 
151
310
  /**
@@ -156,24 +315,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
156
315
  * @returns The visited block, or undefined if the visit was aborted
157
316
  */
158
317
  override async visitBlock(block: J.Block, other: J): Promise<J | undefined> {
159
- if (!this.match || other.kind !== J.Kind.Block) {
160
- return this.abort(block);
161
- }
162
-
163
- const otherBlock = other as J.Block;
164
- if (block.statements.length !== otherBlock.statements.length) {
165
- return this.abort(block);
166
- }
167
-
168
- // Visit each statement in lock step
169
- for (let i = 0; i < block.statements.length; i++) {
170
- await this.visit(block.statements[i].element, otherBlock.statements[i].element);
171
- if (!this.match) {
172
- return this.abort(block);
173
- }
174
- }
175
-
176
- return block;
318
+ return this.visitElement(block, other as J.Block);
177
319
  }
178
320
 
179
321
  /**
@@ -184,24 +326,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
184
326
  * @returns The visited compilation unit, or undefined if the visit was aborted
185
327
  */
186
328
  override async visitJsCompilationUnit(compilationUnit: JS.CompilationUnit, other: J): Promise<J | undefined> {
187
- if (!this.match || other.kind !== JS.Kind.CompilationUnit) {
188
- return this.abort(compilationUnit);
189
- }
190
-
191
- const otherCompilationUnit = other as JS.CompilationUnit;
192
- if (compilationUnit.statements.length !== otherCompilationUnit.statements.length) {
193
- return this.abort(compilationUnit);
194
- }
195
-
196
- // Visit each statement in lock step
197
- for (let i = 0; i < compilationUnit.statements.length; i++) {
198
- await this.visit(compilationUnit.statements[i].element, otherCompilationUnit.statements[i].element);
199
- if (!this.match) {
200
- return this.abort(compilationUnit);
201
- }
202
- }
203
-
204
- return compilationUnit;
329
+ return this.visitElement(compilationUnit, other as JS.CompilationUnit);
205
330
  }
206
331
 
207
332
  /**
@@ -212,20 +337,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
212
337
  * @returns The visited alias, or undefined if the visit was aborted
213
338
  */
214
339
  override async visitAlias(alias: JS.Alias, other: J): Promise<J | undefined> {
215
- if (!this.match || other.kind !== JS.Kind.Alias) {
216
- return this.abort(alias);
217
- }
218
-
219
- const otherAlias = other as JS.Alias;
220
-
221
- // Visit property name and alias in lock step
222
- await this.visit(alias.propertyName.element, otherAlias.propertyName.element);
223
- if (!this.match) {
224
- return this.abort(alias);
225
- }
226
-
227
- await this.visit(alias.alias, otherAlias.alias);
228
- return alias;
340
+ return this.visitElement(alias, other as JS.Alias);
229
341
  }
230
342
 
231
343
  /**
@@ -236,47 +348,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
236
348
  * @returns The visited arrow function, or undefined if the visit was aborted
237
349
  */
238
350
  override async visitArrowFunction(arrowFunction: JS.ArrowFunction, other: J): Promise<J | undefined> {
239
- if (!this.match || other.kind !== JS.Kind.ArrowFunction) {
240
- return this.abort(arrowFunction);
241
- }
242
-
243
- const otherArrowFunction = other as JS.ArrowFunction;
244
-
245
- // Compare modifiers
246
- if (arrowFunction.modifiers.length !== otherArrowFunction.modifiers.length) {
247
- return this.abort(arrowFunction);
248
- }
249
-
250
- // Visit modifiers in lock step
251
- for (let i = 0; i < arrowFunction.modifiers.length; i++) {
252
- await this.visit(arrowFunction.modifiers[i], otherArrowFunction.modifiers[i]);
253
- if (!this.match) return arrowFunction;
254
- }
255
-
256
- // Visit type parameters if present
257
- if (!!arrowFunction.typeParameters !== !!otherArrowFunction.typeParameters) {
258
- return this.abort(arrowFunction);
259
- }
260
-
261
- if (arrowFunction.typeParameters) {
262
- await this.visit(arrowFunction.typeParameters, otherArrowFunction.typeParameters!);
263
- if (!this.match) return arrowFunction;
264
- }
265
-
266
- // Visit lambda
267
- await this.visit(arrowFunction.lambda, otherArrowFunction.lambda);
268
- if (!this.match) return arrowFunction;
269
-
270
- // Visit return type expression if present
271
- if (!!arrowFunction.returnTypeExpression !== !!otherArrowFunction.returnTypeExpression) {
272
- return this.abort(arrowFunction);
273
- }
274
-
275
- if (arrowFunction.returnTypeExpression) {
276
- await this.visit(arrowFunction.returnTypeExpression, otherArrowFunction.returnTypeExpression!);
277
- }
278
-
279
- return arrowFunction;
351
+ return this.visitElement(arrowFunction, other as JS.ArrowFunction);
280
352
  }
281
353
 
282
354
  /**
@@ -287,16 +359,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
287
359
  * @returns The visited await expression, or undefined if the visit was aborted
288
360
  */
289
361
  override async visitAwait(await_: JS.Await, other: J): Promise<J | undefined> {
290
- if (!this.match || other.kind !== JS.Kind.Await) {
291
- return this.abort(await_);
292
- }
293
-
294
- const otherAwait = other as JS.Await;
295
-
296
- // Visit expression
297
- await this.visit(await_.expression, otherAwait.expression);
298
-
299
- return await_;
362
+ return this.visitElement(await_, other as JS.Await);
300
363
  }
301
364
 
302
365
  /**
@@ -307,59 +370,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
307
370
  * @returns The visited JSX tag, or undefined if the visit was aborted
308
371
  */
309
372
  override async visitJsxTag(element: JSX.Tag, other: J): Promise<J | undefined> {
310
- if (!this.match || other.kind !== JS.Kind.JsxTag) {
311
- return this.abort(element);
312
- }
313
-
314
- const otherElement = other as JSX.Tag;
315
-
316
- // Visit open name
317
- await this.visit(element.openName.element, otherElement.openName.element);
318
- if (!this.match) return element;
319
-
320
- // Compare attributes
321
- if (element.attributes.length !== otherElement.attributes.length) {
322
- return this.abort(element);
323
- }
324
-
325
- // Visit attributes in lock step
326
- for (let i = 0; i < element.attributes.length; i++) {
327
- await this.visit(element.attributes[i].element, otherElement.attributes[i].element);
328
- if (!this.match) return element;
329
- }
330
-
331
- // Compare self-closing
332
- if (!!element.selfClosing !== !!otherElement.selfClosing) {
333
- return this.abort(element);
334
- }
335
-
336
- // Compare children
337
- if (!!element.children !== !!otherElement.children) {
338
- return this.abort(element);
339
- }
340
-
341
- if (element.children) {
342
- if (element.children.length !== otherElement.children!.length) {
343
- return this.abort(element);
344
- }
345
-
346
- // Visit children in lock step
347
- for (let i = 0; i < element.children.length; i++) {
348
- await this.visit(element.children[i], otherElement.children![i]);
349
- if (!this.match) return element;
350
- }
351
- }
352
-
353
- // Compare closing name
354
- if (!!element.closingName !== !!otherElement.closingName) {
355
- return this.abort(element);
356
- }
357
-
358
- if (element.closingName) {
359
- await this.visit(element.closingName.element, otherElement.closingName!.element);
360
- }
361
-
362
- return element;
373
+ return this.visitElement(element, other as JSX.Tag);
363
374
  }
364
375
 
365
376
  /**
@@ -370,26 +381,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
370
381
  * @returns The visited JSX attribute, or undefined if the visit was aborted
371
382
  */
372
383
  override async visitJsxAttribute(attribute: JSX.Attribute, other: J): Promise<J | undefined> {
373
- if (!this.match || other.kind !== JS.Kind.JsxAttribute) {
374
- return this.abort(attribute);
375
- }
376
-
377
- const otherAttribute = other as JSX.Attribute;
378
-
379
- // Visit key
380
- await this.visit(attribute.key, otherAttribute.key);
381
- if (!this.match) return attribute;
382
-
383
- // Compare value
384
- if (!!attribute.value !== !!otherAttribute.value) {
385
- return this.abort(attribute);
386
- }
387
-
388
- if (attribute.value) {
389
- await this.visit(attribute.value.element, otherAttribute.value!.element);
390
- }
391
-
392
- return attribute;
384
+ return this.visitElement(attribute, other as JSX.Attribute);
393
385
  }
394
386
 
395
387
  /**
@@ -400,16 +392,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
400
392
  * @returns The visited JSX spread attribute, or undefined if the visit was aborted
401
393
  */
402
394
  override async visitJsxSpreadAttribute(spread: JSX.SpreadAttribute, other: J): Promise<J | undefined> {
403
- if (!this.match || other.kind !== JS.Kind.JsxSpreadAttribute) {
404
- return this.abort(spread);
405
- }
406
-
407
- const otherSpread = other as JSX.SpreadAttribute;
408
-
409
- // Visit expression
410
- await this.visit(spread.expression.element, otherSpread.expression.element);
411
-
412
- return spread;
395
+ return this.visitElement(spread, other as JSX.SpreadAttribute);
413
396
  }
414
397
 
415
398
  /**
@@ -420,16 +403,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
420
403
  * @returns The visited JSX expression, or undefined if the visit was aborted
421
404
  */
422
405
  override async visitJsxEmbeddedExpression(expr: JSX.EmbeddedExpression, other: J): Promise<J | undefined> {
423
- if (!this.match || other.kind !== JS.Kind.JsxEmbeddedExpression) {
424
- return this.abort(expr);
425
- }
426
-
427
- const otherExpr = other as JSX.EmbeddedExpression;
428
-
429
- // Visit expression
430
- await this.visit(expr.expression.element, otherExpr.expression.element);
431
-
432
- return expr;
406
+ return this.visitElement(expr, other as JSX.EmbeddedExpression);
433
407
  }
434
408
 
435
409
  /**
@@ -440,20 +414,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
440
414
  * @returns The visited JSX namespaced name, or undefined if the visit was aborted
441
415
  */
442
416
  override async visitJsxNamespacedName(ns: JSX.NamespacedName, other: J): Promise<J | undefined> {
443
- if (!this.match || other.kind !== JS.Kind.JsxNamespacedName) {
444
- return this.abort(ns);
445
- }
446
-
447
- const otherNs = other as JSX.NamespacedName;
448
-
449
- // Visit namespace
450
- await this.visit(ns.namespace, otherNs.namespace);
451
- if (!this.match) return ns;
452
-
453
- // Visit name
454
- await this.visit(ns.name.element, otherNs.name.element);
455
-
456
- return ns;
417
+ return this.visitElement(ns, other as JSX.NamespacedName);
457
418
  }
458
419
 
459
420
  /**
@@ -464,20 +425,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
464
425
  * @returns The visited conditional type, or undefined if the visit was aborted
465
426
  */
466
427
  override async visitConditionalType(conditionalType: JS.ConditionalType, other: J): Promise<J | undefined> {
467
- if (!this.match || other.kind !== JS.Kind.ConditionalType) {
468
- return this.abort(conditionalType);
469
- }
470
-
471
- const otherConditionalType = other as JS.ConditionalType;
472
-
473
- // Visit check type
474
- await this.visit(conditionalType.checkType, otherConditionalType.checkType);
475
- if (!this.match) return conditionalType;
476
-
477
- // Visit condition
478
- await this.visit(conditionalType.condition.element, otherConditionalType.condition.element);
479
-
480
- return conditionalType;
428
+ return this.visitElement(conditionalType, other as JS.ConditionalType);
481
429
  }
482
430
 
483
431
  /**
@@ -488,16 +436,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
488
436
  * @returns The visited delete expression, or undefined if the visit was aborted
489
437
  */
490
438
  override async visitDelete(delete_: JS.Delete, other: J): Promise<J | undefined> {
491
- if (!this.match || other.kind !== JS.Kind.Delete) {
492
- return this.abort(delete_);
493
- }
494
-
495
- const otherDelete = other as JS.Delete;
496
-
497
- // Visit expression
498
- await this.visit(delete_.expression, otherDelete.expression);
499
-
500
- return delete_;
439
+ return this.visitElement(delete_, other as JS.Delete);
501
440
  }
502
441
 
503
442
  /**
@@ -508,16 +447,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
508
447
  * @returns The visited expression statement, or undefined if the visit was aborted
509
448
  */
510
449
  override async visitExpressionStatement(expressionStatement: JS.ExpressionStatement, other: J): Promise<J | undefined> {
511
- if (!this.match || other.kind !== JS.Kind.ExpressionStatement) {
512
- return this.abort(expressionStatement);
513
- }
514
-
515
- const otherExpressionStatement = other as JS.ExpressionStatement;
516
-
517
- // Visit expression
518
- await this.visit(expressionStatement.expression, otherExpressionStatement.expression);
519
-
520
- return expressionStatement;
450
+ return this.visitElement(expressionStatement, other as JS.ExpressionStatement);
521
451
  }
522
452
 
523
453
  /**
@@ -528,35 +458,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
528
458
  * @returns The visited expression with type arguments, or undefined if the visit was aborted
529
459
  */
530
460
  override async visitExpressionWithTypeArguments(expressionWithTypeArguments: JS.ExpressionWithTypeArguments, other: J): Promise<J | undefined> {
531
- if (!this.match || other.kind !== JS.Kind.ExpressionWithTypeArguments) {
532
- return this.abort(expressionWithTypeArguments);
533
- }
534
-
535
- const otherExpressionWithTypeArguments = other as JS.ExpressionWithTypeArguments;
536
-
537
- // Visit class
538
- await this.visit(expressionWithTypeArguments.clazz, otherExpressionWithTypeArguments.clazz);
539
- if (!this.match) return expressionWithTypeArguments;
540
-
541
- // Compare type arguments
542
- if (!!expressionWithTypeArguments.typeArguments !== !!otherExpressionWithTypeArguments.typeArguments) {
543
- return this.abort(expressionWithTypeArguments);
544
- }
545
-
546
- if (expressionWithTypeArguments.typeArguments) {
547
- if (expressionWithTypeArguments.typeArguments.elements.length !== otherExpressionWithTypeArguments.typeArguments!.elements.length) {
548
- return this.abort(expressionWithTypeArguments);
549
- }
550
-
551
- // Visit type arguments in lock step
552
- for (let i = 0; i < expressionWithTypeArguments.typeArguments.elements.length; i++) {
553
- await this.visit(expressionWithTypeArguments.typeArguments.elements[i].element,
554
- otherExpressionWithTypeArguments.typeArguments!.elements[i].element);
555
- if (!this.match) return expressionWithTypeArguments;
556
- }
557
- }
558
-
559
- return expressionWithTypeArguments;
461
+ return this.visitElement(expressionWithTypeArguments, other as JS.ExpressionWithTypeArguments);
560
462
  }
561
463
 
562
464
  /**
@@ -567,53 +469,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
567
469
  * @returns The visited function call, or undefined if the visit was aborted
568
470
  */
569
471
  override async visitFunctionCall(functionCall: JS.FunctionCall, other: J): Promise<J | undefined> {
570
- if (!this.match || other.kind !== JS.Kind.FunctionCall) {
571
- return this.abort(functionCall);
572
- }
573
-
574
- const otherFunctionCall = other as JS.FunctionCall;
575
-
576
- // Compare function
577
- if ((functionCall.function === undefined) !== (otherFunctionCall.function === undefined)) {
578
- return this.abort(functionCall);
579
- }
580
-
581
- // Visit function if present
582
- if (functionCall.function && otherFunctionCall.function) {
583
- await this.visit(functionCall.function.element, otherFunctionCall.function.element);
584
- if (!this.match) return functionCall;
585
- }
586
-
587
- // Compare typeParameters
588
- if ((functionCall.typeParameters === undefined) !== (otherFunctionCall.typeParameters === undefined)) {
589
- return this.abort(functionCall);
590
- }
591
-
592
- // Visit typeParameters if present
593
- if (functionCall.typeParameters && otherFunctionCall.typeParameters) {
594
- if (functionCall.typeParameters.elements.length !== otherFunctionCall.typeParameters.elements.length) {
595
- return this.abort(functionCall);
596
- }
597
-
598
- // Visit each type parameter in lock step
599
- for (let i = 0; i < functionCall.typeParameters.elements.length; i++) {
600
- await this.visit(functionCall.typeParameters.elements[i].element, otherFunctionCall.typeParameters.elements[i].element);
601
- if (!this.match) return functionCall;
602
- }
603
- }
604
-
605
- // Compare arguments
606
- if (functionCall.arguments.elements.length !== otherFunctionCall.arguments.elements.length) {
607
- return this.abort(functionCall);
608
- }
609
-
610
- // Visit each argument in lock step
611
- for (let i = 0; i < functionCall.arguments.elements.length; i++) {
612
- await this.visit(functionCall.arguments.elements[i].element, otherFunctionCall.arguments.elements[i].element);
613
- if (!this.match) return functionCall;
614
- }
615
-
616
- return functionCall;
472
+ return this.visitElement(functionCall, other as JS.FunctionCall);
617
473
  }
618
474
 
619
475
  /**
@@ -624,48 +480,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
624
480
  * @returns The visited function type, or undefined if the visit was aborted
625
481
  */
626
482
  override async visitFunctionType(functionType: JS.FunctionType, other: J): Promise<J | undefined> {
627
- if (!this.match || other.kind !== JS.Kind.FunctionType) {
628
- return this.abort(functionType);
629
- }
630
-
631
- const otherFunctionType = other as JS.FunctionType;
632
-
633
- // Compare constructor type
634
- if (!!functionType.constructorType.element !== !!otherFunctionType.constructorType.element) {
635
- return this.abort(functionType);
636
- }
637
-
638
- if (functionType.constructorType.element) {
639
- if (functionType.constructorType.element !== otherFunctionType.constructorType.element) {
640
- return this.abort(functionType);
641
- }
642
- }
643
-
644
- // Compare type parameters
645
- if (!!functionType.typeParameters !== !!otherFunctionType.typeParameters) {
646
- return this.abort(functionType);
647
- }
648
-
649
- if (functionType.typeParameters) {
650
- await this.visit(functionType.typeParameters, otherFunctionType.typeParameters!);
651
- if (!this.match) return functionType;
652
- }
653
-
654
- // Compare parameters
655
- if (functionType.parameters.elements.length !== otherFunctionType.parameters.elements.length) {
656
- return this.abort(functionType);
657
- }
658
-
659
- // Visit parameters in lock step
660
- for (let i = 0; i < functionType.parameters.elements.length; i++) {
661
- await this.visit(functionType.parameters.elements[i].element, otherFunctionType.parameters.elements[i].element);
662
- if (!this.match) return functionType;
663
- }
664
-
665
- // Compare return type
666
- await this.visit(functionType.returnType.element, otherFunctionType.returnType.element);
667
-
668
- return functionType;
483
+ return this.visitElement(functionType, other as JS.FunctionType);
669
484
  }
670
485
 
671
486
  /**
@@ -676,16 +491,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
676
491
  * @returns The visited infer type, or undefined if the visit was aborted
677
492
  */
678
493
  override async visitInferType(inferType: JS.InferType, other: J): Promise<J | undefined> {
679
- if (!this.match || other.kind !== JS.Kind.InferType) {
680
- return this.abort(inferType);
681
- }
682
-
683
- const otherInferType = other as JS.InferType;
684
-
685
- // Visit type parameter
686
- await this.visit(inferType.typeParameter.element, otherInferType.typeParameter.element);
687
-
688
- return inferType;
494
+ return this.visitElement(inferType, other as JS.InferType);
689
495
  }
690
496
 
691
497
  /**
@@ -696,59 +502,8 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
696
502
  * @returns The visited import type, or undefined if the visit was aborted
697
503
  */
698
504
  override async visitImportType(importType: JS.ImportType, other: J): Promise<J | undefined> {
699
- if (!this.match || other.kind !== JS.Kind.ImportType) {
700
- return this.abort(importType);
701
- }
702
-
703
- const otherImportType = other as JS.ImportType;
704
-
705
- // Compare has typeof
706
- if (importType.hasTypeof.element !== otherImportType.hasTypeof.element) {
707
- return this.abort(importType);
708
- }
709
-
710
- // Compare argument and attributes
711
- if (importType.argumentAndAttributes.elements.length !== otherImportType.argumentAndAttributes.elements.length) {
712
- return this.abort(importType);
713
- }
714
-
715
- // Visit argument and attributes in lock step
716
- for (let i = 0; i < importType.argumentAndAttributes.elements.length; i++) {
717
- await this.visit(importType.argumentAndAttributes.elements[i].element,
718
- otherImportType.argumentAndAttributes.elements[i].element);
719
- if (!this.match) return importType;
720
- }
721
-
722
- // Compare qualifier
723
- if (!!importType.qualifier !== !!otherImportType.qualifier) {
724
- return this.abort(importType);
725
- }
726
-
727
- if (importType.qualifier) {
728
- await this.visit(importType.qualifier.element, otherImportType.qualifier!.element);
729
- if (!this.match) return importType;
730
- }
731
-
732
- // Compare type arguments
733
- if (!!importType.typeArguments !== !!otherImportType.typeArguments) {
734
- return this.abort(importType);
735
- }
736
-
737
- if (importType.typeArguments) {
738
- if (importType.typeArguments.elements.length !== otherImportType.typeArguments!.elements.length) {
739
- return this.abort(importType);
740
- }
741
-
742
- // Visit type arguments in lock step
743
- for (let i = 0; i < importType.typeArguments.elements.length; i++) {
744
- await this.visit(importType.typeArguments.elements[i].element,
745
- otherImportType.typeArguments!.elements[i].element);
746
- if (!this.match) return importType;
747
- }
748
- }
749
-
750
- return importType;
751
- }
505
+ return this.visitElement(importType, other as JS.ImportType);
506
+ }
752
507
 
753
508
  /**
754
509
  * Overrides the visitImportDeclaration method to compare import declarations.
@@ -758,55 +513,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
758
513
  * @returns The visited import declaration, or undefined if the visit was aborted
759
514
  */
760
515
  override async visitImportDeclaration(jsImport: JS.Import, other: J): Promise<J | undefined> {
761
- if (!this.match || other.kind !== JS.Kind.Import) {
762
- return this.abort(jsImport);
763
- }
764
-
765
- const otherImport = other as JS.Import;
766
-
767
- // Compare modifiers
768
- if (jsImport.modifiers.length !== otherImport.modifiers.length) {
769
- return this.abort(jsImport);
770
- }
771
-
772
- // Visit each modifier in lock step
773
- for (let i = 0; i < jsImport.modifiers.length; i++) {
774
- await this.visit(jsImport.modifiers[i], otherImport.modifiers[i]);
775
- if (!this.match) return jsImport;
776
- }
777
-
778
- // Compare import clause
779
- if (!!jsImport.importClause !== !!otherImport.importClause) {
780
- return this.abort(jsImport);
781
- }
782
-
783
- if (jsImport.importClause) {
784
- await this.visit(jsImport.importClause, otherImport.importClause!);
785
- if (!this.match) return jsImport;
786
- }
787
-
788
- // Visit module specifier
789
- if (jsImport.moduleSpecifier) {
790
- await this.visit(jsImport.moduleSpecifier.element, otherImport.moduleSpecifier!.element);
791
- if (!this.match) return jsImport;
792
- }
793
-
794
- // Compare attributes
795
- if (!!jsImport.attributes !== !!otherImport.attributes) {
796
- return this.abort(jsImport);
797
- }
798
-
799
- if (jsImport.attributes) {
800
- await this.visit(jsImport.attributes, otherImport.attributes!);
801
- }
802
-
803
- // Compare initializer
804
- if (jsImport.initializer) {
805
- await this.visit(jsImport.initializer.element, otherImport.initializer!.element);
806
- if (!this.match) return jsImport;
807
- }
808
-
809
- return jsImport;
516
+ return this.visitElement(jsImport, other as JS.Import);
810
517
  }
811
518
 
812
519
  /**
@@ -817,32 +524,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
817
524
  * @returns The visited import clause, or undefined if the visit was aborted
818
525
  */
819
526
  override async visitImportClause(importClause: JS.ImportClause, other: J): Promise<J | undefined> {
820
- if (!this.match || other.kind !== JS.Kind.ImportClause) {
821
- return this.abort(importClause);
822
- }
823
-
824
- const otherImportClause = other as JS.ImportClause;
825
-
826
- // Compare name
827
- if (!!importClause.name !== !!otherImportClause.name) {
828
- return this.abort(importClause);
829
- }
830
-
831
- if (importClause.name) {
832
- await this.visit(importClause.name.element, otherImportClause.name!.element);
833
- if (!this.match) return importClause;
834
- }
835
-
836
- // Compare named bindings
837
- if (!!importClause.namedBindings !== !!otherImportClause.namedBindings) {
838
- return this.abort(importClause);
839
- }
840
-
841
- if (importClause.namedBindings) {
842
- await this.visit(importClause.namedBindings, otherImportClause.namedBindings!);
843
- }
844
-
845
- return importClause;
527
+ return this.visitElement(importClause, other as JS.ImportClause);
846
528
  }
847
529
 
848
530
  /**
@@ -853,24 +535,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
853
535
  * @returns The visited named imports, or undefined if the visit was aborted
854
536
  */
855
537
  override async visitNamedImports(namedImports: JS.NamedImports, other: J): Promise<J | undefined> {
856
- if (!this.match || other.kind !== JS.Kind.NamedImports) {
857
- return this.abort(namedImports);
858
- }
859
-
860
- const otherNamedImports = other as JS.NamedImports;
861
-
862
- // Compare elements
863
- if (namedImports.elements.elements.length !== otherNamedImports.elements.elements.length) {
864
- return this.abort(namedImports);
865
- }
866
-
867
- // Visit elements in lock step
868
- for (let i = 0; i < namedImports.elements.elements.length; i++) {
869
- await this.visit(namedImports.elements.elements[i].element, otherNamedImports.elements.elements[i].element);
870
- if (!this.match) return namedImports;
871
- }
872
-
873
- return namedImports;
538
+ return this.visitElement(namedImports, other as JS.NamedImports);
874
539
  }
875
540
 
876
541
  /**
@@ -881,27 +546,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
881
546
  * @returns The visited import specifier, or undefined if the visit was aborted
882
547
  */
883
548
  override async visitImportSpecifier(importSpecifier: JS.ImportSpecifier, other: J): Promise<J | undefined> {
884
- if (!this.match || other.kind !== JS.Kind.ImportSpecifier) {
885
- return this.abort(importSpecifier);
886
- }
887
-
888
- const otherImportSpecifier = other as JS.ImportSpecifier;
889
-
890
- // Compare import type
891
- if (!!importSpecifier.importType.element !== !!otherImportSpecifier.importType.element) {
892
- return this.abort(importSpecifier);
893
- }
894
-
895
- if (importSpecifier.importType.element) {
896
- if (importSpecifier.importType.element !== otherImportSpecifier.importType.element) {
897
- return this.abort(importSpecifier);
898
- }
899
- }
900
-
901
- // Visit specifier
902
- await this.visit(importSpecifier.specifier, otherImportSpecifier.specifier);
903
-
904
- return importSpecifier;
549
+ return this.visitElement(importSpecifier, other as JS.ImportSpecifier);
905
550
  }
906
551
 
907
552
  /**
@@ -912,24 +557,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
912
557
  * @returns The visited import attributes, or undefined if the visit was aborted
913
558
  */
914
559
  override async visitImportAttributes(importAttributes: JS.ImportAttributes, other: J): Promise<J | undefined> {
915
- if (!this.match || other.kind !== JS.Kind.ImportAttributes) {
916
- return this.abort(importAttributes);
917
- }
918
-
919
- const otherImportAttributes = other as JS.ImportAttributes;
920
-
921
- // Compare elements
922
- if (importAttributes.elements.elements.length !== otherImportAttributes.elements.elements.length) {
923
- return this.abort(importAttributes);
924
- }
925
-
926
- // Visit elements in lock step
927
- for (let i = 0; i < importAttributes.elements.elements.length; i++) {
928
- await this.visit(importAttributes.elements.elements[i].element, otherImportAttributes.elements.elements[i].element);
929
- if (!this.match) return importAttributes;
930
- }
931
-
932
- return importAttributes;
560
+ return this.visitElement(importAttributes, other as JS.ImportAttributes);
933
561
  }
934
562
 
935
563
  /**
@@ -940,28 +568,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
940
568
  * @returns The visited import type attributes, or undefined if the visit was aborted
941
569
  */
942
570
  override async visitImportTypeAttributes(importTypeAttributes: JS.ImportTypeAttributes, other: J): Promise<J | undefined> {
943
- if (!this.match || other.kind !== JS.Kind.ImportTypeAttributes) {
944
- return this.abort(importTypeAttributes);
945
- }
946
-
947
- const otherImportTypeAttributes = other as JS.ImportTypeAttributes;
948
-
949
- // Compare token
950
- await this.visit(importTypeAttributes.token.element, otherImportTypeAttributes.token.element);
951
- if (!this.match) return importTypeAttributes;
952
-
953
- // Compare elements
954
- if (importTypeAttributes.elements.elements.length !== otherImportTypeAttributes.elements.elements.length) {
955
- return this.abort(importTypeAttributes);
956
- }
957
-
958
- // Visit elements in lock step
959
- for (let i = 0; i < importTypeAttributes.elements.elements.length; i++) {
960
- await this.visit(importTypeAttributes.elements.elements[i].element, otherImportTypeAttributes.elements.elements[i].element);
961
- if (!this.match) return importTypeAttributes;
962
- }
963
-
964
- return importTypeAttributes;
571
+ return this.visitElement(importTypeAttributes, other as JS.ImportTypeAttributes);
965
572
  }
966
573
 
967
574
  /**
@@ -972,20 +579,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
972
579
  * @returns The visited import attribute, or undefined if the visit was aborted
973
580
  */
974
581
  override async visitImportAttribute(importAttribute: JS.ImportAttribute, other: J): Promise<J | undefined> {
975
- if (!this.match || other.kind !== JS.Kind.ImportAttribute) {
976
- return this.abort(importAttribute);
977
- }
978
-
979
- const otherImportAttribute = other as JS.ImportAttribute;
980
-
981
- // Visit name
982
- await this.visit(importAttribute.name, otherImportAttribute.name);
983
- if (!this.match) return importAttribute;
984
-
985
- // Visit value
986
- await this.visit(importAttribute.value.element, otherImportAttribute.value.element);
987
-
988
- return importAttribute;
582
+ return this.visitElement(importAttribute, other as JS.ImportAttribute);
989
583
  }
990
584
 
991
585
  /**
@@ -996,25 +590,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
996
590
  * @returns The visited binary expression, or undefined if the visit was aborted
997
591
  */
998
592
  override async visitBinaryExtensions(jsBinary: JS.Binary, other: J): Promise<J | undefined> {
999
- if (!this.match || other.kind !== JS.Kind.Binary) {
1000
- return this.abort(jsBinary);
1001
- }
1002
-
1003
- const otherBinary = other as JS.Binary;
1004
-
1005
- // Visit left operand
1006
- await this.visit(jsBinary.left, otherBinary.left);
1007
- if (!this.match) return jsBinary;
1008
-
1009
- // Compare operator
1010
- if (jsBinary.operator.element !== otherBinary.operator.element) {
1011
- return this.abort(jsBinary);
1012
- }
1013
-
1014
- // Visit right operand
1015
- await this.visit(jsBinary.right, otherBinary.right);
1016
-
1017
- return jsBinary;
593
+ return this.visitElement(jsBinary, other as JS.Binary);
1018
594
  }
1019
595
 
1020
596
  /**
@@ -1025,16 +601,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1025
601
  * @returns The visited literal type, or undefined if the visit was aborted
1026
602
  */
1027
603
  override async visitLiteralType(literalType: JS.LiteralType, other: J): Promise<J | undefined> {
1028
- if (!this.match || other.kind !== JS.Kind.LiteralType) {
1029
- return this.abort(literalType);
1030
- }
1031
-
1032
- const otherLiteralType = other as JS.LiteralType;
1033
-
1034
- // Visit literal
1035
- await this.visit(literalType.literal, otherLiteralType.literal);
1036
-
1037
- return literalType;
604
+ return this.visitElement(literalType, other as JS.LiteralType);
1038
605
  }
1039
606
 
1040
607
  /**
@@ -1045,58 +612,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1045
612
  * @returns The visited mapped type, or undefined if the visit was aborted
1046
613
  */
1047
614
  override async visitMappedType(mappedType: JS.MappedType, other: J): Promise<J | undefined> {
1048
- if (!this.match || other.kind !== JS.Kind.MappedType) {
1049
- return this.abort(mappedType);
1050
- }
1051
-
1052
- const otherMappedType = other as JS.MappedType;
1053
-
1054
- // Compare prefix token
1055
- if (!!mappedType.prefixToken !== !!otherMappedType.prefixToken) {
1056
- return this.abort(mappedType);
1057
- }
1058
-
1059
- if (mappedType.prefixToken) {
1060
- await this.visit(mappedType.prefixToken.element, otherMappedType.prefixToken!.element);
1061
- if (!this.match) return mappedType;
1062
- }
1063
-
1064
- // Compare has readonly
1065
- if (mappedType.hasReadonly.element !== otherMappedType.hasReadonly.element) {
1066
- return this.abort(mappedType);
1067
- }
1068
-
1069
- // Visit keys remapping
1070
- await this.visit(mappedType.keysRemapping, otherMappedType.keysRemapping);
1071
- if (!this.match) return mappedType;
1072
-
1073
- // Compare suffix token
1074
- if (!!mappedType.suffixToken !== !!otherMappedType.suffixToken) {
1075
- return this.abort(mappedType);
1076
- }
1077
-
1078
- if (mappedType.suffixToken) {
1079
- await this.visit(mappedType.suffixToken.element, otherMappedType.suffixToken!.element);
1080
- if (!this.match) return mappedType;
1081
- }
1082
-
1083
- // Compare has question token
1084
- if (mappedType.hasQuestionToken.element !== otherMappedType.hasQuestionToken.element) {
1085
- return this.abort(mappedType);
1086
- }
1087
-
1088
- // Compare value type
1089
- if (mappedType.valueType.elements.length !== otherMappedType.valueType.elements.length) {
1090
- return this.abort(mappedType);
1091
- }
1092
-
1093
- // Visit value type elements in lock step
1094
- for (let i = 0; i < mappedType.valueType.elements.length; i++) {
1095
- await this.visit(mappedType.valueType.elements[i].element, otherMappedType.valueType.elements[i].element);
1096
- if (!this.match) return mappedType;
1097
- }
1098
-
1099
- return mappedType;
615
+ return this.visitElement(mappedType, other as JS.MappedType);
1100
616
  }
1101
617
 
1102
618
  /**
@@ -1107,26 +623,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1107
623
  * @returns The visited keys remapping, or undefined if the visit was aborted
1108
624
  */
1109
625
  override async visitMappedTypeKeysRemapping(keysRemapping: JS.MappedType.KeysRemapping, other: J): Promise<J | undefined> {
1110
- if (!this.match || other.kind !== JS.Kind.MappedTypeKeysRemapping) {
1111
- return this.abort(keysRemapping);
1112
- }
1113
-
1114
- const otherKeysRemapping = other as JS.MappedType.KeysRemapping;
1115
-
1116
- // Visit type parameter
1117
- await this.visit(keysRemapping.typeParameter.element, otherKeysRemapping.typeParameter.element);
1118
- if (!this.match) return keysRemapping;
1119
-
1120
- // Compare name type
1121
- if (!!keysRemapping.nameType !== !!otherKeysRemapping.nameType) {
1122
- return this.abort(keysRemapping);
1123
- }
1124
-
1125
- if (keysRemapping.nameType) {
1126
- await this.visit(keysRemapping.nameType.element, otherKeysRemapping.nameType!.element);
1127
- }
1128
-
1129
- return keysRemapping;
626
+ return this.visitElement(keysRemapping, other as JS.MappedType.KeysRemapping);
1130
627
  }
1131
628
 
1132
629
  /**
@@ -1137,20 +634,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1137
634
  * @returns The visited mapped type parameter, or undefined if the visit was aborted
1138
635
  */
1139
636
  override async visitMappedTypeParameter(mappedTypeParameter: JS.MappedType.Parameter, other: J): Promise<J | undefined> {
1140
- if (!this.match || other.kind !== JS.Kind.MappedTypeParameter) {
1141
- return this.abort(mappedTypeParameter);
1142
- }
1143
-
1144
- const otherMappedTypeParameter = other as JS.MappedType.Parameter;
1145
-
1146
- // Visit name
1147
- await this.visit(mappedTypeParameter.name, otherMappedTypeParameter.name);
1148
- if (!this.match) return mappedTypeParameter;
1149
-
1150
- // Visit iterate type
1151
- await this.visit(mappedTypeParameter.iterateType.element, otherMappedTypeParameter.iterateType.element);
1152
-
1153
- return mappedTypeParameter;
637
+ return this.visitElement(mappedTypeParameter, other as JS.MappedType.Parameter);
1154
638
  }
1155
639
 
1156
640
  /**
@@ -1161,66 +645,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1161
645
  * @returns The visited object binding declarations, or undefined if the visit was aborted
1162
646
  */
1163
647
  override async visitObjectBindingPattern(objectBindingPattern: JS.ObjectBindingPattern, other: J): Promise<J | undefined> {
1164
- if (!this.match || other.kind !== JS.Kind.ObjectBindingPattern) {
1165
- return this.abort(objectBindingPattern);
1166
- }
1167
-
1168
- const otherObjectBindingPattern = other as JS.ObjectBindingPattern;
1169
-
1170
- // Compare leading annotations
1171
- if (objectBindingPattern.leadingAnnotations.length !== otherObjectBindingPattern.leadingAnnotations.length) {
1172
- return this.abort(objectBindingPattern);
1173
- }
1174
-
1175
- // Visit leading annotations in lock step
1176
- for (let i = 0; i < objectBindingPattern.leadingAnnotations.length; i++) {
1177
- await this.visit(objectBindingPattern.leadingAnnotations[i], otherObjectBindingPattern.leadingAnnotations[i]);
1178
- if (!this.match) return objectBindingPattern;
1179
- }
1180
-
1181
- // Compare modifiers
1182
- if (objectBindingPattern.modifiers.length !== otherObjectBindingPattern.modifiers.length) {
1183
- return this.abort(objectBindingPattern);
1184
- }
1185
-
1186
- // Visit modifiers in lock step
1187
- for (let i = 0; i < objectBindingPattern.modifiers.length; i++) {
1188
- await this.visit(objectBindingPattern.modifiers[i], otherObjectBindingPattern.modifiers[i]);
1189
- if (!this.match) return objectBindingPattern;
1190
- }
1191
-
1192
- // Compare type expression
1193
- if (!!objectBindingPattern.typeExpression !== !!otherObjectBindingPattern.typeExpression) {
1194
- return this.abort(objectBindingPattern);
1195
- }
1196
-
1197
- if (objectBindingPattern.typeExpression) {
1198
- await this.visit(objectBindingPattern.typeExpression, otherObjectBindingPattern.typeExpression!);
1199
- if (!this.match) return objectBindingPattern;
1200
- }
1201
-
1202
- // Compare bindings
1203
- if (objectBindingPattern.bindings.elements.length !== otherObjectBindingPattern.bindings.elements.length) {
1204
- return this.abort(objectBindingPattern);
1205
- }
1206
-
1207
- // Visit bindings in lock step
1208
- for (let i = 0; i < objectBindingPattern.bindings.elements.length; i++) {
1209
- await this.visit(objectBindingPattern.bindings.elements[i].element,
1210
- otherObjectBindingPattern.bindings.elements[i].element);
1211
- if (!this.match) return objectBindingPattern;
1212
- }
1213
-
1214
- // Compare initializer
1215
- if (!!objectBindingPattern.initializer !== !!otherObjectBindingPattern.initializer) {
1216
- return this.abort(objectBindingPattern);
1217
- }
1218
-
1219
- if (objectBindingPattern.initializer) {
1220
- await this.visit(objectBindingPattern.initializer.element, otherObjectBindingPattern.initializer!.element);
1221
- }
1222
-
1223
- return objectBindingPattern;
648
+ return this.visitElement(objectBindingPattern, other as JS.ObjectBindingPattern);
1224
649
  }
1225
650
 
1226
651
  /**
@@ -1231,26 +656,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1231
656
  * @returns The visited property assignment, or undefined if the visit was aborted
1232
657
  */
1233
658
  override async visitPropertyAssignment(propertyAssignment: JS.PropertyAssignment, other: J): Promise<J | undefined> {
1234
- if (!this.match || other.kind !== JS.Kind.PropertyAssignment) {
1235
- return this.abort(propertyAssignment);
1236
- }
1237
-
1238
- const otherPropertyAssignment = other as JS.PropertyAssignment;
1239
-
1240
- // Visit name
1241
- await this.visit(propertyAssignment.name.element, otherPropertyAssignment.name.element);
1242
- if (!this.match) return propertyAssignment;
1243
-
1244
- // Compare initializer
1245
- if (!!propertyAssignment.initializer !== !!otherPropertyAssignment.initializer) {
1246
- return this.abort(propertyAssignment);
1247
- }
1248
-
1249
- if (propertyAssignment.initializer) {
1250
- await this.visit(propertyAssignment.initializer, otherPropertyAssignment.initializer!);
1251
- }
1252
-
1253
- return propertyAssignment;
659
+ return this.visitElement(propertyAssignment, other as JS.PropertyAssignment);
1254
660
  }
1255
661
 
1256
662
  /**
@@ -1261,20 +667,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1261
667
  * @returns The visited satisfies expression, or undefined if the visit was aborted
1262
668
  */
1263
669
  override async visitSatisfiesExpression(satisfiesExpression: JS.SatisfiesExpression, other: J): Promise<J | undefined> {
1264
- if (!this.match || other.kind !== JS.Kind.SatisfiesExpression) {
1265
- return this.abort(satisfiesExpression);
1266
- }
1267
-
1268
- const otherSatisfiesExpression = other as JS.SatisfiesExpression;
1269
-
1270
- // Visit expression
1271
- await this.visit(satisfiesExpression.expression, otherSatisfiesExpression.expression);
1272
- if (!this.match) return satisfiesExpression;
1273
-
1274
- // Visit satisfies type
1275
- await this.visit(satisfiesExpression.satisfiesType.element, otherSatisfiesExpression.satisfiesType.element);
1276
-
1277
- return satisfiesExpression;
670
+ return this.visitElement(satisfiesExpression, other as JS.SatisfiesExpression);
1278
671
  }
1279
672
 
1280
673
  /**
@@ -1285,35 +678,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1285
678
  * @returns The visited scoped variable declarations, or undefined if the visit was aborted
1286
679
  */
1287
680
  override async visitScopedVariableDeclarations(scopedVariableDeclarations: JS.ScopedVariableDeclarations, other: J): Promise<J | undefined> {
1288
- if (!this.match || other.kind !== JS.Kind.ScopedVariableDeclarations) {
1289
- return this.abort(scopedVariableDeclarations);
1290
- }
1291
-
1292
- const otherScopedVariableDeclarations = other as JS.ScopedVariableDeclarations;
1293
-
1294
- // Compare modifiers
1295
- if (scopedVariableDeclarations.modifiers.length !== otherScopedVariableDeclarations.modifiers.length) {
1296
- return this.abort(scopedVariableDeclarations);
1297
- }
1298
-
1299
- // Visit modifiers in lock step
1300
- for (let i = 0; i < scopedVariableDeclarations.modifiers.length; i++) {
1301
- await this.visit(scopedVariableDeclarations.modifiers[i], otherScopedVariableDeclarations.modifiers[i]);
1302
- if (!this.match) return scopedVariableDeclarations;
1303
- }
1304
-
1305
- // Compare variables
1306
- if (scopedVariableDeclarations.variables.length !== otherScopedVariableDeclarations.variables.length) {
1307
- return this.abort(scopedVariableDeclarations);
1308
- }
1309
-
1310
- // Visit variables in lock step
1311
- for (let i = 0; i < scopedVariableDeclarations.variables.length; i++) {
1312
- await this.visit(scopedVariableDeclarations.variables[i].element, otherScopedVariableDeclarations.variables[i].element);
1313
- if (!this.match) return scopedVariableDeclarations;
1314
- }
1315
-
1316
- return scopedVariableDeclarations;
681
+ return this.visitElement(scopedVariableDeclarations, other as JS.ScopedVariableDeclarations);
1317
682
  }
1318
683
 
1319
684
  /**
@@ -1324,16 +689,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1324
689
  * @returns The visited statement expression, or undefined if the visit was aborted
1325
690
  */
1326
691
  override async visitStatementExpression(statementExpression: JS.StatementExpression, other: J): Promise<J | undefined> {
1327
- if (!this.match || other.kind !== JS.Kind.StatementExpression) {
1328
- return this.abort(statementExpression);
1329
- }
1330
-
1331
- const otherStatementExpression = other as JS.StatementExpression;
1332
-
1333
- // Visit statement
1334
- await this.visit(statementExpression.statement, otherStatementExpression.statement);
1335
-
1336
- return statementExpression;
692
+ return this.visitElement(statementExpression, other as JS.StatementExpression);
1337
693
  }
1338
694
 
1339
695
  /**
@@ -1344,44 +700,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1344
700
  * @returns The visited tagged template expression, or undefined if the visit was aborted
1345
701
  */
1346
702
  override async visitTaggedTemplateExpression(taggedTemplateExpression: JS.TaggedTemplateExpression, other: J): Promise<J | undefined> {
1347
- if (!this.match || other.kind !== JS.Kind.TaggedTemplateExpression) {
1348
- return this.abort(taggedTemplateExpression);
1349
- }
1350
-
1351
- const otherTaggedTemplateExpression = other as JS.TaggedTemplateExpression;
1352
-
1353
- // Compare tag
1354
- if (!!taggedTemplateExpression.tag !== !!otherTaggedTemplateExpression.tag) {
1355
- return this.abort(taggedTemplateExpression);
1356
- }
1357
-
1358
- if (taggedTemplateExpression.tag) {
1359
- await this.visit(taggedTemplateExpression.tag.element, otherTaggedTemplateExpression.tag!.element);
1360
- if (!this.match) return taggedTemplateExpression;
1361
- }
1362
-
1363
- // Compare type arguments
1364
- if (!!taggedTemplateExpression.typeArguments !== !!otherTaggedTemplateExpression.typeArguments) {
1365
- return this.abort(taggedTemplateExpression);
1366
- }
1367
-
1368
- if (taggedTemplateExpression.typeArguments) {
1369
- if (taggedTemplateExpression.typeArguments.elements.length !== otherTaggedTemplateExpression.typeArguments!.elements.length) {
1370
- return this.abort(taggedTemplateExpression);
1371
- }
1372
-
1373
- // Visit type arguments in lock step
1374
- for (let i = 0; i < taggedTemplateExpression.typeArguments.elements.length; i++) {
1375
- await this.visit(taggedTemplateExpression.typeArguments.elements[i].element,
1376
- otherTaggedTemplateExpression.typeArguments!.elements[i].element);
1377
- if (!this.match) return taggedTemplateExpression;
1378
- }
1379
- }
1380
-
1381
- // Visit template expression
1382
- await this.visit(taggedTemplateExpression.templateExpression, otherTaggedTemplateExpression.templateExpression);
1383
-
1384
- return taggedTemplateExpression;
703
+ return this.visitElement(taggedTemplateExpression, other as JS.TaggedTemplateExpression);
1385
704
  }
1386
705
 
1387
706
  /**
@@ -1392,28 +711,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1392
711
  * @returns The visited template expression, or undefined if the visit was aborted
1393
712
  */
1394
713
  override async visitTemplateExpression(templateExpression: JS.TemplateExpression, other: J): Promise<J | undefined> {
1395
- if (!this.match || other.kind !== JS.Kind.TemplateExpression) {
1396
- return this.abort(templateExpression);
1397
- }
1398
-
1399
- const otherTemplateExpression = other as JS.TemplateExpression;
1400
-
1401
- // Visit head
1402
- await this.visit(templateExpression.head, otherTemplateExpression.head);
1403
- if (!this.match) return templateExpression;
1404
-
1405
- // Compare spans
1406
- if (templateExpression.spans.length !== otherTemplateExpression.spans.length) {
1407
- return this.abort(templateExpression);
1408
- }
1409
-
1410
- // Visit spans in lock step
1411
- for (let i = 0; i < templateExpression.spans.length; i++) {
1412
- await this.visit(templateExpression.spans[i].element, otherTemplateExpression.spans[i].element);
1413
- if (!this.match) return templateExpression;
1414
- }
1415
-
1416
- return templateExpression;
714
+ return this.visitElement(templateExpression, other as JS.TemplateExpression);
1417
715
  }
1418
716
 
1419
717
  /**
@@ -1424,20 +722,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1424
722
  * @returns The visited template expression span, or undefined if the visit was aborted
1425
723
  */
1426
724
  override async visitTemplateExpressionSpan(span: JS.TemplateExpression.Span, other: J): Promise<J | undefined> {
1427
- if (!this.match || other.kind !== JS.Kind.TemplateExpressionSpan) {
1428
- return this.abort(span);
1429
- }
1430
-
1431
- const otherSpan = other as JS.TemplateExpression.Span;
1432
-
1433
- // Visit expression
1434
- await this.visit(span.expression, otherSpan.expression);
1435
- if (!this.match) return span;
1436
-
1437
- // Visit tail
1438
- await this.visit(span.tail, otherSpan.tail);
1439
-
1440
- return span;
725
+ return this.visitElement(span, other as JS.TemplateExpression.Span);
1441
726
  }
1442
727
 
1443
728
  /**
@@ -1448,24 +733,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1448
733
  * @returns The visited tuple, or undefined if the visit was aborted
1449
734
  */
1450
735
  override async visitTuple(tuple: JS.Tuple, other: J): Promise<J | undefined> {
1451
- if (!this.match || other.kind !== JS.Kind.Tuple) {
1452
- return this.abort(tuple);
1453
- }
1454
-
1455
- const otherTuple = other as JS.Tuple;
1456
-
1457
- // Compare elements
1458
- if (tuple.elements.elements.length !== otherTuple.elements.elements.length) {
1459
- return this.abort(tuple);
1460
- }
1461
-
1462
- // Visit elements in lock step
1463
- for (let i = 0; i < tuple.elements.elements.length; i++) {
1464
- await this.visit(tuple.elements.elements[i].element, otherTuple.elements.elements[i].element);
1465
- if (!this.match) return tuple;
1466
- }
1467
-
1468
- return tuple;
736
+ return this.visitElement(tuple, other as JS.Tuple);
1469
737
  }
1470
738
 
1471
739
  /**
@@ -1476,41 +744,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1476
744
  * @returns The visited type declaration, or undefined if the visit was aborted
1477
745
  */
1478
746
  override async visitTypeDeclaration(typeDeclaration: JS.TypeDeclaration, other: J): Promise<J | undefined> {
1479
- if (!this.match || other.kind !== JS.Kind.TypeDeclaration) {
1480
- return this.abort(typeDeclaration);
1481
- }
1482
-
1483
- const otherTypeDeclaration = other as JS.TypeDeclaration;
1484
-
1485
- // Compare modifiers
1486
- if (typeDeclaration.modifiers.length !== otherTypeDeclaration.modifiers.length) {
1487
- return this.abort(typeDeclaration);
1488
- }
1489
-
1490
- // Visit modifiers in lock step
1491
- for (let i = 0; i < typeDeclaration.modifiers.length; i++) {
1492
- await this.visit(typeDeclaration.modifiers[i], otherTypeDeclaration.modifiers[i]);
1493
- if (!this.match) return typeDeclaration;
1494
- }
1495
-
1496
- // Visit name
1497
- await this.visit(typeDeclaration.name.element, otherTypeDeclaration.name.element);
1498
- if (!this.match) return typeDeclaration;
1499
-
1500
- // Compare type parameters
1501
- if (!!typeDeclaration.typeParameters !== !!otherTypeDeclaration.typeParameters) {
1502
- return this.abort(typeDeclaration);
1503
- }
1504
-
1505
- if (typeDeclaration.typeParameters) {
1506
- await this.visit(typeDeclaration.typeParameters, otherTypeDeclaration.typeParameters!);
1507
- if (!this.match) return typeDeclaration;
1508
- }
1509
-
1510
- // Visit initializer
1511
- await this.visit(typeDeclaration.initializer.element, otherTypeDeclaration.initializer.element);
1512
-
1513
- return typeDeclaration;
747
+ return this.visitElement(typeDeclaration, other as JS.TypeDeclaration);
1514
748
  }
1515
749
 
1516
750
  /**
@@ -1521,16 +755,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1521
755
  * @returns The visited typeof expression, or undefined if the visit was aborted
1522
756
  */
1523
757
  override async visitTypeOf(typeOf: JS.TypeOf, other: J): Promise<J | undefined> {
1524
- if (!this.match || other.kind !== JS.Kind.TypeOf) {
1525
- return this.abort(typeOf);
1526
- }
1527
-
1528
- const otherTypeOf = other as JS.TypeOf;
1529
-
1530
- // Visit expression
1531
- await this.visit(typeOf.expression, otherTypeOf.expression);
1532
-
1533
- return typeOf;
758
+ return this.visitElement(typeOf, other as JS.TypeOf);
1534
759
  }
1535
760
 
1536
761
  /**
@@ -1541,16 +766,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1541
766
  * @returns The visited type tree expression, or undefined if the visit was aborted
1542
767
  */
1543
768
  override async visitTypeTreeExpression(typeTreeExpression: JS.TypeTreeExpression, other: J): Promise<J | undefined> {
1544
- if (!this.match || other.kind !== JS.Kind.TypeTreeExpression) {
1545
- return this.abort(typeTreeExpression);
1546
- }
1547
-
1548
- const otherTypeTreeExpression = other as JS.TypeTreeExpression;
1549
-
1550
- // Visit expression
1551
- await this.visit(typeTreeExpression.expression, otherTypeTreeExpression.expression);
1552
-
1553
- return typeTreeExpression;
769
+ return this.visitElement(typeTreeExpression, other as JS.TypeTreeExpression);
1554
770
  }
1555
771
 
1556
772
  /**
@@ -1561,18 +777,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1561
777
  * @returns The visited as expression, or undefined if the visit was aborted
1562
778
  */
1563
779
  override async visitAs(as_: JS.As, other: J): Promise<J | undefined> {
1564
- if (!this.match || other.kind !== JS.Kind.As) {
1565
- return this.abort(as_);
1566
- }
1567
-
1568
- const otherAs = other as JS.As;
1569
-
1570
- // Visit left and right operands in lock step
1571
- await this.visit(as_.left.element, otherAs.left.element);
1572
- if (!this.match) return as_;
1573
-
1574
- await this.visit(as_.right, otherAs.right);
1575
- return as_;
780
+ return this.visitElement(as_, other as JS.As);
1576
781
  }
1577
782
 
1578
783
  /**
@@ -1583,25 +788,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1583
788
  * @returns The visited assignment operation, or undefined if the visit was aborted
1584
789
  */
1585
790
  override async visitAssignmentOperationExtensions(assignmentOperation: JS.AssignmentOperation, other: J): Promise<J | undefined> {
1586
- if (!this.match || other.kind !== JS.Kind.AssignmentOperation) {
1587
- return this.abort(assignmentOperation);
1588
- }
1589
-
1590
- const otherAssignmentOperation = other as JS.AssignmentOperation;
1591
-
1592
- // Visit variable
1593
- await this.visit(assignmentOperation.variable, otherAssignmentOperation.variable);
1594
- if (!this.match) return assignmentOperation;
1595
-
1596
- // Compare operator
1597
- if (assignmentOperation.operator.element !== otherAssignmentOperation.operator.element) {
1598
- return this.abort(assignmentOperation);
1599
- }
1600
-
1601
- // Visit assignment
1602
- await this.visit(assignmentOperation.assignment, otherAssignmentOperation.assignment);
1603
-
1604
- return assignmentOperation;
791
+ return this.visitElement(assignmentOperation, other as JS.AssignmentOperation);
1605
792
  }
1606
793
 
1607
794
  /**
@@ -1612,21 +799,8 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1612
799
  * @returns The visited indexed access type, or undefined if the visit was aborted
1613
800
  */
1614
801
  override async visitIndexedAccessType(indexedAccessType: JS.IndexedAccessType, other: J): Promise<J | undefined> {
1615
- if (!this.match || other.kind !== JS.Kind.IndexedAccessType) {
1616
- return this.abort(indexedAccessType);
1617
- }
1618
-
1619
- const otherIndexedAccessType = other as JS.IndexedAccessType;
1620
-
1621
- // Visit object type
1622
- await this.visit(indexedAccessType.objectType, otherIndexedAccessType.objectType);
1623
- if (!this.match) return indexedAccessType;
1624
-
1625
- // Visit index type
1626
- await this.visit(indexedAccessType.indexType, otherIndexedAccessType.indexType);
1627
-
1628
- return indexedAccessType;
1629
- }
802
+ return this.visitElement(indexedAccessType, other as JS.IndexedAccessType);
803
+ }
1630
804
 
1631
805
  /**
1632
806
  * Overrides the visitIndexType method to compare index types.
@@ -1636,16 +810,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1636
810
  * @returns The visited index type, or undefined if the visit was aborted
1637
811
  */
1638
812
  override async visitIndexedAccessTypeIndexType(indexType: JS.IndexedAccessType.IndexType, other: J): Promise<J | undefined> {
1639
- if (!this.match || other.kind !== JS.Kind.IndexType) {
1640
- return this.abort(indexType);
1641
- }
1642
-
1643
- const otherIndexType = other as JS.IndexedAccessType.IndexType;
1644
-
1645
- // Visit element
1646
- await this.visit(indexType.element.element, otherIndexType.element.element);
1647
-
1648
- return indexType;
813
+ return this.visitElement(indexType, other as JS.IndexedAccessType.IndexType);
1649
814
  }
1650
815
 
1651
816
  /**
@@ -1656,35 +821,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1656
821
  * @returns The visited type query, or undefined if the visit was aborted
1657
822
  */
1658
823
  override async visitTypeQuery(typeQuery: JS.TypeQuery, other: J): Promise<J | undefined> {
1659
- if (!this.match || other.kind !== JS.Kind.TypeQuery) {
1660
- return this.abort(typeQuery);
1661
- }
1662
-
1663
- const otherTypeQuery = other as JS.TypeQuery;
1664
-
1665
- // Visit type expression
1666
- await this.visit(typeQuery.typeExpression, otherTypeQuery.typeExpression);
1667
- if (!this.match) return typeQuery;
1668
-
1669
- // Compare type arguments
1670
- if (!!typeQuery.typeArguments !== !!otherTypeQuery.typeArguments) {
1671
- return this.abort(typeQuery);
1672
- }
1673
-
1674
- if (typeQuery.typeArguments) {
1675
- if (typeQuery.typeArguments.elements.length !== otherTypeQuery.typeArguments!.elements.length) {
1676
- return this.abort(typeQuery);
1677
- }
1678
-
1679
- // Visit type arguments in lock step
1680
- for (let i = 0; i < typeQuery.typeArguments.elements.length; i++) {
1681
- await this.visit(typeQuery.typeArguments.elements[i].element,
1682
- otherTypeQuery.typeArguments!.elements[i].element);
1683
- if (!this.match) return typeQuery;
1684
- }
1685
- }
1686
-
1687
- return typeQuery;
824
+ return this.visitElement(typeQuery, other as JS.TypeQuery);
1688
825
  }
1689
826
 
1690
827
  /**
@@ -1695,16 +832,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1695
832
  * @returns The visited type info, or undefined if the visit was aborted
1696
833
  */
1697
834
  override async visitTypeInfo(typeInfo: JS.TypeInfo, other: J): Promise<J | undefined> {
1698
- if (!this.match || other.kind !== JS.Kind.TypeInfo) {
1699
- return this.abort(typeInfo);
1700
- }
1701
-
1702
- const otherTypeInfo = other as JS.TypeInfo;
1703
-
1704
- // Visit type identifier
1705
- await this.visit(typeInfo.typeIdentifier, otherTypeInfo.typeIdentifier);
1706
-
1707
- return typeInfo;
835
+ return this.visitElement(typeInfo, other as JS.TypeInfo);
1708
836
  }
1709
837
 
1710
838
  /**
@@ -1715,16 +843,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1715
843
  * @returns The visited computed property name, or undefined if the visit was aborted
1716
844
  */
1717
845
  override async visitComputedPropertyName(computedPropertyName: JS.ComputedPropertyName, other: J): Promise<J | undefined> {
1718
- if (!this.match || other.kind !== JS.Kind.ComputedPropertyName) {
1719
- return this.abort(computedPropertyName);
1720
- }
1721
-
1722
- const otherComputedPropertyName = other as JS.ComputedPropertyName;
1723
-
1724
- // Visit expression
1725
- await this.visit(computedPropertyName.expression.element, otherComputedPropertyName.expression.element);
1726
-
1727
- return computedPropertyName;
846
+ return this.visitElement(computedPropertyName, other as JS.ComputedPropertyName);
1728
847
  }
1729
848
 
1730
849
  /**
@@ -1735,16 +854,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1735
854
  * @returns The visited type operator, or undefined if the visit was aborted
1736
855
  */
1737
856
  override async visitTypeOperator(typeOperator: JS.TypeOperator, other: J): Promise<J | undefined> {
1738
- if (!this.match || other.kind !== JS.Kind.TypeOperator) {
1739
- return this.abort(typeOperator);
1740
- }
1741
-
1742
- const otherTypeOperator = other as JS.TypeOperator;
1743
-
1744
- // Visit expression
1745
- await this.visit(typeOperator.expression.element, otherTypeOperator.expression.element);
1746
-
1747
- return typeOperator;
857
+ return this.visitElement(typeOperator, other as JS.TypeOperator);
1748
858
  }
1749
859
 
1750
860
  /**
@@ -1755,31 +865,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1755
865
  * @returns The visited type predicate, or undefined if the visit was aborted
1756
866
  */
1757
867
  override async visitTypePredicate(typePredicate: JS.TypePredicate, other: J): Promise<J | undefined> {
1758
- if (!this.match || other.kind !== JS.Kind.TypePredicate) {
1759
- return this.abort(typePredicate);
1760
- }
1761
-
1762
- const otherTypePredicate = other as JS.TypePredicate;
1763
-
1764
- // Compare asserts
1765
- if (typePredicate.asserts.element !== otherTypePredicate.asserts.element) {
1766
- return this.abort(typePredicate);
1767
- }
1768
-
1769
- // Visit parameter name
1770
- await this.visit(typePredicate.parameterName, otherTypePredicate.parameterName);
1771
- if (!this.match) return typePredicate;
1772
-
1773
- // Compare expression
1774
- if (!!typePredicate.expression !== !!otherTypePredicate.expression) {
1775
- return this.abort(typePredicate);
1776
- }
1777
-
1778
- if (typePredicate.expression) {
1779
- await this.visit(typePredicate.expression.element, otherTypePredicate.expression!.element);
1780
- }
1781
-
1782
- return typePredicate;
868
+ return this.visitElement(typePredicate, other as JS.TypePredicate);
1783
869
  }
1784
870
 
1785
871
  /**
@@ -1790,24 +876,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1790
876
  * @returns The visited union, or undefined if the visit was aborted
1791
877
  */
1792
878
  override async visitUnion(union: JS.Union, other: J): Promise<J | undefined> {
1793
- if (!this.match || other.kind !== JS.Kind.Union) {
1794
- return this.abort(union);
1795
- }
1796
-
1797
- const otherUnion = other as JS.Union;
1798
-
1799
- // Compare types
1800
- if (union.types.length !== otherUnion.types.length) {
1801
- return this.abort(union);
1802
- }
1803
-
1804
- // Visit types in lock step
1805
- for (let i = 0; i < union.types.length; i++) {
1806
- await this.visit(union.types[i].element, otherUnion.types[i].element);
1807
- if (!this.match) return union;
1808
- }
1809
-
1810
- return union;
879
+ return this.visitElement(union, other as JS.Union);
1811
880
  }
1812
881
 
1813
882
  /**
@@ -1818,24 +887,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1818
887
  * @returns The visited intersection, or undefined if the visit was aborted
1819
888
  */
1820
889
  override async visitIntersection(intersection: JS.Intersection, other: J): Promise<J | undefined> {
1821
- if (!this.match || other.kind !== JS.Kind.Intersection) {
1822
- return this.abort(intersection);
1823
- }
1824
-
1825
- const otherIntersection = other as JS.Intersection;
1826
-
1827
- // Compare types
1828
- if (intersection.types.length !== otherIntersection.types.length) {
1829
- return this.abort(intersection);
1830
- }
1831
-
1832
- // Visit types in lock step
1833
- for (let i = 0; i < intersection.types.length; i++) {
1834
- await this.visit(intersection.types[i].element, otherIntersection.types[i].element);
1835
- if (!this.match) return intersection;
1836
- }
1837
-
1838
- return intersection;
890
+ return this.visitElement(intersection, other as JS.Intersection);
1839
891
  }
1840
892
 
1841
893
  /**
@@ -1846,27 +898,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1846
898
  * @returns The visited annotated type, or undefined if the visit was aborted
1847
899
  */
1848
900
  override async visitAnnotatedType(annotatedType: J.AnnotatedType, other: J): Promise<J | undefined> {
1849
- if (!this.match || other.kind !== J.Kind.AnnotatedType) {
1850
- return this.abort(annotatedType);
1851
- }
1852
-
1853
- const otherAnnotatedType = other as J.AnnotatedType;
1854
-
1855
- // Compare annotations
1856
- if (annotatedType.annotations.length !== otherAnnotatedType.annotations.length) {
1857
- return this.abort(annotatedType);
1858
- }
1859
-
1860
- // Visit each annotation in lock step
1861
- for (let i = 0; i < annotatedType.annotations.length; i++) {
1862
- await this.visit(annotatedType.annotations[i], otherAnnotatedType.annotations[i]);
1863
- if (!this.match) return annotatedType;
1864
- }
1865
-
1866
- // Visit type expression
1867
- await this.visit(annotatedType.typeExpression, otherAnnotatedType.typeExpression);
1868
-
1869
- return annotatedType;
901
+ return this.visitElement(annotatedType, other as J.AnnotatedType);
1870
902
  }
1871
903
 
1872
904
  /**
@@ -1877,35 +909,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1877
909
  * @returns The visited annotation, or undefined if the visit was aborted
1878
910
  */
1879
911
  override async visitAnnotation(annotation: J.Annotation, other: J): Promise<J | undefined> {
1880
- if (!this.match || other.kind !== J.Kind.Annotation) {
1881
- return this.abort(annotation);
1882
- }
1883
-
1884
- const otherAnnotation = other as J.Annotation;
1885
-
1886
- // Visit annotation type
1887
- await this.visit(annotation.annotationType, otherAnnotation.annotationType);
1888
- if (!this.match) return annotation;
1889
-
1890
- // Compare arguments
1891
- if ((annotation.arguments === undefined) !== (otherAnnotation.arguments === undefined)) {
1892
- return this.abort(annotation);
1893
- }
1894
-
1895
- // If both have arguments, visit them
1896
- if (annotation.arguments && otherAnnotation.arguments) {
1897
- if (annotation.arguments.elements.length !== otherAnnotation.arguments.elements.length) {
1898
- return this.abort(annotation);
1899
- }
1900
-
1901
- // Visit each argument in lock step
1902
- for (let i = 0; i < annotation.arguments.elements.length; i++) {
1903
- await this.visit(annotation.arguments.elements[i].element, otherAnnotation.arguments.elements[i].element);
1904
- if (!this.match) return annotation;
1905
- }
1906
- }
1907
-
1908
- return annotation;
912
+ return this.visitElement(annotation, other as J.Annotation);
1909
913
  }
1910
914
 
1911
915
  /**
@@ -1916,20 +920,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1916
920
  * @returns The visited array access expression, or undefined if the visit was aborted
1917
921
  */
1918
922
  override async visitArrayAccess(arrayAccess: J.ArrayAccess, other: J): Promise<J | undefined> {
1919
- if (!this.match || other.kind !== J.Kind.ArrayAccess) {
1920
- return this.abort(arrayAccess);
1921
- }
1922
-
1923
- const otherArrayAccess = other as J.ArrayAccess;
1924
-
1925
- // Visit indexed expression
1926
- await this.visit(arrayAccess.indexed, otherArrayAccess.indexed);
1927
- if (!this.match) return arrayAccess;
1928
-
1929
- // Visit dimension
1930
- await this.visit(arrayAccess.dimension, otherArrayAccess.dimension);
1931
-
1932
- return arrayAccess;
923
+ return this.visitElement(arrayAccess, other as J.ArrayAccess);
1933
924
  }
1934
925
 
1935
926
  /**
@@ -1940,21 +931,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1940
931
  * @returns The visited array dimension, or undefined if the visit was aborted
1941
932
  */
1942
933
  override async visitArrayDimension(arrayDimension: J.ArrayDimension, other: J): Promise<J | undefined> {
1943
- if (!this.match || other.kind !== J.Kind.ArrayDimension) {
1944
- return this.abort(arrayDimension);
1945
- }
1946
-
1947
- const otherArrayDimension = other as J.ArrayDimension;
1948
-
1949
- // Visit index
1950
- if (arrayDimension.index && otherArrayDimension.index) {
1951
- await this.visit(arrayDimension.index.element, otherArrayDimension.index.element);
1952
- } else if (arrayDimension.index !== otherArrayDimension.index) {
1953
- // One has an index and the other doesn't
1954
- return this.abort(arrayDimension);
1955
- }
1956
-
1957
- return arrayDimension;
934
+ return this.visitElement(arrayDimension, other as J.ArrayDimension);
1958
935
  }
1959
936
 
1960
937
  /**
@@ -1965,30 +942,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1965
942
  * @returns The visited array type, or undefined if the visit was aborted
1966
943
  */
1967
944
  override async visitArrayType(arrayType: J.ArrayType, other: J): Promise<J | undefined> {
1968
- if (!this.match || other.kind !== J.Kind.ArrayType) {
1969
- return this.abort(arrayType);
1970
- }
1971
-
1972
- const otherArrayType = other as J.ArrayType;
1973
-
1974
- // Visit element type
1975
- await this.visit(arrayType.elementType, otherArrayType.elementType);
1976
- if (!this.match) return arrayType;
1977
-
1978
- // Compare annotations
1979
- if ((arrayType.annotations?.length || 0) !== (otherArrayType.annotations?.length || 0)) {
1980
- return this.abort(arrayType);
1981
- }
1982
-
1983
- // Visit annotations if they exist
1984
- if (arrayType.annotations && otherArrayType.annotations) {
1985
- for (let i = 0; i < arrayType.annotations.length; i++) {
1986
- await this.visit(arrayType.annotations[i], otherArrayType.annotations[i]);
1987
- if (!this.match) return arrayType;
1988
- }
1989
- }
1990
-
1991
- return arrayType;
945
+ return this.visitElement(arrayType, other as J.ArrayType);
1992
946
  }
1993
947
 
1994
948
  /**
@@ -1999,27 +953,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
1999
953
  * @returns The visited assert statement, or undefined if the visit was aborted
2000
954
  */
2001
955
  override async visitAssert(anAssert: J.Assert, other: J): Promise<J | undefined> {
2002
- if (!this.match || other.kind !== J.Kind.Assert) {
2003
- return this.abort(anAssert);
2004
- }
2005
-
2006
- const otherAssert = other as J.Assert;
2007
-
2008
- // Visit condition
2009
- await this.visit(anAssert.condition, otherAssert.condition);
2010
- if (!this.match) return anAssert;
2011
-
2012
- // Compare detail
2013
- if ((anAssert.detail !== undefined) !== (otherAssert.detail !== undefined)) {
2014
- return this.abort(anAssert);
2015
- }
2016
-
2017
- // Visit detail if it exists
2018
- if (anAssert.detail && otherAssert.detail) {
2019
- await this.visit(anAssert.detail.element, otherAssert.detail.element);
2020
- }
2021
-
2022
- return anAssert;
956
+ return this.visitElement(anAssert, other as J.Assert);
2023
957
  }
2024
958
 
2025
959
  /**
@@ -2030,25 +964,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2030
964
  * @returns The visited assignment expression, or undefined if the visit was aborted
2031
965
  */
2032
966
  override async visitAssignment(assignment: J.Assignment, other: J): Promise<J | undefined> {
2033
- if (!this.match || other.kind !== J.Kind.Assignment) {
2034
- return this.abort(assignment);
2035
- }
2036
-
2037
- const otherAssignment = other as J.Assignment;
2038
-
2039
- // Visit variable
2040
- await this.visit(assignment.variable, otherAssignment.variable);
2041
- if (!this.match) return assignment;
2042
-
2043
- // Visit assignment
2044
- if (assignment.assignment && otherAssignment.assignment) {
2045
- await this.visit(assignment.assignment.element, otherAssignment.assignment.element);
2046
- } else if (assignment.assignment !== otherAssignment.assignment) {
2047
- // One has an assignment and the other doesn't
2048
- return this.abort(assignment);
2049
- }
2050
-
2051
- return assignment;
967
+ return this.visitElement(assignment, other as J.Assignment);
2052
968
  }
2053
969
 
2054
970
  /**
@@ -2059,25 +975,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2059
975
  * @returns The visited assignment operation expression, or undefined if the visit was aborted
2060
976
  */
2061
977
  override async visitAssignmentOperation(assignOp: J.AssignmentOperation, other: J): Promise<J | undefined> {
2062
- if (!this.match || other.kind !== J.Kind.AssignmentOperation) {
2063
- return this.abort(assignOp);
2064
- }
2065
-
2066
- const otherAssignOp = other as J.AssignmentOperation;
2067
-
2068
- // Visit variable
2069
- await this.visit(assignOp.variable, otherAssignOp.variable);
2070
- if (!this.match) return assignOp;
2071
-
2072
- // Compare operator
2073
- if (assignOp.operator.element !== otherAssignOp.operator.element) {
2074
- return this.abort(assignOp);
2075
- }
2076
-
2077
- // Visit assignment
2078
- await this.visit(assignOp.assignment, otherAssignOp.assignment);
2079
-
2080
- return assignOp;
978
+ return this.visitElement(assignOp, other as J.AssignmentOperation);
2081
979
  }
2082
980
 
2083
981
  /**
@@ -2088,23 +986,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2088
986
  * @returns The visited break statement, or undefined if the visit was aborted
2089
987
  */
2090
988
  override async visitBreak(breakStatement: J.Break, other: J): Promise<J | undefined> {
2091
- if (!this.match || other.kind !== J.Kind.Break) {
2092
- return this.abort(breakStatement);
2093
- }
2094
-
2095
- const otherBreak = other as J.Break;
2096
-
2097
- // Compare label presence
2098
- if ((breakStatement.label !== undefined) !== (otherBreak.label !== undefined)) {
2099
- return this.abort(breakStatement);
2100
- }
2101
-
2102
- // Visit label if it exists
2103
- if (breakStatement.label && otherBreak.label) {
2104
- await this.visit(breakStatement.label, otherBreak.label);
2105
- }
2106
-
2107
- return breakStatement;
989
+ return this.visitElement(breakStatement, other as J.Break);
2108
990
  }
2109
991
 
2110
992
  /**
@@ -2115,56 +997,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2115
997
  * @returns The visited case statement, or undefined if the visit was aborted
2116
998
  */
2117
999
  override async visitCase(aCase: J.Case, other: J): Promise<J | undefined> {
2118
- if (!this.match || other.kind !== J.Kind.Case) {
2119
- return this.abort(aCase);
2120
- }
2121
-
2122
- const otherCase = other as J.Case;
2123
-
2124
- // Compare case labels
2125
- if (aCase.caseLabels.elements.length !== otherCase.caseLabels.elements.length) {
2126
- return this.abort(aCase);
2127
- }
2128
-
2129
- // Visit each case label in lock step
2130
- for (let i = 0; i < aCase.caseLabels.elements.length; i++) {
2131
- await this.visit(aCase.caseLabels.elements[i].element, otherCase.caseLabels.elements[i].element);
2132
- if (!this.match) return aCase;
2133
- }
2134
-
2135
- // Compare statements
2136
- if (aCase.statements.elements.length !== otherCase.statements.elements.length) {
2137
- return this.abort(aCase);
2138
- }
2139
-
2140
- // Visit each statement in lock step
2141
- for (let i = 0; i < aCase.statements.elements.length; i++) {
2142
- await this.visit(aCase.statements.elements[i].element, otherCase.statements.elements[i].element);
2143
- if (!this.match) return aCase;
2144
- }
2145
-
2146
- // Compare body presence
2147
- if ((aCase.body !== undefined) !== (otherCase.body !== undefined)) {
2148
- return this.abort(aCase);
2149
- }
2150
-
2151
- // Visit body if it exists
2152
- if (aCase.body && otherCase.body) {
2153
- await this.visit(aCase.body.element, otherCase.body.element);
2154
- if (!this.match) return aCase;
2155
- }
2156
-
2157
- // Compare guard presence
2158
- if ((aCase.guard !== undefined) !== (otherCase.guard !== undefined)) {
2159
- return this.abort(aCase);
2160
- }
2161
-
2162
- // Visit guard if it exists
2163
- if (aCase.guard && otherCase.guard) {
2164
- await this.visit(aCase.guard, otherCase.guard);
2165
- }
2166
-
2167
- return aCase;
1000
+ return this.visitElement(aCase, other as J.Case);
2168
1001
  }
2169
1002
 
2170
1003
  /**
@@ -2175,129 +1008,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2175
1008
  * @returns The visited class declaration, or undefined if the visit was aborted
2176
1009
  */
2177
1010
  override async visitClassDeclaration(classDecl: J.ClassDeclaration, other: J): Promise<J | undefined> {
2178
- if (!this.match || other.kind !== J.Kind.ClassDeclaration) {
2179
- return this.abort(classDecl);
2180
- }
2181
-
2182
- const otherClassDecl = other as J.ClassDeclaration;
2183
-
2184
- // Compare leading annotations
2185
- if (classDecl.leadingAnnotations.length !== otherClassDecl.leadingAnnotations.length) {
2186
- return this.abort(classDecl);
2187
- }
2188
-
2189
- // Visit each leading annotation in lock step
2190
- for (let i = 0; i < classDecl.leadingAnnotations.length; i++) {
2191
- await this.visit(classDecl.leadingAnnotations[i], otherClassDecl.leadingAnnotations[i]);
2192
- if (!this.match) return classDecl;
2193
- }
2194
-
2195
- // Compare modifiers
2196
- if (classDecl.modifiers.length !== otherClassDecl.modifiers.length) {
2197
- return this.abort(classDecl);
2198
- }
2199
-
2200
- // Visit each modifier in lock step
2201
- for (let i = 0; i < classDecl.modifiers.length; i++) {
2202
- await this.visit(classDecl.modifiers[i], otherClassDecl.modifiers[i]);
2203
- if (!this.match) return classDecl;
2204
- }
2205
-
2206
- // Visit class kind
2207
- await this.visit(classDecl.classKind, otherClassDecl.classKind);
2208
- if (!this.match) return classDecl;
2209
-
2210
- // Visit name
2211
- await this.visit(classDecl.name, otherClassDecl.name);
2212
- if (!this.match) return classDecl;
2213
-
2214
- // Compare type parameters presence
2215
- if ((classDecl.typeParameters !== undefined) !== (otherClassDecl.typeParameters !== undefined)) {
2216
- return this.abort(classDecl);
2217
- }
2218
-
2219
- // Visit type parameters if they exist
2220
- if (classDecl.typeParameters && otherClassDecl.typeParameters) {
2221
- if (classDecl.typeParameters.elements.length !== otherClassDecl.typeParameters.elements.length) {
2222
- return this.abort(classDecl);
2223
- }
2224
-
2225
- // Visit each type parameter in lock step
2226
- for (let i = 0; i < classDecl.typeParameters.elements.length; i++) {
2227
- await this.visit(classDecl.typeParameters.elements[i].element, otherClassDecl.typeParameters.elements[i].element);
2228
- if (!this.match) return classDecl;
2229
- }
2230
- }
2231
-
2232
- // Compare primary constructor presence
2233
- if ((classDecl.primaryConstructor !== undefined) !== (otherClassDecl.primaryConstructor !== undefined)) {
2234
- return this.abort(classDecl);
2235
- }
2236
-
2237
- // Visit primary constructor if it exists
2238
- if (classDecl.primaryConstructor && otherClassDecl.primaryConstructor) {
2239
- if (classDecl.primaryConstructor.elements.length !== otherClassDecl.primaryConstructor.elements.length) {
2240
- return this.abort(classDecl);
2241
- }
2242
-
2243
- // Visit each primary constructor element in lock step
2244
- for (let i = 0; i < classDecl.primaryConstructor.elements.length; i++) {
2245
- await this.visit(classDecl.primaryConstructor.elements[i].element, otherClassDecl.primaryConstructor.elements[i].element);
2246
- if (!this.match) return classDecl;
2247
- }
2248
- }
2249
-
2250
- // Compare extends presence
2251
- if ((classDecl.extends !== undefined) !== (otherClassDecl.extends !== undefined)) {
2252
- return this.abort(classDecl);
2253
- }
2254
-
2255
- // Visit extends if it exists
2256
- if (classDecl.extends && otherClassDecl.extends) {
2257
- await this.visit(classDecl.extends.element, otherClassDecl.extends.element);
2258
- if (!this.match) return classDecl;
2259
- }
2260
-
2261
- // Compare implements presence
2262
- if ((classDecl.implements !== undefined) !== (otherClassDecl.implements !== undefined)) {
2263
- return this.abort(classDecl);
2264
- }
2265
-
2266
- // Visit implements if it exists
2267
- if (classDecl.implements && otherClassDecl.implements) {
2268
- if (classDecl.implements.elements.length !== otherClassDecl.implements.elements.length) {
2269
- return this.abort(classDecl);
2270
- }
2271
-
2272
- // Visit each implements element in lock step
2273
- for (let i = 0; i < classDecl.implements.elements.length; i++) {
2274
- await this.visit(classDecl.implements.elements[i].element, otherClassDecl.implements.elements[i].element);
2275
- if (!this.match) return classDecl;
2276
- }
2277
- }
2278
-
2279
- // Compare permitting presence
2280
- if ((classDecl.permitting !== undefined) !== (otherClassDecl.permitting !== undefined)) {
2281
- return this.abort(classDecl);
2282
- }
2283
-
2284
- // Visit permitting if it exists
2285
- if (classDecl.permitting && otherClassDecl.permitting) {
2286
- if (classDecl.permitting.elements.length !== otherClassDecl.permitting.elements.length) {
2287
- return this.abort(classDecl);
2288
- }
2289
-
2290
- // Visit each permitting element in lock step
2291
- for (let i = 0; i < classDecl.permitting.elements.length; i++) {
2292
- await this.visit(classDecl.permitting.elements[i].element, otherClassDecl.permitting.elements[i].element);
2293
- if (!this.match) return classDecl;
2294
- }
2295
- }
2296
-
2297
- // Visit body
2298
- await this.visit(classDecl.body, otherClassDecl.body);
2299
-
2300
- return classDecl;
1011
+ return this.visitElement(classDecl, other as J.ClassDeclaration);
2301
1012
  }
2302
1013
 
2303
1014
  /**
@@ -2308,24 +1019,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2308
1019
  * @returns The visited class declaration kind, or undefined if the visit was aborted
2309
1020
  */
2310
1021
  override async visitClassDeclarationKind(kind: J.ClassDeclaration.Kind, other: J): Promise<J | undefined> {
2311
- if (!this.match || other.kind !== J.Kind.ClassDeclarationKind) {
2312
- return this.abort(kind);
2313
- }
2314
-
2315
- const otherKind = other as J.ClassDeclaration.Kind;
2316
-
2317
- // Compare annotations
2318
- if (kind.annotations.length !== otherKind.annotations.length) {
2319
- return this.abort(kind);
2320
- }
2321
-
2322
- // Visit each annotation in lock step
2323
- for (let i = 0; i < kind.annotations.length; i++) {
2324
- await this.visit(kind.annotations[i], otherKind.annotations[i]);
2325
- if (!this.match) return kind;
2326
- }
2327
-
2328
- return kind;
1022
+ return this.visitElement(kind, other as J.ClassDeclaration.Kind);
2329
1023
  }
2330
1024
 
2331
1025
  /**
@@ -2336,46 +1030,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2336
1030
  * @returns The visited compilation unit, or undefined if the visit was aborted
2337
1031
  */
2338
1032
  override async visitCompilationUnit(compilationUnit: J.CompilationUnit, other: J): Promise<J | undefined> {
2339
- if (!this.match || other.kind !== J.Kind.CompilationUnit) {
2340
- return this.abort(compilationUnit);
2341
- }
2342
-
2343
- const otherCompilationUnit = other as J.CompilationUnit;
2344
-
2345
- // Compare package declaration presence
2346
- if ((compilationUnit.packageDeclaration !== undefined) !== (otherCompilationUnit.packageDeclaration !== undefined)) {
2347
- return this.abort(compilationUnit);
2348
- }
2349
-
2350
- // Visit package declaration if it exists
2351
- if (compilationUnit.packageDeclaration && otherCompilationUnit.packageDeclaration) {
2352
- await this.visit(compilationUnit.packageDeclaration.element, otherCompilationUnit.packageDeclaration.element);
2353
- if (!this.match) return compilationUnit;
2354
- }
2355
-
2356
- // Compare imports
2357
- if (compilationUnit.imports.length !== otherCompilationUnit.imports.length) {
2358
- return this.abort(compilationUnit);
2359
- }
2360
-
2361
- // Visit each import in lock step
2362
- for (let i = 0; i < compilationUnit.imports.length; i++) {
2363
- await this.visit(compilationUnit.imports[i].element, otherCompilationUnit.imports[i].element);
2364
- if (!this.match) return compilationUnit;
2365
- }
2366
-
2367
- // Compare classes
2368
- if (compilationUnit.classes.length !== otherCompilationUnit.classes.length) {
2369
- return this.abort(compilationUnit);
2370
- }
2371
-
2372
- // Visit each class in lock step
2373
- for (let i = 0; i < compilationUnit.classes.length; i++) {
2374
- await this.visit(compilationUnit.classes[i], otherCompilationUnit.classes[i]);
2375
- if (!this.match) return compilationUnit;
2376
- }
2377
-
2378
- return compilationUnit;
1033
+ return this.visitElement(compilationUnit, other as J.CompilationUnit);
2379
1034
  }
2380
1035
 
2381
1036
  /**
@@ -2386,23 +1041,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2386
1041
  * @returns The visited continue statement, or undefined if the visit was aborted
2387
1042
  */
2388
1043
  override async visitContinue(continueStatement: J.Continue, other: J): Promise<J | undefined> {
2389
- if (!this.match || other.kind !== J.Kind.Continue) {
2390
- return this.abort(continueStatement);
2391
- }
2392
-
2393
- const otherContinue = other as J.Continue;
2394
-
2395
- // Compare label presence
2396
- if ((continueStatement.label !== undefined) !== (otherContinue.label !== undefined)) {
2397
- return this.abort(continueStatement);
2398
- }
2399
-
2400
- // Visit label if it exists
2401
- if (continueStatement.label && otherContinue.label) {
2402
- await this.visit(continueStatement.label, otherContinue.label);
2403
- }
2404
-
2405
- return continueStatement;
1044
+ return this.visitElement(continueStatement, other as J.Continue);
2406
1045
  }
2407
1046
 
2408
1047
  /**
@@ -2413,16 +1052,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2413
1052
  * @returns The visited control parentheses, or undefined if the visit was aborted
2414
1053
  */
2415
1054
  override async visitControlParentheses<T extends J>(controlParens: J.ControlParentheses<T>, other: J): Promise<J | undefined> {
2416
- if (!this.match || other.kind !== J.Kind.ControlParentheses) {
2417
- return this.abort(controlParens);
2418
- }
2419
-
2420
- const otherControlParens = other as J.ControlParentheses<J>;
2421
-
2422
- // Visit tree
2423
- await this.visit(controlParens.tree.element, otherControlParens.tree.element);
2424
-
2425
- return controlParens;
1055
+ return this.visitElement(controlParens, other as J.ControlParentheses<T>);
2426
1056
  }
2427
1057
 
2428
1058
  /**
@@ -2433,28 +1063,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2433
1063
  * @returns The visited deconstruction pattern, or undefined if the visit was aborted
2434
1064
  */
2435
1065
  override async visitDeconstructionPattern(pattern: J.DeconstructionPattern, other: J): Promise<J | undefined> {
2436
- if (!this.match || other.kind !== J.Kind.DeconstructionPattern) {
2437
- return this.abort(pattern);
2438
- }
2439
-
2440
- const otherPattern = other as J.DeconstructionPattern;
2441
-
2442
- // Visit deconstructor
2443
- await this.visit(pattern.deconstructor, otherPattern.deconstructor);
2444
- if (!this.match) return pattern;
2445
-
2446
- // Compare nested elements
2447
- if (pattern.nested.elements.length !== otherPattern.nested.elements.length) {
2448
- return this.abort(pattern);
2449
- }
2450
-
2451
- // Visit each nested element in lock step
2452
- for (let i = 0; i < pattern.nested.elements.length; i++) {
2453
- await this.visit(pattern.nested.elements[i].element, otherPattern.nested.elements[i].element);
2454
- if (!this.match) return pattern;
2455
- }
2456
-
2457
- return pattern;
1066
+ return this.visitElement(pattern, other as J.DeconstructionPattern);
2458
1067
  }
2459
1068
 
2460
1069
  /**
@@ -2465,20 +1074,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2465
1074
  * @returns The visited do-while loop, or undefined if the visit was aborted
2466
1075
  */
2467
1076
  override async visitDoWhileLoop(doWhileLoop: J.DoWhileLoop, other: J): Promise<J | undefined> {
2468
- if (!this.match || other.kind !== J.Kind.DoWhileLoop) {
2469
- return this.abort(doWhileLoop);
2470
- }
2471
-
2472
- const otherDoWhileLoop = other as J.DoWhileLoop;
2473
-
2474
- // Visit body
2475
- await this.visit(doWhileLoop.body.element, otherDoWhileLoop.body.element);
2476
- if (!this.match) return doWhileLoop;
2477
-
2478
- // Visit while condition
2479
- await this.visit(doWhileLoop.whileCondition.element, otherDoWhileLoop.whileCondition.element);
2480
-
2481
- return doWhileLoop;
1077
+ return this.visitElement(doWhileLoop, other as J.DoWhileLoop);
2482
1078
  }
2483
1079
 
2484
1080
  /**
@@ -2489,13 +1085,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2489
1085
  * @returns The visited empty statement, or undefined if the visit was aborted
2490
1086
  */
2491
1087
  override async visitEmpty(empty: J.Empty, other: J): Promise<J | undefined> {
2492
- if (!this.match || other.kind !== J.Kind.Empty) {
2493
- return this.abort(empty);
2494
- }
2495
-
2496
- // Empty statements have no properties to compare, so we just check the kind
2497
-
2498
- return empty;
1088
+ return this.visitElement(empty, other as J.Empty);
2499
1089
  }
2500
1090
 
2501
1091
  /**
@@ -2506,38 +1096,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2506
1096
  * @returns The visited enum value, or undefined if the visit was aborted
2507
1097
  */
2508
1098
  override async visitEnumValue(enumValue: J.EnumValue, other: J): Promise<J | undefined> {
2509
- if (!this.match || other.kind !== J.Kind.EnumValue) {
2510
- return this.abort(enumValue);
2511
- }
2512
-
2513
- const otherEnumValue = other as J.EnumValue;
2514
-
2515
- // Compare annotations
2516
- if (enumValue.annotations.length !== otherEnumValue.annotations.length) {
2517
- return this.abort(enumValue);
2518
- }
2519
-
2520
- // Visit each annotation in lock step
2521
- for (let i = 0; i < enumValue.annotations.length; i++) {
2522
- await this.visit(enumValue.annotations[i], otherEnumValue.annotations[i]);
2523
- if (!this.match) return enumValue;
2524
- }
2525
-
2526
- // Visit name
2527
- await this.visit(enumValue.name, otherEnumValue.name);
2528
- if (!this.match) return enumValue;
2529
-
2530
- // Compare initializer presence
2531
- if ((enumValue.initializer !== undefined) !== (otherEnumValue.initializer !== undefined)) {
2532
- return this.abort(enumValue);
2533
- }
2534
-
2535
- // Visit initializer if it exists
2536
- if (enumValue.initializer && otherEnumValue.initializer) {
2537
- await this.visit(enumValue.initializer, otherEnumValue.initializer);
2538
- }
2539
-
2540
- return enumValue;
1099
+ return this.visitElement(enumValue, other as J.EnumValue);
2541
1100
  }
2542
1101
 
2543
1102
  /**
@@ -2548,24 +1107,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2548
1107
  * @returns The visited enum value set, or undefined if the visit was aborted
2549
1108
  */
2550
1109
  override async visitEnumValueSet(enumValueSet: J.EnumValueSet, other: J): Promise<J | undefined> {
2551
- if (!this.match || other.kind !== J.Kind.EnumValueSet) {
2552
- return this.abort(enumValueSet);
2553
- }
2554
-
2555
- const otherEnumValueSet = other as J.EnumValueSet;
2556
-
2557
- // Compare enums
2558
- if (enumValueSet.enums.length !== otherEnumValueSet.enums.length) {
2559
- return this.abort(enumValueSet);
2560
- }
2561
-
2562
- // Visit each enum in lock step
2563
- for (let i = 0; i < enumValueSet.enums.length; i++) {
2564
- await this.visit(enumValueSet.enums[i].element, otherEnumValueSet.enums[i].element);
2565
- if (!this.match) return enumValueSet;
2566
- }
2567
-
2568
- return enumValueSet;
1110
+ return this.visitElement(enumValueSet, other as J.EnumValueSet);
2569
1111
  }
2570
1112
 
2571
1113
  /**
@@ -2576,18 +1118,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2576
1118
  * @returns The visited erroneous node, or undefined if the visit was aborted
2577
1119
  */
2578
1120
  override async visitErroneous(erroneous: J.Erroneous, other: J): Promise<J | undefined> {
2579
- if (!this.match || other.kind !== J.Kind.Erroneous) {
2580
- return this.abort(erroneous);
2581
- }
2582
-
2583
- const otherErroneous = other as J.Erroneous;
2584
-
2585
- // Compare text
2586
- if (erroneous.text !== otherErroneous.text) {
2587
- return this.abort(erroneous);
2588
- }
2589
-
2590
- return erroneous;
1121
+ return this.visitElement(erroneous, other as J.Erroneous);
2591
1122
  }
2592
1123
 
2593
1124
  /**
@@ -2598,21 +1129,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2598
1129
  * @returns The visited field access expression, or undefined if the visit was aborted
2599
1130
  */
2600
1131
  override async visitFieldAccess(fieldAccess: J.FieldAccess, other: J): Promise<J | undefined> {
2601
- if (!this.match || other.kind !== J.Kind.FieldAccess) {
2602
- return this.abort(fieldAccess);
2603
- }
2604
-
2605
- const otherFieldAccess = other as J.FieldAccess;
2606
-
2607
- // Visit target
2608
- await this.visit(fieldAccess.target, otherFieldAccess.target);
2609
- if (!this.match) return fieldAccess;
2610
-
2611
- // Visit name
2612
- await this.visit(fieldAccess.name.element, otherFieldAccess.name.element);
2613
- if (!this.match) return fieldAccess;
2614
-
2615
- return fieldAccess;
1132
+ return this.visitElement(fieldAccess, other as J.FieldAccess);
2616
1133
  }
2617
1134
 
2618
1135
  /**
@@ -2623,21 +1140,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2623
1140
  * @returns The visited for-each loop, or undefined if the visit was aborted
2624
1141
  */
2625
1142
  override async visitForEachLoop(forEachLoop: J.ForEachLoop, other: J): Promise<J | undefined> {
2626
- if (!this.match || other.kind !== J.Kind.ForEachLoop) {
2627
- return this.abort(forEachLoop);
2628
- }
2629
-
2630
- const otherForEachLoop = other as J.ForEachLoop;
2631
-
2632
- // Visit control
2633
- await this.visit(forEachLoop.control, otherForEachLoop.control);
2634
- if (!this.match) return forEachLoop;
2635
-
2636
- // Visit body
2637
- await this.visit(forEachLoop.body.element, otherForEachLoop.body.element);
2638
- if (!this.match) return forEachLoop;
2639
-
2640
- return forEachLoop;
1143
+ return this.visitElement(forEachLoop, other as J.ForEachLoop);
2641
1144
  }
2642
1145
 
2643
1146
  /**
@@ -2648,21 +1151,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2648
1151
  * @returns The visited for-each loop control, or undefined if the visit was aborted
2649
1152
  */
2650
1153
  override async visitForEachLoopControl(control: J.ForEachLoop.Control, other: J): Promise<J | undefined> {
2651
- if (!this.match || other.kind !== J.Kind.ForEachLoopControl) {
2652
- return this.abort(control);
2653
- }
2654
-
2655
- const otherControl = other as J.ForEachLoop.Control;
2656
-
2657
- // Visit variable
2658
- await this.visit(control.variable.element, otherControl.variable.element);
2659
- if (!this.match) return control;
2660
-
2661
- // Visit iterable
2662
- await this.visit(control.iterable.element, otherControl.iterable.element);
2663
- if (!this.match) return control;
2664
-
2665
- return control;
1154
+ return this.visitElement(control, other as J.ForEachLoop.Control);
2666
1155
  }
2667
1156
 
2668
1157
  /**
@@ -2673,21 +1162,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2673
1162
  * @returns The visited for loop, or undefined if the visit was aborted
2674
1163
  */
2675
1164
  override async visitForLoop(forLoop: J.ForLoop, other: J): Promise<J | undefined> {
2676
- if (!this.match || other.kind !== J.Kind.ForLoop) {
2677
- return this.abort(forLoop);
2678
- }
2679
-
2680
- const otherForLoop = other as J.ForLoop;
2681
-
2682
- // Visit control
2683
- await this.visit(forLoop.control, otherForLoop.control);
2684
- if (!this.match) return forLoop;
2685
-
2686
- // Visit body
2687
- await this.visit(forLoop.body.element, otherForLoop.body.element);
2688
- if (!this.match) return forLoop;
2689
-
2690
- return forLoop;
1165
+ return this.visitElement(forLoop, other as J.ForLoop);
2691
1166
  }
2692
1167
 
2693
1168
  /**
@@ -2698,46 +1173,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2698
1173
  * @returns The visited for loop control, or undefined if the visit was aborted
2699
1174
  */
2700
1175
  override async visitForLoopControl(control: J.ForLoop.Control, other: J): Promise<J | undefined> {
2701
- if (!this.match || other.kind !== J.Kind.ForLoopControl) {
2702
- return this.abort(control);
2703
- }
2704
-
2705
- const otherControl = other as J.ForLoop.Control;
2706
-
2707
- // Compare init statements
2708
- if (control.init.length !== otherControl.init.length) {
2709
- return this.abort(control);
2710
- }
2711
-
2712
- // Visit each init statement in lock step
2713
- for (let i = 0; i < control.init.length; i++) {
2714
- await this.visit(control.init[i].element, otherControl.init[i].element);
2715
- if (!this.match) return control;
2716
- }
2717
-
2718
- // Compare condition
2719
- if ((control.condition === undefined) !== (otherControl.condition === undefined)) {
2720
- return this.abort(control);
2721
- }
2722
-
2723
- // Visit condition if present
2724
- if (control.condition && otherControl.condition) {
2725
- await this.visit(control.condition.element, otherControl.condition.element);
2726
- if (!this.match) return control;
2727
- }
2728
-
2729
- // Compare update statements
2730
- if (control.update.length !== otherControl.update.length) {
2731
- return this.abort(control);
2732
- }
2733
-
2734
- // Visit each update statement in lock step
2735
- for (let i = 0; i < control.update.length; i++) {
2736
- await this.visit(control.update[i].element, otherControl.update[i].element);
2737
- if (!this.match) return control;
2738
- }
2739
-
2740
- return control;
1176
+ return this.visitElement(control, other as J.ForLoop.Control);
2741
1177
  }
2742
1178
 
2743
1179
  /**
@@ -2748,32 +1184,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2748
1184
  * @returns The visited if statement, or undefined if the visit was aborted
2749
1185
  */
2750
1186
  override async visitIf(ifStatement: J.If, other: J): Promise<J | undefined> {
2751
- if (!this.match || other.kind !== J.Kind.If) {
2752
- return this.abort(ifStatement);
2753
- }
2754
-
2755
- const otherIfStatement = other as J.If;
2756
-
2757
- // Visit condition
2758
- await this.visit(ifStatement.ifCondition, otherIfStatement.ifCondition);
2759
- if (!this.match) return ifStatement;
2760
-
2761
- // Visit then part
2762
- await this.visit(ifStatement.thenPart.element, otherIfStatement.thenPart.element);
2763
- if (!this.match) return ifStatement;
2764
-
2765
- // Compare else part
2766
- if ((ifStatement.elsePart === undefined) !== (otherIfStatement.elsePart === undefined)) {
2767
- return this.abort(ifStatement);
2768
- }
2769
-
2770
- // Visit else part if present
2771
- if (ifStatement.elsePart && otherIfStatement.elsePart) {
2772
- await this.visit(ifStatement.elsePart, otherIfStatement.elsePart);
2773
- if (!this.match) return ifStatement;
2774
- }
2775
-
2776
- return ifStatement;
1187
+ return this.visitElement(ifStatement, other as J.If);
2777
1188
  }
2778
1189
 
2779
1190
  /**
@@ -2784,17 +1195,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2784
1195
  * @returns The visited else statement, or undefined if the visit was aborted
2785
1196
  */
2786
1197
  override async visitElse(elseStatement: J.If.Else, other: J): Promise<J | undefined> {
2787
- if (!this.match || other.kind !== J.Kind.IfElse) {
2788
- return this.abort(elseStatement);
2789
- }
2790
-
2791
- const otherElseStatement = other as J.If.Else;
2792
-
2793
- // Visit body
2794
- await this.visit(elseStatement.body.element, otherElseStatement.body.element);
2795
- if (!this.match) return elseStatement;
2796
-
2797
- return elseStatement;
1198
+ return this.visitElement(elseStatement, other as J.If.Else);
2798
1199
  }
2799
1200
 
2800
1201
  /**
@@ -2805,33 +1206,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2805
1206
  * @returns The visited import statement, or undefined if the visit was aborted
2806
1207
  */
2807
1208
  override async visitImport(importStatement: J.Import, other: J): Promise<J | undefined> {
2808
- if (!this.match || other.kind !== J.Kind.Import) {
2809
- return this.abort(importStatement);
2810
- }
2811
-
2812
- const otherImportStatement = other as J.Import;
2813
-
2814
- // Compare static
2815
- if (importStatement.static.element !== otherImportStatement.static.element) {
2816
- return this.abort(importStatement);
2817
- }
2818
-
2819
- // Visit qualid
2820
- await this.visit(importStatement.qualid, otherImportStatement.qualid);
2821
- if (!this.match) return importStatement;
2822
-
2823
- // Compare alias
2824
- if ((importStatement.alias === undefined) !== (otherImportStatement.alias === undefined)) {
2825
- return this.abort(importStatement);
2826
- }
2827
-
2828
- // Visit alias if present
2829
- if (importStatement.alias && otherImportStatement.alias) {
2830
- await this.visit(importStatement.alias.element, otherImportStatement.alias.element);
2831
- if (!this.match) return importStatement;
2832
- }
2833
-
2834
- return importStatement;
1209
+ return this.visitElement(importStatement, other as J.Import);
2835
1210
  }
2836
1211
 
2837
1212
  /**
@@ -2842,43 +1217,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2842
1217
  * @returns The visited instanceof expression, or undefined if the visit was aborted
2843
1218
  */
2844
1219
  override async visitInstanceOf(instanceOf: J.InstanceOf, other: J): Promise<J | undefined> {
2845
- if (!this.match || other.kind !== J.Kind.InstanceOf) {
2846
- return this.abort(instanceOf);
2847
- }
2848
-
2849
- const otherInstanceOf = other as J.InstanceOf;
2850
-
2851
- // Visit expression
2852
- await this.visit(instanceOf.expression.element, otherInstanceOf.expression.element);
2853
- if (!this.match) return instanceOf;
2854
-
2855
- // Visit class
2856
- await this.visit(instanceOf.class, otherInstanceOf.class);
2857
- if (!this.match) return instanceOf;
2858
-
2859
- // Compare pattern
2860
- if ((instanceOf.pattern === undefined) !== (otherInstanceOf.pattern === undefined)) {
2861
- return this.abort(instanceOf);
2862
- }
2863
-
2864
- // Visit pattern if present
2865
- if (instanceOf.pattern && otherInstanceOf.pattern) {
2866
- await this.visit(instanceOf.pattern, otherInstanceOf.pattern);
2867
- if (!this.match) return instanceOf;
2868
- }
2869
-
2870
- // Compare modifier
2871
- if ((instanceOf.modifier === undefined) !== (otherInstanceOf.modifier === undefined)) {
2872
- return this.abort(instanceOf);
2873
- }
2874
-
2875
- // Visit modifier if present
2876
- if (instanceOf.modifier && otherInstanceOf.modifier) {
2877
- await this.visit(instanceOf.modifier, otherInstanceOf.modifier);
2878
- if (!this.match) return instanceOf;
2879
- }
2880
-
2881
- return instanceOf;
1220
+ return this.visitElement(instanceOf, other as J.InstanceOf);
2882
1221
  }
2883
1222
 
2884
1223
  /**
@@ -2889,24 +1228,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2889
1228
  * @returns The visited intersection type, or undefined if the visit was aborted
2890
1229
  */
2891
1230
  override async visitIntersectionType(intersectionType: J.IntersectionType, other: J): Promise<J | undefined> {
2892
- if (!this.match || other.kind !== J.Kind.IntersectionType) {
2893
- return this.abort(intersectionType);
2894
- }
2895
-
2896
- const otherIntersectionType = other as J.IntersectionType;
2897
-
2898
- // Compare bounds
2899
- if (intersectionType.bounds.elements.length !== otherIntersectionType.bounds.elements.length) {
2900
- return this.abort(intersectionType);
2901
- }
2902
-
2903
- // Visit each bound in lock step
2904
- for (let i = 0; i < intersectionType.bounds.elements.length; i++) {
2905
- await this.visit(intersectionType.bounds.elements[i].element, otherIntersectionType.bounds.elements[i].element);
2906
- if (!this.match) return intersectionType;
2907
- }
2908
-
2909
- return intersectionType;
1231
+ return this.visitElement(intersectionType, other as J.IntersectionType);
2910
1232
  }
2911
1233
 
2912
1234
  /**
@@ -2917,21 +1239,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2917
1239
  * @returns The visited label statement, or undefined if the visit was aborted
2918
1240
  */
2919
1241
  override async visitLabel(label: J.Label, other: J): Promise<J | undefined> {
2920
- if (!this.match || other.kind !== J.Kind.Label) {
2921
- return this.abort(label);
2922
- }
2923
-
2924
- const otherLabel = other as J.Label;
2925
-
2926
- // Visit label identifier
2927
- await this.visit(label.label.element, otherLabel.label.element);
2928
- if (!this.match) return label;
2929
-
2930
- // Visit statement
2931
- await this.visit(label.statement, otherLabel.statement);
2932
- if (!this.match) return label;
2933
-
2934
- return label;
1242
+ return this.visitElement(label, other as J.Label);
2935
1243
  }
2936
1244
 
2937
1245
  /**
@@ -2942,21 +1250,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2942
1250
  * @returns The visited lambda expression, or undefined if the visit was aborted
2943
1251
  */
2944
1252
  override async visitLambda(lambda: J.Lambda, other: J): Promise<J | undefined> {
2945
- if (!this.match || other.kind !== J.Kind.Lambda) {
2946
- return this.abort(lambda);
2947
- }
2948
-
2949
- const otherLambda = other as J.Lambda;
2950
-
2951
- // Visit parameters
2952
- await this.visit(lambda.parameters, otherLambda.parameters);
2953
- if (!this.match) return lambda;
2954
-
2955
- // Visit body
2956
- await this.visit(lambda.body, otherLambda.body);
2957
- if (!this.match) return lambda;
2958
-
2959
- return lambda;
1253
+ return this.visitElement(lambda, other as J.Lambda);
2960
1254
  }
2961
1255
 
2962
1256
  /**
@@ -2967,29 +1261,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
2967
1261
  * @returns The visited lambda parameters, or undefined if the visit was aborted
2968
1262
  */
2969
1263
  override async visitLambdaParameters(parameters: J.Lambda.Parameters, other: J): Promise<J | undefined> {
2970
- if (!this.match || other.kind !== J.Kind.LambdaParameters) {
2971
- return this.abort(parameters);
2972
- }
2973
-
2974
- const otherParameters = other as J.Lambda.Parameters;
2975
-
2976
- // Compare parenthesized
2977
- if (parameters.parenthesized !== otherParameters.parenthesized) {
2978
- return this.abort(parameters);
2979
- }
2980
-
2981
- // Compare parameters
2982
- if (parameters.parameters.length !== otherParameters.parameters.length) {
2983
- return this.abort(parameters);
2984
- }
2985
-
2986
- // Visit each parameter in lock step
2987
- for (let i = 0; i < parameters.parameters.length; i++) {
2988
- await this.visit(parameters.parameters[i].element, otherParameters.parameters[i].element);
2989
- if (!this.match) return parameters;
2990
- }
2991
-
2992
- return parameters;
1264
+ return this.visitElement(parameters, other as J.Lambda.Parameters);
2993
1265
  }
2994
1266
 
2995
1267
  /**
@@ -3000,39 +1272,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3000
1272
  * @returns The visited member reference, or undefined if the visit was aborted
3001
1273
  */
3002
1274
  override async visitMemberReference(memberReference: J.MemberReference, other: J): Promise<J | undefined> {
3003
- if (!this.match || other.kind !== J.Kind.MemberReference) {
3004
- return this.abort(memberReference);
3005
- }
3006
-
3007
- const otherMemberReference = other as J.MemberReference;
3008
-
3009
- // Visit containing
3010
- await this.visit(memberReference.containing.element, otherMemberReference.containing.element);
3011
- if (!this.match) return memberReference;
3012
-
3013
- // Compare typeParameters
3014
- if ((memberReference.typeParameters === undefined) !== (otherMemberReference.typeParameters === undefined)) {
3015
- return this.abort(memberReference);
3016
- }
3017
-
3018
- // Visit typeParameters if present
3019
- if (memberReference.typeParameters && otherMemberReference.typeParameters) {
3020
- if (memberReference.typeParameters.elements.length !== otherMemberReference.typeParameters.elements.length) {
3021
- return this.abort(memberReference);
3022
- }
3023
-
3024
- // Visit each type parameter in lock step
3025
- for (let i = 0; i < memberReference.typeParameters.elements.length; i++) {
3026
- await this.visit(memberReference.typeParameters.elements[i].element, otherMemberReference.typeParameters.elements[i].element);
3027
- if (!this.match) return memberReference;
3028
- }
3029
- }
3030
-
3031
- // Visit reference
3032
- await this.visit(memberReference.reference.element, otherMemberReference.reference.element);
3033
- if (!this.match) return memberReference;
3034
-
3035
- return memberReference;
1275
+ return this.visitElement(memberReference, other as J.MemberReference);
3036
1276
  }
3037
1277
 
3038
1278
  /**
@@ -3043,123 +1283,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3043
1283
  * @returns The visited method declaration, or undefined if the visit was aborted
3044
1284
  */
3045
1285
  override async visitMethodDeclaration(methodDeclaration: J.MethodDeclaration, other: J): Promise<J | undefined> {
3046
- if (!this.match || other.kind !== J.Kind.MethodDeclaration) {
3047
- return this.abort(methodDeclaration);
3048
- }
3049
-
3050
- const otherMethodDeclaration = other as J.MethodDeclaration;
3051
-
3052
- // Compare leadingAnnotations
3053
- if (methodDeclaration.leadingAnnotations.length !== otherMethodDeclaration.leadingAnnotations.length) {
3054
- return this.abort(methodDeclaration);
3055
- }
3056
-
3057
- // Visit each leading annotation in lock step
3058
- for (let i = 0; i < methodDeclaration.leadingAnnotations.length; i++) {
3059
- await this.visit(methodDeclaration.leadingAnnotations[i], otherMethodDeclaration.leadingAnnotations[i]);
3060
- if (!this.match) return methodDeclaration;
3061
- }
3062
-
3063
- // Compare modifiers
3064
- if (methodDeclaration.modifiers.length !== otherMethodDeclaration.modifiers.length) {
3065
- return this.abort(methodDeclaration);
3066
- }
3067
-
3068
- // Visit each modifier in lock step
3069
- for (let i = 0; i < methodDeclaration.modifiers.length; i++) {
3070
- await this.visit(methodDeclaration.modifiers[i], otherMethodDeclaration.modifiers[i]);
3071
- if (!this.match) return methodDeclaration;
3072
- }
3073
-
3074
- // Compare typeParameters
3075
- if ((methodDeclaration.typeParameters === undefined) !== (otherMethodDeclaration.typeParameters === undefined)) {
3076
- return this.abort(methodDeclaration);
3077
- }
3078
-
3079
- // Visit typeParameters if present
3080
- if (methodDeclaration.typeParameters && otherMethodDeclaration.typeParameters) {
3081
- await this.visit(methodDeclaration.typeParameters, otherMethodDeclaration.typeParameters);
3082
- if (!this.match) return methodDeclaration;
3083
- }
3084
-
3085
- // Compare returnTypeExpression
3086
- if ((methodDeclaration.returnTypeExpression === undefined) !== (otherMethodDeclaration.returnTypeExpression === undefined)) {
3087
- return this.abort(methodDeclaration);
3088
- }
3089
-
3090
- // Visit returnTypeExpression if present
3091
- if (methodDeclaration.returnTypeExpression && otherMethodDeclaration.returnTypeExpression) {
3092
- await this.visit(methodDeclaration.returnTypeExpression, otherMethodDeclaration.returnTypeExpression);
3093
- if (!this.match) return methodDeclaration;
3094
- }
3095
-
3096
- // Compare nameAnnotations
3097
- if (methodDeclaration.nameAnnotations.length !== otherMethodDeclaration.nameAnnotations.length) {
3098
- return this.abort(methodDeclaration);
3099
- }
3100
-
3101
- // Visit each name annotation in lock step
3102
- for (let i = 0; i < methodDeclaration.nameAnnotations.length; i++) {
3103
- await this.visit(methodDeclaration.nameAnnotations[i], otherMethodDeclaration.nameAnnotations[i]);
3104
- if (!this.match) return methodDeclaration;
3105
- }
3106
-
3107
- // Visit name
3108
- await this.visit(methodDeclaration.name, otherMethodDeclaration.name);
3109
- if (!this.match) return methodDeclaration;
3110
-
3111
- // Compare parameters
3112
- if (methodDeclaration.parameters.elements.length !== otherMethodDeclaration.parameters.elements.length) {
3113
- return this.abort(methodDeclaration);
3114
- }
3115
-
3116
- // Visit each parameter in lock step
3117
- for (let i = 0; i < methodDeclaration.parameters.elements.length; i++) {
3118
- await this.visit(methodDeclaration.parameters.elements[i].element, otherMethodDeclaration.parameters.elements[i].element);
3119
- if (!this.match) return methodDeclaration;
3120
- }
3121
-
3122
- // Compare throws
3123
- if ((methodDeclaration.throws === undefined) !== (otherMethodDeclaration.throws === undefined)) {
3124
- return this.abort(methodDeclaration);
3125
- }
3126
-
3127
- // Visit throws if present
3128
- if (methodDeclaration.throws && otherMethodDeclaration.throws) {
3129
- if (methodDeclaration.throws.elements.length !== otherMethodDeclaration.throws.elements.length) {
3130
- return this.abort(methodDeclaration);
3131
- }
3132
-
3133
- // Visit each throw in lock step
3134
- for (let i = 0; i < methodDeclaration.throws.elements.length; i++) {
3135
- await this.visit(methodDeclaration.throws.elements[i].element, otherMethodDeclaration.throws.elements[i].element);
3136
- if (!this.match) return methodDeclaration;
3137
- }
3138
- }
3139
-
3140
- // Compare body
3141
- if ((methodDeclaration.body === undefined) !== (otherMethodDeclaration.body === undefined)) {
3142
- return this.abort(methodDeclaration);
3143
- }
3144
-
3145
- // Visit body if present
3146
- if (methodDeclaration.body && otherMethodDeclaration.body) {
3147
- await this.visit(methodDeclaration.body, otherMethodDeclaration.body);
3148
- if (!this.match) return methodDeclaration;
3149
- }
3150
-
3151
- // Compare defaultValue
3152
- if ((methodDeclaration.defaultValue === undefined) !== (otherMethodDeclaration.defaultValue === undefined)) {
3153
- return this.abort(methodDeclaration);
3154
- }
3155
-
3156
- // Visit defaultValue if present
3157
- if (methodDeclaration.defaultValue && otherMethodDeclaration.defaultValue) {
3158
- await this.visit(methodDeclaration.defaultValue.element, otherMethodDeclaration.defaultValue.element);
3159
- if (!this.match) return methodDeclaration;
3160
- }
3161
-
3162
- return methodDeclaration;
1286
+ return this.visitElement(methodDeclaration, other as J.MethodDeclaration);
3163
1287
  }
3164
1288
 
3165
1289
  /**
@@ -3170,57 +1294,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3170
1294
  * @returns The visited method invocation, or undefined if the visit was aborted
3171
1295
  */
3172
1296
  override async visitMethodInvocation(methodInvocation: J.MethodInvocation, other: J): Promise<J | undefined> {
3173
- if (!this.match || other.kind !== J.Kind.MethodInvocation) {
3174
- return this.abort(methodInvocation);
3175
- }
3176
-
3177
- const otherMethodInvocation = other as J.MethodInvocation;
3178
-
3179
- // Compare select
3180
- if ((methodInvocation.select === undefined) !== (otherMethodInvocation.select === undefined)) {
3181
- return this.abort(methodInvocation);
3182
- }
3183
-
3184
- // Visit select if present
3185
- if (methodInvocation.select && otherMethodInvocation.select) {
3186
- await this.visit(methodInvocation.select.element, otherMethodInvocation.select.element);
3187
- if (!this.match) return methodInvocation;
3188
- }
3189
-
3190
- // Compare typeParameters
3191
- if ((methodInvocation.typeParameters === undefined) !== (otherMethodInvocation.typeParameters === undefined)) {
3192
- return this.abort(methodInvocation);
3193
- }
3194
-
3195
- // Visit typeParameters if present
3196
- if (methodInvocation.typeParameters && otherMethodInvocation.typeParameters) {
3197
- if (methodInvocation.typeParameters.elements.length !== otherMethodInvocation.typeParameters.elements.length) {
3198
- return this.abort(methodInvocation);
3199
- }
3200
-
3201
- // Visit each type parameter in lock step
3202
- for (let i = 0; i < methodInvocation.typeParameters.elements.length; i++) {
3203
- await this.visit(methodInvocation.typeParameters.elements[i].element, otherMethodInvocation.typeParameters.elements[i].element);
3204
- if (!this.match) return methodInvocation;
3205
- }
3206
- }
3207
-
3208
- // Visit name
3209
- await this.visit(methodInvocation.name, otherMethodInvocation.name);
3210
- if (!this.match) return methodInvocation;
3211
-
3212
- // Compare arguments
3213
- if (methodInvocation.arguments.elements.length !== otherMethodInvocation.arguments.elements.length) {
3214
- return this.abort(methodInvocation);
3215
- }
3216
-
3217
- // Visit each argument in lock step
3218
- for (let i = 0; i < methodInvocation.arguments.elements.length; i++) {
3219
- await this.visit(methodInvocation.arguments.elements[i].element, otherMethodInvocation.arguments.elements[i].element);
3220
- if (!this.match) return methodInvocation;
3221
- }
3222
-
3223
- return methodInvocation;
1297
+ return this.visitElement(methodInvocation, other as J.MethodInvocation);
3224
1298
  }
3225
1299
 
3226
1300
  /**
@@ -3231,34 +1305,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3231
1305
  * @returns The visited modifier, or undefined if the visit was aborted
3232
1306
  */
3233
1307
  override async visitModifier(modifier: J.Modifier, other: J): Promise<J | undefined> {
3234
- if (!this.match || other.kind !== J.Kind.Modifier) {
3235
- return this.abort(modifier);
3236
- }
3237
-
3238
- const otherModifier = other as J.Modifier;
3239
-
3240
- // Compare keyword
3241
- if (modifier.keyword !== otherModifier.keyword) {
3242
- return this.abort(modifier);
3243
- }
3244
-
3245
- // Compare type
3246
- if (modifier.type !== otherModifier.type) {
3247
- return this.abort(modifier);
3248
- }
3249
-
3250
- // Compare annotations
3251
- if (modifier.annotations.length !== otherModifier.annotations.length) {
3252
- return this.abort(modifier);
3253
- }
3254
-
3255
- // Visit each annotation in lock step
3256
- for (let i = 0; i < modifier.annotations.length; i++) {
3257
- await this.visit(modifier.annotations[i], otherModifier.annotations[i]);
3258
- if (!this.match) return modifier;
3259
- }
3260
-
3261
- return modifier;
1308
+ return this.visitElement(modifier, other as J.Modifier);
3262
1309
  }
3263
1310
 
3264
1311
  /**
@@ -3269,24 +1316,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3269
1316
  * @returns The visited multi-catch expression, or undefined if the visit was aborted
3270
1317
  */
3271
1318
  override async visitMultiCatch(multiCatch: J.MultiCatch, other: J): Promise<J | undefined> {
3272
- if (!this.match || other.kind !== J.Kind.MultiCatch) {
3273
- return this.abort(multiCatch);
3274
- }
3275
-
3276
- const otherMultiCatch = other as J.MultiCatch;
3277
-
3278
- // Compare alternatives
3279
- if (multiCatch.alternatives.length !== otherMultiCatch.alternatives.length) {
3280
- return this.abort(multiCatch);
3281
- }
3282
-
3283
- // Visit each alternative in lock step
3284
- for (let i = 0; i < multiCatch.alternatives.length; i++) {
3285
- await this.visit(multiCatch.alternatives[i].element, otherMultiCatch.alternatives[i].element);
3286
- if (!this.match) return multiCatch;
3287
- }
3288
-
3289
- return multiCatch;
1319
+ return this.visitElement(multiCatch, other as J.MultiCatch);
3290
1320
  }
3291
1321
 
3292
1322
  /**
@@ -3297,53 +1327,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3297
1327
  * @returns The visited new array expression, or undefined if the visit was aborted
3298
1328
  */
3299
1329
  override async visitNewArray(newArray: J.NewArray, other: J): Promise<J | undefined> {
3300
- if (!this.match || other.kind !== J.Kind.NewArray) {
3301
- return this.abort(newArray);
3302
- }
3303
-
3304
- const otherNewArray = other as J.NewArray;
3305
-
3306
- // Compare typeExpression
3307
- if ((newArray.typeExpression === undefined) !== (otherNewArray.typeExpression === undefined)) {
3308
- return this.abort(newArray);
3309
- }
3310
-
3311
- // Visit typeExpression if present
3312
- if (newArray.typeExpression && otherNewArray.typeExpression) {
3313
- await this.visit(newArray.typeExpression, otherNewArray.typeExpression);
3314
- if (!this.match) return newArray;
3315
- }
3316
-
3317
- // Compare dimensions
3318
- if (newArray.dimensions.length !== otherNewArray.dimensions.length) {
3319
- return this.abort(newArray);
3320
- }
3321
-
3322
- // Visit each dimension in lock step
3323
- for (let i = 0; i < newArray.dimensions.length; i++) {
3324
- await this.visit(newArray.dimensions[i], otherNewArray.dimensions[i]);
3325
- if (!this.match) return newArray;
3326
- }
3327
-
3328
- // Compare initializer
3329
- if ((newArray.initializer === undefined) !== (otherNewArray.initializer === undefined)) {
3330
- return this.abort(newArray);
3331
- }
3332
-
3333
- // Visit initializer if present
3334
- if (newArray.initializer && otherNewArray.initializer) {
3335
- if (newArray.initializer.elements.length !== otherNewArray.initializer.elements.length) {
3336
- return this.abort(newArray);
3337
- }
3338
-
3339
- // Visit each initializer element in lock step
3340
- for (let i = 0; i < newArray.initializer.elements.length; i++) {
3341
- await this.visit(newArray.initializer.elements[i].element, otherNewArray.initializer.elements[i].element);
3342
- if (!this.match) return newArray;
3343
- }
3344
- }
3345
-
3346
- return newArray;
1330
+ return this.visitElement(newArray, other as J.NewArray);
3347
1331
  }
3348
1332
 
3349
1333
  /**
@@ -3354,57 +1338,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3354
1338
  * @returns The visited new class expression, or undefined if the visit was aborted
3355
1339
  */
3356
1340
  override async visitNewClass(newClass: J.NewClass, other: J): Promise<J | undefined> {
3357
- if (!this.match || other.kind !== J.Kind.NewClass) {
3358
- return this.abort(newClass);
3359
- }
3360
-
3361
- const otherNewClass = other as J.NewClass;
3362
-
3363
- // Compare enclosing
3364
- if ((newClass.enclosing === undefined) !== (otherNewClass.enclosing === undefined)) {
3365
- return this.abort(newClass);
3366
- }
3367
-
3368
- // Visit enclosing if present
3369
- if (newClass.enclosing && otherNewClass.enclosing) {
3370
- await this.visit(newClass.enclosing.element, otherNewClass.enclosing.element);
3371
- if (!this.match) return newClass;
3372
- }
3373
-
3374
- // Compare class
3375
- if ((newClass.class === undefined) !== (otherNewClass.class === undefined)) {
3376
- return this.abort(newClass);
3377
- }
3378
-
3379
- // Visit class if present
3380
- if (newClass.class && otherNewClass.class) {
3381
- await this.visit(newClass.class, otherNewClass.class);
3382
- if (!this.match) return newClass;
3383
- }
3384
-
3385
- // Compare arguments
3386
- if (newClass.arguments.elements.length !== otherNewClass.arguments.elements.length) {
3387
- return this.abort(newClass);
3388
- }
3389
-
3390
- // Visit each argument in lock step
3391
- for (let i = 0; i < newClass.arguments.elements.length; i++) {
3392
- await this.visit(newClass.arguments.elements[i].element, otherNewClass.arguments.elements[i].element);
3393
- if (!this.match) return newClass;
3394
- }
3395
-
3396
- // Compare body
3397
- if ((newClass.body === undefined) !== (otherNewClass.body === undefined)) {
3398
- return this.abort(newClass);
3399
- }
3400
-
3401
- // Visit body if present
3402
- if (newClass.body && otherNewClass.body) {
3403
- await this.visit(newClass.body, otherNewClass.body);
3404
- if (!this.match) return newClass;
3405
- }
3406
-
3407
- return newClass;
1341
+ return this.visitElement(newClass, other as J.NewClass);
3408
1342
  }
3409
1343
 
3410
1344
  /**
@@ -3415,28 +1349,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3415
1349
  * @returns The visited nullable type, or undefined if the visit was aborted
3416
1350
  */
3417
1351
  override async visitNullableType(nullableType: J.NullableType, other: J): Promise<J | undefined> {
3418
- if (!this.match || other.kind !== J.Kind.NullableType) {
3419
- return this.abort(nullableType);
3420
- }
3421
-
3422
- const otherNullableType = other as J.NullableType;
3423
-
3424
- // Compare annotations
3425
- if (nullableType.annotations.length !== otherNullableType.annotations.length) {
3426
- return this.abort(nullableType);
3427
- }
3428
-
3429
- // Visit each annotation in lock step
3430
- for (let i = 0; i < nullableType.annotations.length; i++) {
3431
- await this.visit(nullableType.annotations[i], otherNullableType.annotations[i]);
3432
- if (!this.match) return nullableType;
3433
- }
3434
-
3435
- // Visit typeTree
3436
- await this.visit(nullableType.typeTree.element, otherNullableType.typeTree.element);
3437
- if (!this.match) return nullableType;
3438
-
3439
- return nullableType;
1352
+ return this.visitElement(nullableType, other as J.NullableType);
3440
1353
  }
3441
1354
 
3442
1355
  /**
@@ -3447,35 +1360,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3447
1360
  * @returns The visited package declaration, or undefined if the visit was aborted
3448
1361
  */
3449
1362
  override async visitPackage(packageDeclaration: J.Package, other: J): Promise<J | undefined> {
3450
- if (!this.match || other.kind !== J.Kind.Package) {
3451
- return this.abort(packageDeclaration);
3452
- }
3453
-
3454
- const otherPackageDeclaration = other as J.Package;
3455
-
3456
- // Visit expression
3457
- await this.visit(packageDeclaration.expression, otherPackageDeclaration.expression);
3458
- if (!this.match) return packageDeclaration;
3459
-
3460
- // Compare annotations
3461
- if ((packageDeclaration.annotations === undefined) !== (otherPackageDeclaration.annotations === undefined)) {
3462
- return this.abort(packageDeclaration);
3463
- }
3464
-
3465
- // Visit annotations if present
3466
- if (packageDeclaration.annotations && otherPackageDeclaration.annotations) {
3467
- if (packageDeclaration.annotations.length !== otherPackageDeclaration.annotations.length) {
3468
- return this.abort(packageDeclaration);
3469
- }
3470
-
3471
- // Visit each annotation in lock step
3472
- for (let i = 0; i < packageDeclaration.annotations.length; i++) {
3473
- await this.visit(packageDeclaration.annotations[i], otherPackageDeclaration.annotations[i]);
3474
- if (!this.match) return packageDeclaration;
3475
- }
3476
- }
3477
-
3478
- return packageDeclaration;
1363
+ return this.visitElement(packageDeclaration, other as J.Package);
3479
1364
  }
3480
1365
 
3481
1366
  /**
@@ -3486,35 +1371,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3486
1371
  * @returns The visited parameterized type, or undefined if the visit was aborted
3487
1372
  */
3488
1373
  override async visitParameterizedType(parameterizedType: J.ParameterizedType, other: J): Promise<J | undefined> {
3489
- if (!this.match || other.kind !== J.Kind.ParameterizedType) {
3490
- return this.abort(parameterizedType);
3491
- }
3492
-
3493
- const otherParameterizedType = other as J.ParameterizedType;
3494
-
3495
- // Visit class
3496
- await this.visit(parameterizedType.class, otherParameterizedType.class);
3497
- if (!this.match) return parameterizedType;
3498
-
3499
- // Compare typeParameters
3500
- if ((parameterizedType.typeParameters === undefined) !== (otherParameterizedType.typeParameters === undefined)) {
3501
- return this.abort(parameterizedType);
3502
- }
3503
-
3504
- // Visit typeParameters if present
3505
- if (parameterizedType.typeParameters && otherParameterizedType.typeParameters) {
3506
- if (parameterizedType.typeParameters.elements.length !== otherParameterizedType.typeParameters.elements.length) {
3507
- return this.abort(parameterizedType);
3508
- }
3509
-
3510
- // Visit each type parameter in lock step
3511
- for (let i = 0; i < parameterizedType.typeParameters.elements.length; i++) {
3512
- await this.visit(parameterizedType.typeParameters.elements[i].element, otherParameterizedType.typeParameters.elements[i].element);
3513
- if (!this.match) return parameterizedType;
3514
- }
3515
- }
3516
-
3517
- return parameterizedType;
1374
+ return this.visitElement(parameterizedType, other as J.ParameterizedType);
3518
1375
  }
3519
1376
 
3520
1377
  /**
@@ -3525,17 +1382,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3525
1382
  * @returns The visited parentheses expression, or undefined if the visit was aborted
3526
1383
  */
3527
1384
  override async visitParentheses(parentheses: J.Parentheses<J>, other: J): Promise<J | undefined> {
3528
- if (!this.match || other.kind !== J.Kind.Parentheses) {
3529
- return this.abort(parentheses);
3530
- }
3531
-
3532
- const otherParentheses = other as J.Parentheses<J>;
3533
-
3534
- // Visit tree
3535
- await this.visit(parentheses.tree.element, otherParentheses.tree.element);
3536
- if (!this.match) return parentheses;
3537
-
3538
- return parentheses;
1385
+ return this.visitElement(parentheses, other as J.Parentheses<J>);
3539
1386
  }
3540
1387
 
3541
1388
  /**
@@ -3546,28 +1393,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3546
1393
  * @returns The visited parenthesized type tree, or undefined if the visit was aborted
3547
1394
  */
3548
1395
  override async visitParenthesizedTypeTree(parenthesizedTypeTree: J.ParenthesizedTypeTree, other: J): Promise<J | undefined> {
3549
- if (!this.match || other.kind !== J.Kind.ParenthesizedTypeTree) {
3550
- return this.abort(parenthesizedTypeTree);
3551
- }
3552
-
3553
- const otherParenthesizedTypeTree = other as J.ParenthesizedTypeTree;
3554
-
3555
- // Compare annotations
3556
- if (parenthesizedTypeTree.annotations.length !== otherParenthesizedTypeTree.annotations.length) {
3557
- return this.abort(parenthesizedTypeTree);
3558
- }
3559
-
3560
- // Visit each annotation in lock step
3561
- for (let i = 0; i < parenthesizedTypeTree.annotations.length; i++) {
3562
- await this.visit(parenthesizedTypeTree.annotations[i], otherParenthesizedTypeTree.annotations[i]);
3563
- if (!this.match) return parenthesizedTypeTree;
3564
- }
3565
-
3566
- // Visit parenthesizedType
3567
- await this.visit(parenthesizedTypeTree.parenthesizedType, otherParenthesizedTypeTree.parenthesizedType);
3568
- if (!this.match) return parenthesizedTypeTree;
3569
-
3570
- return parenthesizedTypeTree;
1396
+ return this.visitElement(parenthesizedTypeTree, other as J.ParenthesizedTypeTree);
3571
1397
  }
3572
1398
 
3573
1399
  /**
@@ -3578,18 +1404,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3578
1404
  * @returns The visited primitive type, or undefined if the visit was aborted
3579
1405
  */
3580
1406
  override async visitPrimitive(primitive: J.Primitive, other: J): Promise<J | undefined> {
3581
- if (!this.match || other.kind !== J.Kind.Primitive) {
3582
- return this.abort(primitive);
3583
- }
3584
-
3585
- const otherPrimitive = other as J.Primitive;
3586
-
3587
- // Compare type
3588
- if (primitive.type.kind !== otherPrimitive.type.kind) {
3589
- return this.abort(primitive);
3590
- }
3591
-
3592
- return primitive;
1407
+ return this.visitElement(primitive, other as J.Primitive);
3593
1408
  }
3594
1409
 
3595
1410
  /**
@@ -3600,24 +1415,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3600
1415
  * @returns The visited return statement, or undefined if the visit was aborted
3601
1416
  */
3602
1417
  override async visitReturn(returnStatement: J.Return, other: J): Promise<J | undefined> {
3603
- if (!this.match || other.kind !== J.Kind.Return) {
3604
- return this.abort(returnStatement);
3605
- }
3606
-
3607
- const otherReturnStatement = other as J.Return;
3608
-
3609
- // Compare expression
3610
- if ((returnStatement.expression === undefined) !== (otherReturnStatement.expression === undefined)) {
3611
- return this.abort(returnStatement);
3612
- }
3613
-
3614
- // Visit expression if present
3615
- if (returnStatement.expression && otherReturnStatement.expression) {
3616
- await this.visit(returnStatement.expression, otherReturnStatement.expression);
3617
- if (!this.match) return returnStatement;
3618
- }
3619
-
3620
- return returnStatement;
1418
+ return this.visitElement(returnStatement, other as J.Return);
3621
1419
  }
3622
1420
 
3623
1421
  /**
@@ -3628,21 +1426,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3628
1426
  * @returns The visited switch statement, or undefined if the visit was aborted
3629
1427
  */
3630
1428
  override async visitSwitch(switchStatement: J.Switch, other: J): Promise<J | undefined> {
3631
- if (!this.match || other.kind !== J.Kind.Switch) {
3632
- return this.abort(switchStatement);
3633
- }
3634
-
3635
- const otherSwitchStatement = other as J.Switch;
3636
-
3637
- // Visit selector
3638
- await this.visit(switchStatement.selector, otherSwitchStatement.selector);
3639
- if (!this.match) return switchStatement;
3640
-
3641
- // Visit cases
3642
- await this.visit(switchStatement.cases, otherSwitchStatement.cases);
3643
- if (!this.match) return switchStatement;
3644
-
3645
- return switchStatement;
1429
+ return this.visitElement(switchStatement, other as J.Switch);
3646
1430
  }
3647
1431
 
3648
1432
  /**
@@ -3653,21 +1437,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3653
1437
  * @returns The visited switch expression, or undefined if the visit was aborted
3654
1438
  */
3655
1439
  override async visitSwitchExpression(switchExpression: J.SwitchExpression, other: J): Promise<J | undefined> {
3656
- if (!this.match || other.kind !== J.Kind.SwitchExpression) {
3657
- return this.abort(switchExpression);
3658
- }
3659
-
3660
- const otherSwitchExpression = other as J.SwitchExpression;
3661
-
3662
- // Visit selector
3663
- await this.visit(switchExpression.selector, otherSwitchExpression.selector);
3664
- if (!this.match) return switchExpression;
3665
-
3666
- // Visit cases
3667
- await this.visit(switchExpression.cases, otherSwitchExpression.cases);
3668
- if (!this.match) return switchExpression;
3669
-
3670
- return switchExpression;
1440
+ return this.visitElement(switchExpression, other as J.SwitchExpression);
3671
1441
  }
3672
1442
 
3673
1443
  /**
@@ -3678,21 +1448,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3678
1448
  * @returns The visited synchronized statement, or undefined if the visit was aborted
3679
1449
  */
3680
1450
  override async visitSynchronized(synchronizedStatement: J.Synchronized, other: J): Promise<J | undefined> {
3681
- if (!this.match || other.kind !== J.Kind.Synchronized) {
3682
- return this.abort(synchronizedStatement);
3683
- }
3684
-
3685
- const otherSynchronizedStatement = other as J.Synchronized;
3686
-
3687
- // Visit lock
3688
- await this.visit(synchronizedStatement.lock, otherSynchronizedStatement.lock);
3689
- if (!this.match) return synchronizedStatement;
3690
-
3691
- // Visit body
3692
- await this.visit(synchronizedStatement.body, otherSynchronizedStatement.body);
3693
- if (!this.match) return synchronizedStatement;
3694
-
3695
- return synchronizedStatement;
1451
+ return this.visitElement(synchronizedStatement, other as J.Synchronized);
3696
1452
  }
3697
1453
 
3698
1454
  /**
@@ -3703,25 +1459,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3703
1459
  * @returns The visited ternary expression, or undefined if the visit was aborted
3704
1460
  */
3705
1461
  override async visitTernary(ternary: J.Ternary, other: J): Promise<J | undefined> {
3706
- if (!this.match || other.kind !== J.Kind.Ternary) {
3707
- return this.abort(ternary);
3708
- }
3709
-
3710
- const otherTernary = other as J.Ternary;
3711
-
3712
- // Visit condition
3713
- await this.visit(ternary.condition, otherTernary.condition);
3714
- if (!this.match) return ternary;
3715
-
3716
- // Visit truePart
3717
- await this.visit(ternary.truePart.element, otherTernary.truePart.element);
3718
- if (!this.match) return ternary;
3719
-
3720
- // Visit falsePart
3721
- await this.visit(ternary.falsePart.element, otherTernary.falsePart.element);
3722
- if (!this.match) return ternary;
3723
-
3724
- return ternary;
1462
+ return this.visitElement(ternary, other as J.Ternary);
3725
1463
  }
3726
1464
 
3727
1465
  /**
@@ -3732,17 +1470,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3732
1470
  * @returns The visited throw statement, or undefined if the visit was aborted
3733
1471
  */
3734
1472
  override async visitThrow(throwStatement: J.Throw, other: J): Promise<J | undefined> {
3735
- if (!this.match || other.kind !== J.Kind.Throw) {
3736
- return this.abort(throwStatement);
3737
- }
3738
-
3739
- const otherThrowStatement = other as J.Throw;
3740
-
3741
- // Visit exception
3742
- await this.visit(throwStatement.exception, otherThrowStatement.exception);
3743
- if (!this.match) return throwStatement;
3744
-
3745
- return throwStatement;
1473
+ return this.visitElement(throwStatement, other as J.Throw);
3746
1474
  }
3747
1475
 
3748
1476
  /**
@@ -3753,57 +1481,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3753
1481
  * @returns The visited try statement, or undefined if the visit was aborted
3754
1482
  */
3755
1483
  override async visitTry(tryStatement: J.Try, other: J): Promise<J | undefined> {
3756
- if (!this.match || other.kind !== J.Kind.Try) {
3757
- return this.abort(tryStatement);
3758
- }
3759
-
3760
- const otherTryStatement = other as J.Try;
3761
-
3762
- // Compare resources
3763
- if ((tryStatement.resources === undefined) !== (otherTryStatement.resources === undefined)) {
3764
- return this.abort(tryStatement);
3765
- }
3766
-
3767
- // Visit resources if present
3768
- if (tryStatement.resources && otherTryStatement.resources) {
3769
- if (tryStatement.resources.elements.length !== otherTryStatement.resources.elements.length) {
3770
- return this.abort(tryStatement);
3771
- }
3772
-
3773
- // Visit each resource in lock step
3774
- for (let i = 0; i < tryStatement.resources.elements.length; i++) {
3775
- await this.visit(tryStatement.resources.elements[i].element, otherTryStatement.resources.elements[i].element);
3776
- if (!this.match) return tryStatement;
3777
- }
3778
- }
3779
-
3780
- // Visit body
3781
- await this.visit(tryStatement.body, otherTryStatement.body);
3782
- if (!this.match) return tryStatement;
3783
-
3784
- // Compare catches
3785
- if (tryStatement.catches.length !== otherTryStatement.catches.length) {
3786
- return this.abort(tryStatement);
3787
- }
3788
-
3789
- // Visit each catch in lock step
3790
- for (let i = 0; i < tryStatement.catches.length; i++) {
3791
- await this.visit(tryStatement.catches[i], otherTryStatement.catches[i]);
3792
- if (!this.match) return tryStatement;
3793
- }
3794
-
3795
- // Compare finally
3796
- if ((tryStatement.finally === undefined) !== (otherTryStatement.finally === undefined)) {
3797
- return this.abort(tryStatement);
3798
- }
3799
-
3800
- // Visit finally if present
3801
- if (tryStatement.finally && otherTryStatement.finally) {
3802
- await this.visit(tryStatement.finally.element, otherTryStatement.finally.element);
3803
- if (!this.match) return tryStatement;
3804
- }
3805
-
3806
- return tryStatement;
1484
+ return this.visitElement(tryStatement, other as J.Try);
3807
1485
  }
3808
1486
 
3809
1487
  /**
@@ -3814,22 +1492,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3814
1492
  * @returns The visited try resource, or undefined if the visit was aborted
3815
1493
  */
3816
1494
  override async visitTryResource(resource: J.Try.Resource, other: J): Promise<J | undefined> {
3817
- if (!this.match || other.kind !== J.Kind.TryResource) {
3818
- return this.abort(resource);
3819
- }
3820
-
3821
- const otherResource = other as J.Try.Resource;
3822
-
3823
- // Visit variableDeclarations
3824
- await this.visit(resource.variableDeclarations, otherResource.variableDeclarations);
3825
- if (!this.match) return resource;
3826
-
3827
- // Compare terminatedWithSemicolon
3828
- if (resource.terminatedWithSemicolon !== otherResource.terminatedWithSemicolon) {
3829
- return this.abort(resource);
3830
- }
3831
-
3832
- return resource;
1495
+ return this.visitElement(resource, other as J.Try.Resource);
3833
1496
  }
3834
1497
 
3835
1498
  /**
@@ -3840,21 +1503,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3840
1503
  * @returns The visited try catch block, or undefined if the visit was aborted
3841
1504
  */
3842
1505
  override async visitTryCatch(tryCatch: J.Try.Catch, other: J): Promise<J | undefined> {
3843
- if (!this.match || other.kind !== J.Kind.TryCatch) {
3844
- return this.abort(tryCatch);
3845
- }
3846
-
3847
- const otherTryCatch = other as J.Try.Catch;
3848
-
3849
- // Visit parameter
3850
- await this.visit(tryCatch.parameter, otherTryCatch.parameter);
3851
- if (!this.match) return tryCatch;
3852
-
3853
- // Visit body
3854
- await this.visit(tryCatch.body, otherTryCatch.body);
3855
- if (!this.match) return tryCatch;
3856
-
3857
- return tryCatch;
1506
+ return this.visitElement(tryCatch, other as J.Try.Catch);
3858
1507
  }
3859
1508
 
3860
1509
  /**
@@ -3865,21 +1514,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3865
1514
  * @returns The visited type cast expression, or undefined if the visit was aborted
3866
1515
  */
3867
1516
  override async visitTypeCast(typeCast: J.TypeCast, other: J): Promise<J | undefined> {
3868
- if (!this.match || other.kind !== J.Kind.TypeCast) {
3869
- return this.abort(typeCast);
3870
- }
3871
-
3872
- const otherTypeCast = other as J.TypeCast;
3873
-
3874
- // Visit class
3875
- await this.visit(typeCast.class, otherTypeCast.class);
3876
- if (!this.match) return typeCast;
3877
-
3878
- // Visit expression
3879
- await this.visit(typeCast.expression, otherTypeCast.expression);
3880
- if (!this.match) return typeCast;
3881
-
3882
- return typeCast;
1517
+ return this.visitElement(typeCast, other as J.TypeCast);
3883
1518
  }
3884
1519
 
3885
1520
  /**
@@ -3890,57 +1525,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3890
1525
  * @returns The visited type parameter, or undefined if the visit was aborted
3891
1526
  */
3892
1527
  override async visitTypeParameter(typeParameter: J.TypeParameter, other: J): Promise<J | undefined> {
3893
- if (!this.match || other.kind !== J.Kind.TypeParameter) {
3894
- return this.abort(typeParameter);
3895
- }
3896
-
3897
- const otherTypeParameter = other as J.TypeParameter;
3898
-
3899
- // Compare annotations
3900
- if (typeParameter.annotations.length !== otherTypeParameter.annotations.length) {
3901
- return this.abort(typeParameter);
3902
- }
3903
-
3904
- // Visit each annotation in lock step
3905
- for (let i = 0; i < typeParameter.annotations.length; i++) {
3906
- await this.visit(typeParameter.annotations[i], otherTypeParameter.annotations[i]);
3907
- if (!this.match) return typeParameter;
3908
- }
3909
-
3910
- // Compare modifiers
3911
- if (typeParameter.modifiers.length !== otherTypeParameter.modifiers.length) {
3912
- return this.abort(typeParameter);
3913
- }
3914
-
3915
- // Visit each modifier in lock step
3916
- for (let i = 0; i < typeParameter.modifiers.length; i++) {
3917
- await this.visit(typeParameter.modifiers[i], otherTypeParameter.modifiers[i]);
3918
- if (!this.match) return typeParameter;
3919
- }
3920
-
3921
- // Visit name
3922
- await this.visit(typeParameter.name, otherTypeParameter.name);
3923
- if (!this.match) return typeParameter;
3924
-
3925
- // Compare bounds
3926
- if ((typeParameter.bounds === undefined) !== (otherTypeParameter.bounds === undefined)) {
3927
- return this.abort(typeParameter);
3928
- }
3929
-
3930
- // Visit bounds if present
3931
- if (typeParameter.bounds && otherTypeParameter.bounds) {
3932
- if (typeParameter.bounds.elements.length !== otherTypeParameter.bounds.elements.length) {
3933
- return this.abort(typeParameter);
3934
- }
3935
-
3936
- // Visit each bound in lock step
3937
- for (let i = 0; i < typeParameter.bounds.elements.length; i++) {
3938
- await this.visit(typeParameter.bounds.elements[i].element, otherTypeParameter.bounds.elements[i].element);
3939
- if (!this.match) return typeParameter;
3940
- }
3941
- }
3942
-
3943
- return typeParameter;
1528
+ return this.visitElement(typeParameter, other as J.TypeParameter);
3944
1529
  }
3945
1530
 
3946
1531
  /**
@@ -3951,35 +1536,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3951
1536
  * @returns The visited type parameters, or undefined if the visit was aborted
3952
1537
  */
3953
1538
  override async visitTypeParameters(typeParameters: J.TypeParameters, other: J): Promise<J | undefined> {
3954
- if (!this.match || other.kind !== J.Kind.TypeParameters) {
3955
- return this.abort(typeParameters);
3956
- }
3957
-
3958
- const otherTypeParameters = other as J.TypeParameters;
3959
-
3960
- // Compare annotations
3961
- if (typeParameters.annotations.length !== otherTypeParameters.annotations.length) {
3962
- return this.abort(typeParameters);
3963
- }
3964
-
3965
- // Visit each annotation in lock step
3966
- for (let i = 0; i < typeParameters.annotations.length; i++) {
3967
- await this.visit(typeParameters.annotations[i], otherTypeParameters.annotations[i]);
3968
- if (!this.match) return typeParameters;
3969
- }
3970
-
3971
- // Compare typeParameters
3972
- if (typeParameters.typeParameters.length !== otherTypeParameters.typeParameters.length) {
3973
- return this.abort(typeParameters);
3974
- }
3975
-
3976
- // Visit each type parameter in lock step
3977
- for (let i = 0; i < typeParameters.typeParameters.length; i++) {
3978
- await this.visit(typeParameters.typeParameters[i].element, otherTypeParameters.typeParameters[i].element);
3979
- if (!this.match) return typeParameters;
3980
- }
3981
-
3982
- return typeParameters;
1539
+ return this.visitElement(typeParameters, other as J.TypeParameters);
3983
1540
  }
3984
1541
 
3985
1542
  /**
@@ -3990,22 +1547,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
3990
1547
  * @returns The visited unary expression, or undefined if the visit was aborted
3991
1548
  */
3992
1549
  override async visitUnary(unary: J.Unary, other: J): Promise<J | undefined> {
3993
- if (!this.match || other.kind !== J.Kind.Unary) {
3994
- return this.abort(unary);
3995
- }
3996
-
3997
- const otherUnary = other as J.Unary;
3998
-
3999
- // Compare operator
4000
- if (unary.operator.element !== otherUnary.operator.element) {
4001
- return this.abort(unary);
4002
- }
4003
-
4004
- // Visit expression
4005
- await this.visit(unary.expression, otherUnary.expression);
4006
- if (!this.match) return unary;
4007
-
4008
- return unary;
1550
+ return this.visitElement(unary, other as J.Unary);
4009
1551
  }
4010
1552
 
4011
1553
  /**
@@ -4016,17 +1558,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4016
1558
  * @returns The visited unknown node, or undefined if the visit was aborted
4017
1559
  */
4018
1560
  override async visitUnknown(unknown: J.Unknown, other: J): Promise<J | undefined> {
4019
- if (!this.match || other.kind !== J.Kind.Unknown) {
4020
- return this.abort(unknown);
4021
- }
4022
-
4023
- const otherUnknown = other as J.Unknown;
4024
-
4025
- // Visit source
4026
- await this.visit(unknown.source, otherUnknown.source);
4027
- if (!this.match) return unknown;
4028
-
4029
- return unknown;
1561
+ return this.visitElement(unknown, other as J.Unknown);
4030
1562
  }
4031
1563
 
4032
1564
  /**
@@ -4037,18 +1569,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4037
1569
  * @returns The visited unknown source, or undefined if the visit was aborted
4038
1570
  */
4039
1571
  override async visitUnknownSource(unknownSource: J.UnknownSource, other: J): Promise<J | undefined> {
4040
- if (!this.match || other.kind !== J.Kind.UnknownSource) {
4041
- return this.abort(unknownSource);
4042
- }
4043
-
4044
- const otherUnknownSource = other as J.UnknownSource;
4045
-
4046
- // Compare text
4047
- if (unknownSource.text !== otherUnknownSource.text) {
4048
- return this.abort(unknownSource);
4049
- }
4050
-
4051
- return unknownSource;
1572
+ return this.visitElement(unknownSource, other as J.UnknownSource);
4052
1573
  }
4053
1574
 
4054
1575
  /**
@@ -4059,62 +1580,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4059
1580
  * @returns The visited variable declarations, or undefined if the visit was aborted
4060
1581
  */
4061
1582
  override async visitVariableDeclarations(variableDeclarations: J.VariableDeclarations, other: J): Promise<J | undefined> {
4062
- if (!this.match || other.kind !== J.Kind.VariableDeclarations) {
4063
- return this.abort(variableDeclarations);
4064
- }
4065
-
4066
- const otherVariableDeclarations = other as J.VariableDeclarations;
4067
-
4068
- // Compare leadingAnnotations
4069
- if (variableDeclarations.leadingAnnotations.length !== otherVariableDeclarations.leadingAnnotations.length) {
4070
- return this.abort(variableDeclarations);
4071
- }
4072
-
4073
- // Visit each leading annotation in lock step
4074
- for (let i = 0; i < variableDeclarations.leadingAnnotations.length; i++) {
4075
- await this.visit(variableDeclarations.leadingAnnotations[i], otherVariableDeclarations.leadingAnnotations[i]);
4076
- if (!this.match) return variableDeclarations;
4077
- }
4078
-
4079
- // Compare modifiers
4080
- if (variableDeclarations.modifiers.length !== otherVariableDeclarations.modifiers.length) {
4081
- return this.abort(variableDeclarations);
4082
- }
4083
-
4084
- // Visit each modifier in lock step
4085
- for (let i = 0; i < variableDeclarations.modifiers.length; i++) {
4086
- await this.visit(variableDeclarations.modifiers[i], otherVariableDeclarations.modifiers[i]);
4087
- if (!this.match) return variableDeclarations;
4088
- }
4089
-
4090
- // Compare typeExpression
4091
- if ((variableDeclarations.typeExpression === undefined) !== (otherVariableDeclarations.typeExpression === undefined)) {
4092
- return this.abort(variableDeclarations);
4093
- }
4094
-
4095
- // Visit typeExpression if present
4096
- if (variableDeclarations.typeExpression && otherVariableDeclarations.typeExpression) {
4097
- await this.visit(variableDeclarations.typeExpression, otherVariableDeclarations.typeExpression);
4098
- if (!this.match) return variableDeclarations;
4099
- }
4100
-
4101
- // Compare varargs
4102
- if ((variableDeclarations.varargs === undefined) !== (otherVariableDeclarations.varargs === undefined)) {
4103
- return this.abort(variableDeclarations);
4104
- }
4105
-
4106
- // Compare variables
4107
- if (variableDeclarations.variables.length !== otherVariableDeclarations.variables.length) {
4108
- return this.abort(variableDeclarations);
4109
- }
4110
-
4111
- // Visit each variable in lock step
4112
- for (let i = 0; i < variableDeclarations.variables.length; i++) {
4113
- await this.visit(variableDeclarations.variables[i].element, otherVariableDeclarations.variables[i].element);
4114
- if (!this.match) return variableDeclarations;
4115
- }
4116
-
4117
- return variableDeclarations;
1583
+ return this.visitElement(variableDeclarations, other as J.VariableDeclarations);
4118
1584
  }
4119
1585
 
4120
1586
  /**
@@ -4125,33 +1591,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4125
1591
  * @returns The visited variable declaration, or undefined if the visit was aborted
4126
1592
  */
4127
1593
  override async visitVariable(variable: J.VariableDeclarations.NamedVariable, other: J): Promise<J | undefined> {
4128
- if (!this.match || other.kind !== J.Kind.NamedVariable) {
4129
- return this.abort(variable);
4130
- }
4131
-
4132
- const otherVariable = other as J.VariableDeclarations.NamedVariable;
4133
-
4134
- // Visit name
4135
- await this.visit(variable.name, otherVariable.name);
4136
- if (!this.match) return variable;
4137
-
4138
- // Compare dimensionsAfterName
4139
- if (variable.dimensionsAfterName.length !== otherVariable.dimensionsAfterName.length) {
4140
- return this.abort(variable);
4141
- }
4142
-
4143
- // Compare initializer
4144
- if ((variable.initializer === undefined) !== (otherVariable.initializer === undefined)) {
4145
- return this.abort(variable);
4146
- }
4147
-
4148
- // Visit initializer if present
4149
- if (variable.initializer && otherVariable.initializer) {
4150
- await this.visit(variable.initializer.element, otherVariable.initializer.element);
4151
- if (!this.match) return variable;
4152
- }
4153
-
4154
- return variable;
1594
+ return this.visitElement(variable, other as J.VariableDeclarations.NamedVariable);
4155
1595
  }
4156
1596
 
4157
1597
  /**
@@ -4162,21 +1602,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4162
1602
  * @returns The visited while loop, or undefined if the visit was aborted
4163
1603
  */
4164
1604
  override async visitWhileLoop(whileLoop: J.WhileLoop, other: J): Promise<J | undefined> {
4165
- if (!this.match || other.kind !== J.Kind.WhileLoop) {
4166
- return this.abort(whileLoop);
4167
- }
4168
-
4169
- const otherWhileLoop = other as J.WhileLoop;
4170
-
4171
- // Visit condition
4172
- await this.visit(whileLoop.condition, otherWhileLoop.condition);
4173
- if (!this.match) return whileLoop;
4174
-
4175
- // Visit body
4176
- await this.visit(whileLoop.body.element, otherWhileLoop.body.element);
4177
- if (!this.match) return whileLoop;
4178
-
4179
- return whileLoop;
1605
+ return this.visitElement(whileLoop, other as J.WhileLoop);
4180
1606
  }
4181
1607
 
4182
1608
  /**
@@ -4187,36 +1613,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4187
1613
  * @returns The visited wildcard, or undefined if the visit was aborted
4188
1614
  */
4189
1615
  override async visitWildcard(wildcard: J.Wildcard, other: J): Promise<J | undefined> {
4190
- if (!this.match || other.kind !== J.Kind.Wildcard) {
4191
- return this.abort(wildcard);
4192
- }
4193
-
4194
- const otherWildcard = other as J.Wildcard;
4195
-
4196
- // Compare bound
4197
- if ((wildcard.bound === undefined) !== (otherWildcard.bound === undefined)) {
4198
- return this.abort(wildcard);
4199
- }
4200
-
4201
- // Compare bound if present
4202
- if (wildcard.bound && otherWildcard.bound) {
4203
- if (wildcard.bound.element !== otherWildcard.bound.element) {
4204
- return this.abort(wildcard);
4205
- }
4206
- }
4207
-
4208
- // Compare boundedType
4209
- if ((wildcard.boundedType === undefined) !== (otherWildcard.boundedType === undefined)) {
4210
- return this.abort(wildcard);
4211
- }
4212
-
4213
- // Visit boundedType if present
4214
- if (wildcard.boundedType && otherWildcard.boundedType) {
4215
- await this.visit(wildcard.boundedType, otherWildcard.boundedType);
4216
- if (!this.match) return wildcard;
4217
- }
4218
-
4219
- return wildcard;
1616
+ return this.visitElement(wildcard, other as J.Wildcard);
4220
1617
  }
4221
1618
 
4222
1619
  /**
@@ -4227,22 +1624,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4227
1624
  * @returns The visited yield statement, or undefined if the visit was aborted
4228
1625
  */
4229
1626
  override async visitYield(yieldStatement: J.Yield, other: J): Promise<J | undefined> {
4230
- if (!this.match || other.kind !== J.Kind.Yield) {
4231
- return this.abort(yieldStatement);
4232
- }
4233
-
4234
- const otherYieldStatement = other as J.Yield;
4235
-
4236
- // Compare implicit
4237
- if (yieldStatement.implicit !== otherYieldStatement.implicit) {
4238
- return this.abort(yieldStatement);
4239
- }
4240
-
4241
- // Visit value
4242
- await this.visit(yieldStatement.value, otherYieldStatement.value);
4243
- if (!this.match) return yieldStatement;
4244
-
4245
- return yieldStatement;
1627
+ return this.visitElement(yieldStatement, other as J.Yield);
4246
1628
  }
4247
1629
 
4248
1630
  /**
@@ -4253,16 +1635,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4253
1635
  * @returns The visited void expression, or undefined if the visit was aborted
4254
1636
  */
4255
1637
  override async visitVoid(void_: JS.Void, other: J): Promise<J | undefined> {
4256
- if (!this.match || other.kind !== JS.Kind.Void) {
4257
- return this.abort(void_);
4258
- }
4259
-
4260
- const otherVoid = other as JS.Void;
4261
-
4262
- // Visit expression
4263
- await this.visit(void_.expression, otherVoid.expression);
4264
-
4265
- return void_;
1638
+ return this.visitElement(void_, other as JS.Void);
4266
1639
  }
4267
1640
 
4268
1641
  /**
@@ -4273,20 +1646,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4273
1646
  * @returns The visited with statement, or undefined if the visit was aborted
4274
1647
  */
4275
1648
  override async visitWithStatement(withStatement: JS.WithStatement, other: J): Promise<J | undefined> {
4276
- if (!this.match || other.kind !== JS.Kind.WithStatement) {
4277
- return this.abort(withStatement);
4278
- }
4279
-
4280
- const otherWithStatement = other as JS.WithStatement;
4281
-
4282
- // Visit expression
4283
- await this.visit(withStatement.expression, otherWithStatement.expression);
4284
- if (!this.match) return withStatement;
4285
-
4286
- // Visit body
4287
- await this.visit(withStatement.body.element, otherWithStatement.body.element);
4288
-
4289
- return withStatement;
1649
+ return this.visitElement(withStatement, other as JS.WithStatement);
4290
1650
  }
4291
1651
 
4292
1652
  /**
@@ -4297,39 +1657,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4297
1657
  * @returns The visited index signature declaration, or undefined if the visit was aborted
4298
1658
  */
4299
1659
  override async visitIndexSignatureDeclaration(indexSignatureDeclaration: JS.IndexSignatureDeclaration, other: J): Promise<J | undefined> {
4300
- if (!this.match || other.kind !== JS.Kind.IndexSignatureDeclaration) {
4301
- return this.abort(indexSignatureDeclaration);
4302
- }
4303
-
4304
- const otherIndexSignatureDeclaration = other as JS.IndexSignatureDeclaration;
4305
-
4306
- // Compare modifiers
4307
- if (indexSignatureDeclaration.modifiers.length !== otherIndexSignatureDeclaration.modifiers.length) {
4308
- return this.abort(indexSignatureDeclaration);
4309
- }
4310
-
4311
- // Visit modifiers in lock step
4312
- for (let i = 0; i < indexSignatureDeclaration.modifiers.length; i++) {
4313
- await this.visit(indexSignatureDeclaration.modifiers[i], otherIndexSignatureDeclaration.modifiers[i]);
4314
- if (!this.match) return indexSignatureDeclaration;
4315
- }
4316
-
4317
- // Compare parameters
4318
- if (indexSignatureDeclaration.parameters.elements.length !== otherIndexSignatureDeclaration.parameters.elements.length) {
4319
- return this.abort(indexSignatureDeclaration);
4320
- }
4321
-
4322
- // Visit parameters in lock step
4323
- for (let i = 0; i < indexSignatureDeclaration.parameters.elements.length; i++) {
4324
- await this.visit(indexSignatureDeclaration.parameters.elements[i].element,
4325
- otherIndexSignatureDeclaration.parameters.elements[i].element);
4326
- if (!this.match) return indexSignatureDeclaration;
4327
- }
4328
-
4329
- // Visit type expression
4330
- await this.visit(indexSignatureDeclaration.typeExpression.element, otherIndexSignatureDeclaration.typeExpression.element);
4331
-
4332
- return indexSignatureDeclaration;
1660
+ return this.visitElement(indexSignatureDeclaration, other as JS.IndexSignatureDeclaration);
4333
1661
  }
4334
1662
 
4335
1663
  /**
@@ -4340,22 +1668,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4340
1668
  * @returns The visited for-of loop, or undefined if the visit was aborted
4341
1669
  */
4342
1670
  override async visitForOfLoop(forOfLoop: JS.ForOfLoop, other: J): Promise<J | undefined> {
4343
- if (!this.match || other.kind !== JS.Kind.ForOfLoop) {
4344
- return this.abort(forOfLoop);
4345
- }
4346
-
4347
- const otherForOfLoop = other as JS.ForOfLoop;
4348
-
4349
- // Compare await
4350
- if ((forOfLoop.await === undefined) !== (otherForOfLoop.await === undefined)) {
4351
- return this.abort(forOfLoop);
4352
- }
4353
-
4354
- // Visit loop
4355
- await this.visit(forOfLoop.loop, otherForOfLoop.loop);
4356
- if (!this.match) return forOfLoop;
4357
-
4358
- return forOfLoop;
1671
+ return this.visitElement(forOfLoop, other as JS.ForOfLoop);
4359
1672
  }
4360
1673
 
4361
1674
  /**
@@ -4366,69 +1679,18 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4366
1679
  * @returns The visited for-in loop, or undefined if the visit was aborted
4367
1680
  */
4368
1681
  override async visitForInLoop(forInLoop: JS.ForInLoop, other: J): Promise<J | undefined> {
4369
- if (!this.match || other.kind !== JS.Kind.ForInLoop) {
4370
- return this.abort(forInLoop);
4371
- }
4372
-
4373
- const otherForInLoop = other as JS.ForInLoop;
4374
-
4375
- // Visit control
4376
- await this.visit(forInLoop.control, otherForInLoop.control);
4377
- if (!this.match) return forInLoop;
4378
-
4379
- // Visit body
4380
- await this.visit(forInLoop.body.element, otherForInLoop.body.element);
4381
- if (!this.match) return forInLoop;
4382
-
4383
- return forInLoop;
1682
+ return this.visitElement(forInLoop, other as JS.ForInLoop);
4384
1683
  }
4385
1684
 
4386
1685
  /**
4387
- * Overrides the visitNamespaceDeclaration method to compare namespace declarations.
4388
- *
4389
- * @param namespaceDeclaration The namespace declaration to visit
4390
- * @param other The other namespace declaration to compare with
4391
- * @returns The visited namespace declaration, or undefined if the visit was aborted
4392
- */
4393
- override async visitNamespaceDeclaration(namespaceDeclaration: JS.NamespaceDeclaration, other: J): Promise<J | undefined> {
4394
- if (!this.match || other.kind !== JS.Kind.NamespaceDeclaration) {
4395
- return this.abort(namespaceDeclaration);
4396
- }
4397
-
4398
- const otherNamespaceDeclaration = other as JS.NamespaceDeclaration;
4399
-
4400
- // Compare modifiers
4401
- if (namespaceDeclaration.modifiers.length !== otherNamespaceDeclaration.modifiers.length) {
4402
- return this.abort(namespaceDeclaration);
4403
- }
4404
-
4405
- // Visit each modifier in lock step
4406
- for (let i = 0; i < namespaceDeclaration.modifiers.length; i++) {
4407
- await this.visit(namespaceDeclaration.modifiers[i], otherNamespaceDeclaration.modifiers[i]);
4408
- if (!this.match) return namespaceDeclaration;
4409
- }
4410
-
4411
- // Compare keywordType
4412
- if (namespaceDeclaration.keywordType.element !== otherNamespaceDeclaration.keywordType.element) {
4413
- return this.abort(namespaceDeclaration);
4414
- }
4415
-
4416
- // Visit name
4417
- await this.visit(namespaceDeclaration.name.element, otherNamespaceDeclaration.name.element);
4418
- if (!this.match) return namespaceDeclaration;
4419
-
4420
- // Compare body
4421
- if ((namespaceDeclaration.body === undefined) !== (otherNamespaceDeclaration.body === undefined)) {
4422
- return this.abort(namespaceDeclaration);
4423
- }
4424
-
4425
- // Visit body if present
4426
- if (namespaceDeclaration.body && otherNamespaceDeclaration.body) {
4427
- await this.visit(namespaceDeclaration.body, otherNamespaceDeclaration.body);
4428
- if (!this.match) return namespaceDeclaration;
4429
- }
4430
-
4431
- return namespaceDeclaration;
1686
+ * Overrides the visitNamespaceDeclaration method to compare namespace declarations.
1687
+ *
1688
+ * @param namespaceDeclaration The namespace declaration to visit
1689
+ * @param other The other namespace declaration to compare with
1690
+ * @returns The visited namespace declaration, or undefined if the visit was aborted
1691
+ */
1692
+ override async visitNamespaceDeclaration(namespaceDeclaration: JS.NamespaceDeclaration, other: J): Promise<J | undefined> {
1693
+ return this.visitElement(namespaceDeclaration, other as JS.NamespaceDeclaration);
4432
1694
  }
4433
1695
 
4434
1696
  /**
@@ -4439,17 +1701,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4439
1701
  * @returns The visited type literal, or undefined if the visit was aborted
4440
1702
  */
4441
1703
  override async visitTypeLiteral(typeLiteral: JS.TypeLiteral, other: J): Promise<J | undefined> {
4442
- if (!this.match || other.kind !== JS.Kind.TypeLiteral) {
4443
- return this.abort(typeLiteral);
4444
- }
4445
-
4446
- const otherTypeLiteral = other as JS.TypeLiteral;
4447
-
4448
- // Visit members
4449
- await this.visit(typeLiteral.members, otherTypeLiteral.members);
4450
- if (!this.match) return typeLiteral;
4451
-
4452
- return typeLiteral;
1704
+ return this.visitElement(typeLiteral, other as JS.TypeLiteral);
4453
1705
  }
4454
1706
 
4455
1707
  /**
@@ -4460,39 +1712,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4460
1712
  * @returns The visited binding element, or undefined if the visit was aborted
4461
1713
  */
4462
1714
  override async visitBindingElement(bindingElement: JS.BindingElement, other: J): Promise<J | undefined> {
4463
- if (!this.match || other.kind !== JS.Kind.BindingElement) {
4464
- return this.abort(bindingElement);
4465
- }
4466
-
4467
- const otherBindingElement = other as JS.BindingElement;
4468
-
4469
- // Compare propertyName
4470
- if ((bindingElement.propertyName === undefined) !== (otherBindingElement.propertyName === undefined)) {
4471
- return this.abort(bindingElement);
4472
- }
4473
-
4474
- // Visit propertyName if present
4475
- if (bindingElement.propertyName && otherBindingElement.propertyName) {
4476
- await this.visit(bindingElement.propertyName.element, otherBindingElement.propertyName.element);
4477
- if (!this.match) return bindingElement;
4478
- }
4479
-
4480
- // Visit name
4481
- await this.visit(bindingElement.name, otherBindingElement.name);
4482
- if (!this.match) return bindingElement;
4483
-
4484
- // Compare initializer
4485
- if ((bindingElement.initializer === undefined) !== (otherBindingElement.initializer === undefined)) {
4486
- return this.abort(bindingElement);
4487
- }
4488
-
4489
- // Visit initializer if present
4490
- if (bindingElement.initializer && otherBindingElement.initializer) {
4491
- await this.visit(bindingElement.initializer.element, otherBindingElement.initializer.element);
4492
- if (!this.match) return bindingElement;
4493
- }
4494
-
4495
- return bindingElement;
1715
+ return this.visitElement(bindingElement, other as JS.BindingElement);
4496
1716
  }
4497
1717
 
4498
1718
  /**
@@ -4503,24 +1723,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4503
1723
  * @returns The visited array binding pattern, or undefined if the visit was aborted
4504
1724
  */
4505
1725
  override async visitArrayBindingPattern(arrayBindingPattern: JS.ArrayBindingPattern, other: J): Promise<J | undefined> {
4506
- if (!this.match || other.kind !== JS.Kind.ArrayBindingPattern) {
4507
- return this.abort(arrayBindingPattern);
4508
- }
4509
-
4510
- const otherArrayBindingPattern = other as JS.ArrayBindingPattern;
4511
-
4512
- // Compare elements
4513
- if (arrayBindingPattern.elements.elements.length !== otherArrayBindingPattern.elements.elements.length) {
4514
- return this.abort(arrayBindingPattern);
4515
- }
4516
-
4517
- // Visit each element in lock step
4518
- for (let i = 0; i < arrayBindingPattern.elements.elements.length; i++) {
4519
- await this.visit(arrayBindingPattern.elements.elements[i].element, otherArrayBindingPattern.elements.elements[i].element);
4520
- if (!this.match) return arrayBindingPattern;
4521
- }
4522
-
4523
- return arrayBindingPattern;
1726
+ return this.visitElement(arrayBindingPattern, other as JS.ArrayBindingPattern);
4524
1727
  }
4525
1728
 
4526
1729
  /**
@@ -4531,62 +1734,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4531
1734
  * @returns The visited export declaration, or undefined if the visit was aborted
4532
1735
  */
4533
1736
  override async visitExportDeclaration(exportDeclaration: JS.ExportDeclaration, other: J): Promise<J | undefined> {
4534
- if (!this.match || other.kind !== JS.Kind.ExportDeclaration) {
4535
- return this.abort(exportDeclaration);
4536
- }
4537
-
4538
- const otherExportDeclaration = other as JS.ExportDeclaration;
4539
-
4540
- // Compare modifiers
4541
- if (exportDeclaration.modifiers.length !== otherExportDeclaration.modifiers.length) {
4542
- return this.abort(exportDeclaration);
4543
- }
4544
-
4545
- // Visit each modifier in lock step
4546
- for (let i = 0; i < exportDeclaration.modifiers.length; i++) {
4547
- await this.visit(exportDeclaration.modifiers[i], otherExportDeclaration.modifiers[i]);
4548
- if (!this.match) return exportDeclaration;
4549
- }
4550
-
4551
- // Compare typeOnly
4552
- if (exportDeclaration.typeOnly.element !== otherExportDeclaration.typeOnly.element) {
4553
- return this.abort(exportDeclaration);
4554
- }
4555
-
4556
- // Compare exportClause
4557
- if ((exportDeclaration.exportClause === undefined) !== (otherExportDeclaration.exportClause === undefined)) {
4558
- return this.abort(exportDeclaration);
4559
- }
4560
-
4561
- // Visit exportClause if present
4562
- if (exportDeclaration.exportClause && otherExportDeclaration.exportClause) {
4563
- await this.visit(exportDeclaration.exportClause, otherExportDeclaration.exportClause);
4564
- if (!this.match) return exportDeclaration;
4565
- }
4566
-
4567
- // Compare moduleSpecifier
4568
- if ((exportDeclaration.moduleSpecifier === undefined) !== (otherExportDeclaration.moduleSpecifier === undefined)) {
4569
- return this.abort(exportDeclaration);
4570
- }
4571
-
4572
- // Visit moduleSpecifier if present
4573
- if (exportDeclaration.moduleSpecifier && otherExportDeclaration.moduleSpecifier) {
4574
- await this.visit(exportDeclaration.moduleSpecifier.element, otherExportDeclaration.moduleSpecifier.element);
4575
- if (!this.match) return exportDeclaration;
4576
- }
4577
-
4578
- // Compare attributes
4579
- if ((exportDeclaration.attributes === undefined) !== (otherExportDeclaration.attributes === undefined)) {
4580
- return this.abort(exportDeclaration);
4581
- }
4582
-
4583
- // Visit attributes if present
4584
- if (exportDeclaration.attributes && otherExportDeclaration.attributes) {
4585
- await this.visit(exportDeclaration.attributes, otherExportDeclaration.attributes);
4586
- if (!this.match) return exportDeclaration;
4587
- }
4588
-
4589
- return exportDeclaration;
1737
+ return this.visitElement(exportDeclaration, other as JS.ExportDeclaration);
4590
1738
  }
4591
1739
 
4592
1740
  /**
@@ -4597,22 +1745,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4597
1745
  * @returns The visited export assignment, or undefined if the visit was aborted
4598
1746
  */
4599
1747
  override async visitExportAssignment(exportAssignment: JS.ExportAssignment, other: J): Promise<J | undefined> {
4600
- if (!this.match || other.kind !== JS.Kind.ExportAssignment) {
4601
- return this.abort(exportAssignment);
4602
- }
4603
-
4604
- const otherExportAssignment = other as JS.ExportAssignment;
4605
-
4606
- // Compare exportEquals
4607
- if (exportAssignment.exportEquals !== otherExportAssignment.exportEquals) {
4608
- return this.abort(exportAssignment);
4609
- }
4610
-
4611
- // Visit expression
4612
- await this.visit(exportAssignment.expression.element, otherExportAssignment.expression.element);
4613
- if (!this.match) return exportAssignment;
4614
-
4615
- return exportAssignment;
1748
+ return this.visitElement(exportAssignment, other as JS.ExportAssignment);
4616
1749
  }
4617
1750
 
4618
1751
  /**
@@ -4623,24 +1756,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4623
1756
  * @returns The visited named exports, or undefined if the visit was aborted
4624
1757
  */
4625
1758
  override async visitNamedExports(namedExports: JS.NamedExports, other: J): Promise<J | undefined> {
4626
- if (!this.match || other.kind !== JS.Kind.NamedExports) {
4627
- return this.abort(namedExports);
4628
- }
4629
-
4630
- const otherNamedExports = other as JS.NamedExports;
4631
-
4632
- // Compare elements
4633
- if (namedExports.elements.elements.length !== otherNamedExports.elements.elements.length) {
4634
- return this.abort(namedExports);
4635
- }
4636
-
4637
- // Visit each element in lock step
4638
- for (let i = 0; i < namedExports.elements.elements.length; i++) {
4639
- await this.visit(namedExports.elements.elements[i].element, otherNamedExports.elements.elements[i].element);
4640
- if (!this.match) return namedExports;
4641
- }
4642
-
4643
- return namedExports;
1759
+ return this.visitElement(namedExports, other as JS.NamedExports);
4644
1760
  }
4645
1761
 
4646
1762
  /**
@@ -4651,22 +1767,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4651
1767
  * @returns The visited export specifier, or undefined if the visit was aborted
4652
1768
  */
4653
1769
  override async visitExportSpecifier(exportSpecifier: JS.ExportSpecifier, other: J): Promise<J | undefined> {
4654
- if (!this.match || other.kind !== JS.Kind.ExportSpecifier) {
4655
- return this.abort(exportSpecifier);
4656
- }
4657
-
4658
- const otherExportSpecifier = other as JS.ExportSpecifier;
4659
-
4660
- // Compare typeOnly
4661
- if (exportSpecifier.typeOnly.element !== otherExportSpecifier.typeOnly.element) {
4662
- return this.abort(exportSpecifier);
4663
- }
4664
-
4665
- // Visit specifier
4666
- await this.visit(exportSpecifier.specifier, otherExportSpecifier.specifier);
4667
- if (!this.match) return exportSpecifier;
4668
-
4669
- return exportSpecifier;
1770
+ return this.visitElement(exportSpecifier, other as JS.ExportSpecifier);
4670
1771
  }
4671
1772
 
4672
1773
  /**
@@ -4677,80 +1778,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4677
1778
  * @returns The visited computed property method declaration, or undefined if the visit was aborted
4678
1779
  */
4679
1780
  override async visitComputedPropertyMethodDeclaration(computedPropMethod: JS.ComputedPropertyMethodDeclaration, other: J): Promise<J | undefined> {
4680
- if (!this.match || other.kind !== JS.Kind.ComputedPropertyMethodDeclaration) {
4681
- return this.abort(computedPropMethod);
4682
- }
4683
-
4684
- const otherComputedPropMethod = other as JS.ComputedPropertyMethodDeclaration;
4685
-
4686
- // Compare leading annotations
4687
- if (computedPropMethod.leadingAnnotations.length !== otherComputedPropMethod.leadingAnnotations.length) {
4688
- return this.abort(computedPropMethod);
4689
- }
4690
-
4691
- // Visit leading annotations in lock step
4692
- for (let i = 0; i < computedPropMethod.leadingAnnotations.length; i++) {
4693
- await this.visit(computedPropMethod.leadingAnnotations[i], otherComputedPropMethod.leadingAnnotations[i]);
4694
- if (!this.match) return computedPropMethod;
4695
- }
4696
-
4697
- // Compare modifiers
4698
- if (computedPropMethod.modifiers.length !== otherComputedPropMethod.modifiers.length) {
4699
- return this.abort(computedPropMethod);
4700
- }
4701
-
4702
- // Visit modifiers in lock step
4703
- for (let i = 0; i < computedPropMethod.modifiers.length; i++) {
4704
- await this.visit(computedPropMethod.modifiers[i], otherComputedPropMethod.modifiers[i]);
4705
- if (!this.match) return computedPropMethod;
4706
- }
4707
-
4708
- // Compare type parameters
4709
- if (!!computedPropMethod.typeParameters !== !!otherComputedPropMethod.typeParameters) {
4710
- return this.abort(computedPropMethod);
4711
- }
4712
-
4713
- if (computedPropMethod.typeParameters) {
4714
- await this.visit(computedPropMethod.typeParameters, otherComputedPropMethod.typeParameters!);
4715
- if (!this.match) return computedPropMethod;
4716
- }
4717
-
4718
- // Compare return type expression
4719
- if (!!computedPropMethod.returnTypeExpression !== !!otherComputedPropMethod.returnTypeExpression) {
4720
- return this.abort(computedPropMethod);
4721
- }
4722
-
4723
- if (computedPropMethod.returnTypeExpression) {
4724
- await this.visit(computedPropMethod.returnTypeExpression, otherComputedPropMethod.returnTypeExpression!);
4725
- if (!this.match) return computedPropMethod;
4726
- }
4727
-
4728
- // Visit name
4729
- await this.visit(computedPropMethod.name, otherComputedPropMethod.name);
4730
- if (!this.match) return computedPropMethod;
4731
-
4732
- // Compare parameters
4733
- if (computedPropMethod.parameters.elements.length !== otherComputedPropMethod.parameters.elements.length) {
4734
- return this.abort(computedPropMethod);
4735
- }
4736
-
4737
- // Visit parameters in lock step
4738
- for (let i = 0; i < computedPropMethod.parameters.elements.length; i++) {
4739
- await this.visit(computedPropMethod.parameters.elements[i].element,
4740
- otherComputedPropMethod.parameters.elements[i].element);
4741
- if (!this.match) return computedPropMethod;
4742
- }
4743
-
4744
- // Compare body
4745
- if (!!computedPropMethod.body !== !!otherComputedPropMethod.body) {
4746
- return this.abort(computedPropMethod);
4747
- }
4748
-
4749
- if (computedPropMethod.body) {
4750
- await this.visit(computedPropMethod.body, otherComputedPropMethod.body!);
4751
- }
4752
-
4753
- return computedPropMethod;
1781
+ return this.visitElement(computedPropMethod, other as JS.ComputedPropertyMethodDeclaration);
4754
1782
  }
4755
1783
  }
4756
1784
 
@@ -4761,13 +1789,58 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
4761
1789
  * when both refer to the same method).
4762
1790
  */
4763
1791
  export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVisitor {
1792
+ /**
1793
+ * When true, allows patterns without type annotations to match code with type annotations.
1794
+ * This enables lenient matching where undefined types on either side are considered compatible.
1795
+ */
1796
+ protected readonly lenientTypeMatching: boolean;
1797
+
1798
+ /**
1799
+ * Creates a new semantic comparator visitor.
1800
+ *
1801
+ * @param lenientTypeMatching If true, allows matching between nodes with and without type annotations
1802
+ */
1803
+ constructor(lenientTypeMatching: boolean = false) {
1804
+ super();
1805
+ this.lenientTypeMatching = lenientTypeMatching;
1806
+ }
1807
+
1808
+ /**
1809
+ * Override visitProperty to allow lenient type matching.
1810
+ * When lenientTypeMatching is enabled, null vs Type comparisons are allowed
1811
+ * (where one value is null/undefined and the other is a Type object).
1812
+ */
1813
+ protected override async visitProperty(j: any, other: any): Promise<any> {
1814
+ // Handle null/undefined with lenient type matching
1815
+ if (this.lenientTypeMatching && (j == null || other == null)) {
1816
+ if (j !== other) {
1817
+ // Don't abort if one is null and the other is a Type
1818
+ const jKind = (j as any)?.kind;
1819
+ const otherKind = (other as any)?.kind;
1820
+ const isTypeComparison =
1821
+ (jKind && typeof jKind === 'string' && jKind.startsWith('org.openrewrite.java.tree.JavaType$')) ||
1822
+ (otherKind && typeof otherKind === 'string' && otherKind.startsWith('org.openrewrite.java.tree.JavaType$'));
1823
+
1824
+ if (!isTypeComparison) {
1825
+ this.abort(j);
1826
+ }
1827
+ }
1828
+ return j;
1829
+ }
1830
+
1831
+ // Otherwise, use base class behavior
1832
+ return super.visitProperty(j, other);
1833
+ }
1834
+
4764
1835
  /**
4765
1836
  * Checks if two types are semantically equal.
4766
1837
  * For method types, this checks that the declaring type and method name match.
1838
+ * With lenient type matching, undefined types on either side are considered compatible.
4767
1839
  */
4768
1840
  private isOfType(target?: Type, source?: Type): boolean {
4769
1841
  if (!target || !source) {
4770
- return target === source;
1842
+ // Lenient mode: if either type is undefined, allow the match
1843
+ return this.lenientTypeMatching ? true : target === source;
4771
1844
  }
4772
1845
 
4773
1846
  if (target.kind !== source.kind) {
@@ -4858,7 +1931,11 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
4858
1931
  // Check type attribution
4859
1932
  // Both must have method types for semantic equality
4860
1933
  if (!method.methodType || !otherMethod.methodType) {
4861
- // If template has type but target doesn't, they don't match
1934
+ // Lenient mode: if either has no type, allow structural matching
1935
+ if (this.lenientTypeMatching) {
1936
+ return super.visitMethodInvocation(method, other);
1937
+ }
1938
+ // Strict mode: if one has type but the other doesn't, they don't match
4862
1939
  if (method.methodType || otherMethod.methodType) {
4863
1940
  return this.abort(method);
4864
1941
  }
@@ -4891,9 +1968,6 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
4891
1968
 
4892
1969
  if (method.select && otherMethod.select) {
4893
1970
  await this.visit(method.select.element, otherMethod.select.element);
4894
- if (!this.match) {
4895
- return this.abort(method);
4896
- }
4897
1971
  }
4898
1972
  }
4899
1973
 
@@ -4952,10 +2026,167 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
4952
2026
  return this.abort(identifier);
4953
2027
  }
4954
2028
  } else if (identifier.fieldType || otherIdentifier.fieldType) {
4955
- // If only one has a type, they don't match
4956
- return this.abort(identifier);
2029
+ // Lenient mode: if either has no type, allow structural matching
2030
+ if (!this.lenientTypeMatching) {
2031
+ // Strict mode: if only one has a type, they don't match
2032
+ return this.abort(identifier);
2033
+ }
4957
2034
  }
4958
2035
 
4959
2036
  return super.visitIdentifier(identifier, other);
4960
2037
  }
2038
+
2039
+ /**
2040
+ * Override variable declarations comparison to handle lenient type matching.
2041
+ * When lenientTypeMatching is true, patterns without typeExpression can match
2042
+ * code with typeExpression.
2043
+ */
2044
+ override async visitVariableDeclarations(variableDeclarations: J.VariableDeclarations, other: J): Promise<J | undefined> {
2045
+ const otherVariableDeclarations = other as J.VariableDeclarations;
2046
+
2047
+ // Visit leading annotations
2048
+ if (variableDeclarations.leadingAnnotations.length !== otherVariableDeclarations.leadingAnnotations.length) {
2049
+ return this.abort(variableDeclarations);
2050
+ }
2051
+
2052
+ for (let i = 0; i < variableDeclarations.leadingAnnotations.length; i++) {
2053
+ await this.visit(variableDeclarations.leadingAnnotations[i], otherVariableDeclarations.leadingAnnotations[i]);
2054
+ if (!this.match) return variableDeclarations;
2055
+ }
2056
+
2057
+ // Visit modifiers
2058
+ if (variableDeclarations.modifiers.length !== otherVariableDeclarations.modifiers.length) {
2059
+ return this.abort(variableDeclarations);
2060
+ }
2061
+
2062
+ for (let i = 0; i < variableDeclarations.modifiers.length; i++) {
2063
+ await this.visit(variableDeclarations.modifiers[i], otherVariableDeclarations.modifiers[i]);
2064
+ if (!this.match) return variableDeclarations;
2065
+ }
2066
+
2067
+ // Compare typeExpression - lenient matching allows one to be undefined
2068
+ if ((variableDeclarations.typeExpression === undefined) !== (otherVariableDeclarations.typeExpression === undefined)) {
2069
+ if (!this.lenientTypeMatching) {
2070
+ return this.abort(variableDeclarations);
2071
+ }
2072
+ // In lenient mode, skip type comparison and continue
2073
+ } else if (variableDeclarations.typeExpression && otherVariableDeclarations.typeExpression) {
2074
+ // Both have typeExpression, visit them
2075
+ await this.visit(variableDeclarations.typeExpression, otherVariableDeclarations.typeExpression);
2076
+ if (!this.match) return variableDeclarations;
2077
+ }
2078
+
2079
+ // Compare varargs
2080
+ if ((variableDeclarations.varargs === undefined) !== (otherVariableDeclarations.varargs === undefined)) {
2081
+ return this.abort(variableDeclarations);
2082
+ }
2083
+
2084
+ // Compare variables
2085
+ if (variableDeclarations.variables.length !== otherVariableDeclarations.variables.length) {
2086
+ return this.abort(variableDeclarations);
2087
+ }
2088
+
2089
+ // Visit each variable in lock step
2090
+ for (let i = 0; i < variableDeclarations.variables.length; i++) {
2091
+ await this.visit(variableDeclarations.variables[i].element, otherVariableDeclarations.variables[i].element);
2092
+ if (!this.match) return variableDeclarations;
2093
+ }
2094
+
2095
+ return variableDeclarations;
2096
+ }
2097
+
2098
+ /**
2099
+ * Override method declaration comparison to handle lenient type matching.
2100
+ * When lenientTypeMatching is true, patterns without returnTypeExpression can match
2101
+ * code with returnTypeExpression.
2102
+ */
2103
+ override async visitMethodDeclaration(methodDeclaration: J.MethodDeclaration, other: J): Promise<J | undefined> {
2104
+ const otherMethodDeclaration = other as J.MethodDeclaration;
2105
+
2106
+ // Visit leading annotations
2107
+ if (methodDeclaration.leadingAnnotations.length !== otherMethodDeclaration.leadingAnnotations.length) {
2108
+ return this.abort(methodDeclaration);
2109
+ }
2110
+
2111
+ for (let i = 0; i < methodDeclaration.leadingAnnotations.length; i++) {
2112
+ await this.visit(methodDeclaration.leadingAnnotations[i], otherMethodDeclaration.leadingAnnotations[i]);
2113
+ if (!this.match) return methodDeclaration;
2114
+ }
2115
+
2116
+ // Visit modifiers
2117
+ if (methodDeclaration.modifiers.length !== otherMethodDeclaration.modifiers.length) {
2118
+ return this.abort(methodDeclaration);
2119
+ }
2120
+
2121
+ for (let i = 0; i < methodDeclaration.modifiers.length; i++) {
2122
+ await this.visit(methodDeclaration.modifiers[i], otherMethodDeclaration.modifiers[i]);
2123
+ if (!this.match) return methodDeclaration;
2124
+ }
2125
+
2126
+ // Visit type parameters if present
2127
+ if (!!methodDeclaration.typeParameters !== !!otherMethodDeclaration.typeParameters) {
2128
+ return this.abort(methodDeclaration);
2129
+ }
2130
+
2131
+ if (methodDeclaration.typeParameters && otherMethodDeclaration.typeParameters) {
2132
+ await this.visit(methodDeclaration.typeParameters, otherMethodDeclaration.typeParameters);
2133
+ if (!this.match) return methodDeclaration;
2134
+ }
2135
+
2136
+ // Compare returnTypeExpression - lenient matching allows one to be undefined
2137
+ if ((methodDeclaration.returnTypeExpression === undefined) !== (otherMethodDeclaration.returnTypeExpression === undefined)) {
2138
+ if (!this.lenientTypeMatching) {
2139
+ return this.abort(methodDeclaration);
2140
+ }
2141
+ // In lenient mode, skip type comparison and continue
2142
+ } else if (methodDeclaration.returnTypeExpression && otherMethodDeclaration.returnTypeExpression) {
2143
+ // Both have returnTypeExpression, visit them
2144
+ await this.visit(methodDeclaration.returnTypeExpression, otherMethodDeclaration.returnTypeExpression);
2145
+ if (!this.match) return methodDeclaration;
2146
+ }
2147
+
2148
+ // Visit name
2149
+ await this.visit(methodDeclaration.name, otherMethodDeclaration.name);
2150
+ if (!this.match) return methodDeclaration;
2151
+
2152
+ // Compare parameters
2153
+ if (methodDeclaration.parameters.elements.length !== otherMethodDeclaration.parameters.elements.length) {
2154
+ return this.abort(methodDeclaration);
2155
+ }
2156
+
2157
+ // Visit each parameter in lock step
2158
+ for (let i = 0; i < methodDeclaration.parameters.elements.length; i++) {
2159
+ await this.visit(methodDeclaration.parameters.elements[i].element, otherMethodDeclaration.parameters.elements[i].element);
2160
+ if (!this.match) return methodDeclaration;
2161
+ }
2162
+
2163
+ // Visit throws if present
2164
+ if (!!methodDeclaration.throws !== !!otherMethodDeclaration.throws) {
2165
+ return this.abort(methodDeclaration);
2166
+ }
2167
+
2168
+ if (methodDeclaration.throws && otherMethodDeclaration.throws) {
2169
+ // Visit each throws expression in lock step
2170
+ if (methodDeclaration.throws.elements.length !== otherMethodDeclaration.throws.elements.length) {
2171
+ return this.abort(methodDeclaration);
2172
+ }
2173
+
2174
+ for (let i = 0; i < methodDeclaration.throws.elements.length; i++) {
2175
+ await this.visit(methodDeclaration.throws.elements[i].element, otherMethodDeclaration.throws.elements[i].element);
2176
+ if (!this.match) return methodDeclaration;
2177
+ }
2178
+ }
2179
+
2180
+ // Visit body if present
2181
+ if (!!methodDeclaration.body !== !!otherMethodDeclaration.body) {
2182
+ return this.abort(methodDeclaration);
2183
+ }
2184
+
2185
+ if (methodDeclaration.body && otherMethodDeclaration.body) {
2186
+ await this.visit(methodDeclaration.body, otherMethodDeclaration.body);
2187
+ if (!this.match) return methodDeclaration;
2188
+ }
2189
+
2190
+ return methodDeclaration;
2191
+ }
4961
2192
  }