@openrewrite/rewrite 8.67.0-20251112-160335 → 8.67.0-20251113-160321
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.
- package/dist/javascript/comparator.d.ts +64 -3
- package/dist/javascript/comparator.d.ts.map +1 -1
- package/dist/javascript/comparator.js +216 -155
- package/dist/javascript/comparator.js.map +1 -1
- package/dist/javascript/templating/comparator.d.ts +118 -9
- package/dist/javascript/templating/comparator.d.ts.map +1 -1
- package/dist/javascript/templating/comparator.js +821 -73
- package/dist/javascript/templating/comparator.js.map +1 -1
- package/dist/javascript/templating/index.d.ts +1 -1
- package/dist/javascript/templating/index.d.ts.map +1 -1
- package/dist/javascript/templating/index.js.map +1 -1
- package/dist/javascript/templating/pattern.d.ts +89 -5
- package/dist/javascript/templating/pattern.d.ts.map +1 -1
- package/dist/javascript/templating/pattern.js +442 -31
- package/dist/javascript/templating/pattern.js.map +1 -1
- package/dist/javascript/templating/types.d.ts +157 -1
- package/dist/javascript/templating/types.d.ts.map +1 -1
- package/dist/version.txt +1 -1
- package/package.json +1 -1
- package/src/javascript/comparator.ts +249 -169
- package/src/javascript/templating/comparator.ts +952 -87
- package/src/javascript/templating/index.ts +6 -1
- package/src/javascript/templating/pattern.ts +543 -23
- package/src/javascript/templating/types.ts +178 -1
|
@@ -67,25 +67,158 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
|
|
|
67
67
|
|
|
68
68
|
/**
|
|
69
69
|
* Aborts the visit operation by setting the match flag to false.
|
|
70
|
+
*
|
|
71
|
+
* @param t The node being compared
|
|
72
|
+
* @param reason Optional reason for the mismatch (e.g., 'kind-mismatch', 'property-mismatch')
|
|
73
|
+
* @param propertyName Optional property name where mismatch occurred
|
|
74
|
+
* @param expected Optional expected value
|
|
75
|
+
* @param actual Optional actual value
|
|
70
76
|
*/
|
|
71
|
-
protected abort<T>(t: T): T {
|
|
77
|
+
protected abort<T>(t: T, reason?: string, propertyName?: string, expected?: any, actual?: any): T {
|
|
72
78
|
this.match = false;
|
|
73
79
|
return t;
|
|
74
80
|
}
|
|
75
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Specialized abort methods for common mismatch scenarios.
|
|
84
|
+
* These provide a cleaner API at call sites.
|
|
85
|
+
* Can be overridden in subclasses to extract values from cursors and provide richer error messages.
|
|
86
|
+
*/
|
|
87
|
+
|
|
88
|
+
protected kindMismatch() {
|
|
89
|
+
const pattern = this.cursor?.value as any;
|
|
90
|
+
return this.abort(pattern, 'kind-mismatch');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
protected structuralMismatch(propertyName?: string) {
|
|
94
|
+
const pattern = this.cursor?.value as any;
|
|
95
|
+
return this.abort(pattern, 'structural-mismatch', propertyName);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
protected arrayLengthMismatch(propertyName: string) {
|
|
99
|
+
const pattern = this.cursor?.value as any;
|
|
100
|
+
return this.abort(pattern, 'array-length-mismatch', propertyName);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
protected valueMismatch(propertyName?: string, expected?: any, actual?: any) {
|
|
104
|
+
const pattern = this.cursor?.value as any;
|
|
105
|
+
// If values not provided, try to extract from cursors (only if propertyName is available)
|
|
106
|
+
const expectedVal = expected !== undefined ? expected : (propertyName ? (pattern as any)?.[propertyName] : pattern);
|
|
107
|
+
const actualVal = actual !== undefined ? actual : (propertyName ? (this.targetCursor?.value as any)?.[propertyName] : this.targetCursor?.value);
|
|
108
|
+
return this.abort(pattern, 'value-mismatch', propertyName, expectedVal, actualVal);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
protected typeMismatch(propertyName?: string) {
|
|
112
|
+
const pattern = this.cursor?.value as any;
|
|
113
|
+
const target = this.targetCursor?.value as any;
|
|
114
|
+
return this.abort(pattern, 'type-mismatch', propertyName, pattern?.type, target?.type);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Helper method to visit an array property by iterating through both arrays in lock-step.
|
|
119
|
+
* Checks length mismatch first, then visits each element pair.
|
|
120
|
+
* Can be overridden in subclasses to add path tracking or other instrumentation.
|
|
121
|
+
*
|
|
122
|
+
* @param parent The parent node containing the array property
|
|
123
|
+
* @param propertyName The name of the array property
|
|
124
|
+
* @param array1 The array from the first tree
|
|
125
|
+
* @param array2 The array from the second tree
|
|
126
|
+
* @param visitor Function to visit each element pair (no need to return anything)
|
|
127
|
+
* @returns undefined, modifying this.match if a mismatch occurs
|
|
128
|
+
*/
|
|
129
|
+
protected async visitArrayProperty<T>(
|
|
130
|
+
parent: J,
|
|
131
|
+
propertyName: string,
|
|
132
|
+
array1: T[],
|
|
133
|
+
array2: T[],
|
|
134
|
+
visitor: (item1: T, item2: T, index: number) => Promise<void>
|
|
135
|
+
): Promise<void> {
|
|
136
|
+
// Check length mismatch
|
|
137
|
+
if (array1.length !== array2.length) {
|
|
138
|
+
this.arrayLengthMismatch(propertyName);
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Visit each element in lock step
|
|
143
|
+
for (let i = 0; i < array1.length; i++) {
|
|
144
|
+
await visitor(array1[i], array2[i], i);
|
|
145
|
+
if (!this.match) return;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Helper method to visit a container property with proper context.
|
|
151
|
+
* Can be overridden in subclasses to add path tracking or other instrumentation.
|
|
152
|
+
*
|
|
153
|
+
* @param parent The parent node containing the container property
|
|
154
|
+
* @param propertyName The name of the container property
|
|
155
|
+
* @param container The container from the first tree
|
|
156
|
+
* @param otherContainer The container from the second tree
|
|
157
|
+
* @returns The container from the first tree
|
|
158
|
+
*/
|
|
159
|
+
protected async visitContainerProperty<T extends J>(
|
|
160
|
+
propertyName: string,
|
|
161
|
+
container: J.Container<T>,
|
|
162
|
+
otherContainer: J.Container<T>
|
|
163
|
+
): Promise<J.Container<T>> {
|
|
164
|
+
// Default implementation just calls visitContainer
|
|
165
|
+
// Subclasses can override to add property context
|
|
166
|
+
await this.visitContainer(container, otherContainer as any);
|
|
167
|
+
return container;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Helper to visit a RightPadded property with property context.
|
|
172
|
+
* This allows subclasses to track which property is being visited.
|
|
173
|
+
*
|
|
174
|
+
* @param propertyName The property name for context
|
|
175
|
+
* @param rightPadded The RightPadded from the first tree
|
|
176
|
+
* @param otherRightPadded The RightPadded from the second tree
|
|
177
|
+
* @returns The RightPadded from the first tree
|
|
178
|
+
*/
|
|
179
|
+
protected async visitRightPaddedProperty<T extends J | boolean>(
|
|
180
|
+
propertyName: string,
|
|
181
|
+
rightPadded: J.RightPadded<T>,
|
|
182
|
+
otherRightPadded: J.RightPadded<T>
|
|
183
|
+
): Promise<J.RightPadded<T>> {
|
|
184
|
+
// Default implementation just calls visitRightPadded
|
|
185
|
+
// Subclasses can override to add property context
|
|
186
|
+
return await this.visitRightPadded(rightPadded, otherRightPadded as any);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
/**
|
|
190
|
+
* Helper to visit a LeftPadded property with property context.
|
|
191
|
+
* This allows subclasses to track which property is being visited.
|
|
192
|
+
*
|
|
193
|
+
* @param propertyName The property name for context
|
|
194
|
+
* @param leftPadded The LeftPadded from the first tree
|
|
195
|
+
* @param otherLeftPadded The LeftPadded from the second tree
|
|
196
|
+
* @returns The LeftPadded from the first tree
|
|
197
|
+
*/
|
|
198
|
+
protected async visitLeftPaddedProperty<T extends J | J.Space | number | string | boolean>(
|
|
199
|
+
propertyName: string,
|
|
200
|
+
leftPadded: J.LeftPadded<T>,
|
|
201
|
+
otherLeftPadded: J.LeftPadded<T>
|
|
202
|
+
): Promise<J.LeftPadded<T>> {
|
|
203
|
+
// Default implementation just calls visitLeftPadded
|
|
204
|
+
// Subclasses can override to add property context
|
|
205
|
+
return await this.visitLeftPadded(leftPadded, otherLeftPadded as any);
|
|
206
|
+
}
|
|
207
|
+
|
|
76
208
|
/**
|
|
77
209
|
* Generic method to visit a property value using the appropriate visitor method.
|
|
78
210
|
* This ensures wrappers (RightPadded, LeftPadded, Container) are properly tracked on the cursor.
|
|
79
211
|
*
|
|
80
212
|
* @param j The property value from the first tree
|
|
81
213
|
* @param other The corresponding property value from the second tree
|
|
214
|
+
* @param propertyName Optional property name for error reporting
|
|
82
215
|
* @returns The visited property value from the first tree
|
|
83
216
|
*/
|
|
84
|
-
protected async visitProperty(j: any, other: any): Promise<any> {
|
|
217
|
+
protected async visitProperty(j: any, other: any, propertyName?: string): Promise<any> {
|
|
85
218
|
// Handle null/undefined (but not other falsy values like 0, false, '')
|
|
86
219
|
if (j == null || other == null) {
|
|
87
220
|
if (j !== other) {
|
|
88
|
-
this.
|
|
221
|
+
return this.structuralMismatch(propertyName);
|
|
89
222
|
}
|
|
90
223
|
return j;
|
|
91
224
|
}
|
|
@@ -94,14 +227,20 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
|
|
|
94
227
|
|
|
95
228
|
// Check wrappers by kind
|
|
96
229
|
if (kind === J.Kind.RightPadded) {
|
|
97
|
-
return await this.
|
|
230
|
+
return propertyName ? await this.visitRightPaddedProperty(propertyName, j, other) :
|
|
231
|
+
await this.visitRightPadded(j, other);
|
|
98
232
|
}
|
|
99
233
|
|
|
100
234
|
if (kind === J.Kind.LeftPadded) {
|
|
101
|
-
return await this.
|
|
235
|
+
return propertyName ? await this.visitLeftPaddedProperty(propertyName, j, other) :
|
|
236
|
+
await this.visitLeftPadded(j, other);
|
|
102
237
|
}
|
|
103
238
|
|
|
104
239
|
if (kind === J.Kind.Container) {
|
|
240
|
+
// Use visitContainerProperty when propertyName is provided for proper context tracking
|
|
241
|
+
if (propertyName) {
|
|
242
|
+
return await this.visitContainerProperty(propertyName, j, other);
|
|
243
|
+
}
|
|
105
244
|
return await this.visitContainer(j, other);
|
|
106
245
|
}
|
|
107
246
|
|
|
@@ -121,7 +260,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
|
|
|
121
260
|
|
|
122
261
|
// For primitive values, compare directly
|
|
123
262
|
if (j !== other) {
|
|
124
|
-
this.
|
|
263
|
+
return this.valueMismatch(propertyName, j, other);
|
|
125
264
|
}
|
|
126
265
|
return j;
|
|
127
266
|
}
|
|
@@ -136,19 +275,17 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
|
|
|
136
275
|
* @returns The visited element from the first tree
|
|
137
276
|
*/
|
|
138
277
|
protected async visitElement<T extends J>(j: T, other: T): Promise<T> {
|
|
139
|
-
if (!this.match)
|
|
140
|
-
return j;
|
|
141
|
-
}
|
|
278
|
+
if (!this.match) return j;
|
|
142
279
|
|
|
143
280
|
// Check if kinds match
|
|
144
281
|
if (j.kind !== other.kind) {
|
|
145
|
-
return this.
|
|
282
|
+
return this.kindMismatch();
|
|
146
283
|
}
|
|
147
284
|
|
|
148
285
|
// Iterate over all properties
|
|
149
286
|
for (const key of Object.keys(j)) {
|
|
150
287
|
// Skip internal/private properties, id property, and markers property
|
|
151
|
-
if (key.startsWith('_') || key === 'id' || key === 'markers') {
|
|
288
|
+
if (key.startsWith('_') || key === 'kind' || key === 'id' || key === 'markers') {
|
|
152
289
|
continue;
|
|
153
290
|
}
|
|
154
291
|
|
|
@@ -158,22 +295,18 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
|
|
|
158
295
|
// Handle arrays - compare element by element
|
|
159
296
|
if (Array.isArray(jValue)) {
|
|
160
297
|
if (!Array.isArray(otherValue) || jValue.length !== otherValue.length) {
|
|
161
|
-
return this.
|
|
298
|
+
return this.arrayLengthMismatch(key);
|
|
162
299
|
}
|
|
163
300
|
|
|
164
301
|
for (let i = 0; i < jValue.length; i++) {
|
|
165
|
-
await this.visitProperty(jValue[i], otherValue[i]);
|
|
166
|
-
if (!this.match)
|
|
167
|
-
return j;
|
|
168
|
-
}
|
|
302
|
+
await this.visitProperty(jValue[i], otherValue[i], `${key}[${i}]`);
|
|
303
|
+
if (!this.match) return j;
|
|
169
304
|
}
|
|
170
305
|
} else {
|
|
171
306
|
// Visit the property (which will handle wrappers, trees, primitives, etc.)
|
|
172
|
-
await this.visitProperty(jValue, otherValue);
|
|
307
|
+
await this.visitProperty(jValue, otherValue, key);
|
|
173
308
|
|
|
174
|
-
if (!this.match)
|
|
175
|
-
return j;
|
|
176
|
-
}
|
|
309
|
+
if (!this.match) return j;
|
|
177
310
|
}
|
|
178
311
|
}
|
|
179
312
|
|
|
@@ -182,13 +315,11 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
|
|
|
182
315
|
|
|
183
316
|
override async visit<R extends J>(j: Tree, p: J, parent?: Cursor): Promise<R | undefined> {
|
|
184
317
|
// If we've already found a mismatch, abort further processing
|
|
185
|
-
if (!this.match)
|
|
186
|
-
return j as R;
|
|
187
|
-
}
|
|
318
|
+
if (!this.match) return j as R;
|
|
188
319
|
|
|
189
320
|
// Check if the nodes have the same kind
|
|
190
321
|
if (!this.hasSameKind(j as J, p)) {
|
|
191
|
-
return this.
|
|
322
|
+
return this.kindMismatch() as R;
|
|
192
323
|
}
|
|
193
324
|
|
|
194
325
|
// Update targetCursor to track the target node in parallel with the pattern cursor
|
|
@@ -210,9 +341,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
|
|
|
210
341
|
* Also updates targetCursor in parallel.
|
|
211
342
|
*/
|
|
212
343
|
public async visitRightPadded<T extends J | boolean>(right: J.RightPadded<T>, p: J): Promise<J.RightPadded<T>> {
|
|
213
|
-
if (!this.match)
|
|
214
|
-
return right;
|
|
215
|
-
}
|
|
344
|
+
if (!this.match) return right;
|
|
216
345
|
|
|
217
346
|
// Extract the other element if it's also a RightPadded
|
|
218
347
|
const isRightPadded = (p as any).kind === J.Kind.RightPadded;
|
|
@@ -225,6 +354,8 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
|
|
|
225
354
|
this.cursor = new Cursor(right, this.cursor);
|
|
226
355
|
this.targetCursor = otherWrapper ? new Cursor(otherWrapper, this.targetCursor) : this.targetCursor;
|
|
227
356
|
try {
|
|
357
|
+
// Call visitProperty without propertyName to avoid pushing spurious 'element' path entries
|
|
358
|
+
// The property context should be provided through visitRightPaddedProperty() if needed
|
|
228
359
|
await this.visitProperty(right.element, otherElement);
|
|
229
360
|
} finally {
|
|
230
361
|
this.cursor = savedCursor;
|
|
@@ -241,9 +372,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
|
|
|
241
372
|
* Also updates targetCursor in parallel.
|
|
242
373
|
*/
|
|
243
374
|
public async visitLeftPadded<T extends J | J.Space | number | string | boolean>(left: J.LeftPadded<T>, p: J): Promise<J.LeftPadded<T>> {
|
|
244
|
-
if (!this.match)
|
|
245
|
-
return left;
|
|
246
|
-
}
|
|
375
|
+
if (!this.match) return left;
|
|
247
376
|
|
|
248
377
|
// Extract the other element if it's also a LeftPadded
|
|
249
378
|
const isLeftPadded = (p as any).kind === J.Kind.LeftPadded;
|
|
@@ -256,6 +385,8 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
|
|
|
256
385
|
this.cursor = new Cursor(left, this.cursor);
|
|
257
386
|
this.targetCursor = otherWrapper ? new Cursor(otherWrapper, this.targetCursor) : this.targetCursor;
|
|
258
387
|
try {
|
|
388
|
+
// Call visitProperty without propertyName to avoid pushing spurious 'element' path entries
|
|
389
|
+
// The property context should be provided through visitLeftPaddedProperty() if needed
|
|
259
390
|
await this.visitProperty(left.element, otherElement);
|
|
260
391
|
} finally {
|
|
261
392
|
this.cursor = savedCursor;
|
|
@@ -272,9 +403,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
|
|
|
272
403
|
* Also updates targetCursor in parallel.
|
|
273
404
|
*/
|
|
274
405
|
public async visitContainer<T extends J>(container: J.Container<T>, p: J): Promise<J.Container<T>> {
|
|
275
|
-
if (!this.match)
|
|
276
|
-
return container;
|
|
277
|
-
}
|
|
406
|
+
if (!this.match) return container;
|
|
278
407
|
|
|
279
408
|
// Extract the other elements if it's also a Container
|
|
280
409
|
const isContainer = (p as any).kind === J.Kind.Container;
|
|
@@ -283,7 +412,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
|
|
|
283
412
|
|
|
284
413
|
// Compare elements array length
|
|
285
414
|
if (container.elements.length !== otherElements.length) {
|
|
286
|
-
return this.
|
|
415
|
+
return this.arrayLengthMismatch('elements');
|
|
287
416
|
}
|
|
288
417
|
|
|
289
418
|
// Push wrappers onto both cursors, then compare each element
|
|
@@ -294,9 +423,7 @@ export class JavaScriptComparatorVisitor extends JavaScriptVisitor<J> {
|
|
|
294
423
|
try {
|
|
295
424
|
for (let i = 0; i < container.elements.length; i++) {
|
|
296
425
|
await this.visitProperty(container.elements[i], otherElements[i]);
|
|
297
|
-
if (!this.match)
|
|
298
|
-
return this.abort(container);
|
|
299
|
-
}
|
|
426
|
+
if (!this.match) return container;
|
|
300
427
|
}
|
|
301
428
|
} finally {
|
|
302
429
|
this.cursor = savedCursor;
|
|
@@ -1866,9 +1993,7 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
1866
1993
|
|
|
1867
1994
|
override async visit<R extends J>(j: Tree, p: J, parent?: Cursor): Promise<R | undefined> {
|
|
1868
1995
|
// If we've already found a mismatch, abort further processing
|
|
1869
|
-
if (!this.match)
|
|
1870
|
-
return j as R;
|
|
1871
|
-
}
|
|
1996
|
+
if (!this.match) return j as R;
|
|
1872
1997
|
|
|
1873
1998
|
// Unwrap parentheses from both trees before comparing
|
|
1874
1999
|
const unwrappedJ = this.unwrap(j) || j;
|
|
@@ -1896,12 +2021,10 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
1896
2021
|
* - `(x, y) => x + y` matches `(x, y) => { return x + y; }`
|
|
1897
2022
|
*/
|
|
1898
2023
|
override async visitArrowFunction(arrowFunction: JS.ArrowFunction, other: J): Promise<J | undefined> {
|
|
1899
|
-
if (!this.match)
|
|
1900
|
-
return arrowFunction;
|
|
1901
|
-
}
|
|
2024
|
+
if (!this.match) return arrowFunction;
|
|
1902
2025
|
|
|
1903
2026
|
if (other.kind !== JS.Kind.ArrowFunction) {
|
|
1904
|
-
return this.
|
|
2027
|
+
return this.kindMismatch();
|
|
1905
2028
|
}
|
|
1906
2029
|
|
|
1907
2030
|
const otherArrow = other as JS.ArrowFunction;
|
|
@@ -1918,7 +2041,7 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
1918
2041
|
// Handle arrays
|
|
1919
2042
|
if (Array.isArray(jValue)) {
|
|
1920
2043
|
if (!Array.isArray(otherValue) || jValue.length !== otherValue.length) {
|
|
1921
|
-
return this.
|
|
2044
|
+
return this.arrayLengthMismatch(key);
|
|
1922
2045
|
}
|
|
1923
2046
|
for (let i = 0; i < jValue.length; i++) {
|
|
1924
2047
|
await this.visitProperty(jValue[i], otherValue[i]);
|
|
@@ -1934,7 +2057,7 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
1934
2057
|
const params1 = arrowFunction.lambda.parameters.parameters;
|
|
1935
2058
|
const params2 = otherArrow.lambda.parameters.parameters;
|
|
1936
2059
|
if (params1.length !== params2.length) {
|
|
1937
|
-
return this.
|
|
2060
|
+
return this.arrayLengthMismatch('lambda.parameters.parameters');
|
|
1938
2061
|
}
|
|
1939
2062
|
for (let i = 0; i < params1.length; i++) {
|
|
1940
2063
|
await this.visitProperty(params1[i], params2[i]);
|
|
@@ -1969,12 +2092,10 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
1969
2092
|
* - `x => x + 1` matches `(x) => x + 1`
|
|
1970
2093
|
*/
|
|
1971
2094
|
override async visitLambdaParameters(parameters: J.Lambda.Parameters, other: J): Promise<J | undefined> {
|
|
1972
|
-
if (!this.match)
|
|
1973
|
-
return parameters;
|
|
1974
|
-
}
|
|
2095
|
+
if (!this.match) return parameters;
|
|
1975
2096
|
|
|
1976
2097
|
if (other.kind !== J.Kind.LambdaParameters) {
|
|
1977
|
-
return this.
|
|
2098
|
+
return this.kindMismatch();
|
|
1978
2099
|
}
|
|
1979
2100
|
|
|
1980
2101
|
const otherParams = other as J.Lambda.Parameters;
|
|
@@ -1991,7 +2112,7 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
1991
2112
|
// Handle arrays
|
|
1992
2113
|
if (Array.isArray(jValue)) {
|
|
1993
2114
|
if (!Array.isArray(otherValue) || jValue.length !== otherValue.length) {
|
|
1994
|
-
return this.
|
|
2115
|
+
return this.arrayLengthMismatch(key);
|
|
1995
2116
|
}
|
|
1996
2117
|
for (let i = 0; i < jValue.length; i++) {
|
|
1997
2118
|
await this.visitProperty(jValue[i], otherValue[i]);
|
|
@@ -2015,12 +2136,10 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2015
2136
|
* - `{ x: x, y: y }` matches `{ x, y }`
|
|
2016
2137
|
*/
|
|
2017
2138
|
override async visitPropertyAssignment(propertyAssignment: JS.PropertyAssignment, other: J): Promise<J | undefined> {
|
|
2018
|
-
if (!this.match)
|
|
2019
|
-
return propertyAssignment;
|
|
2020
|
-
}
|
|
2139
|
+
if (!this.match) return propertyAssignment;
|
|
2021
2140
|
|
|
2022
2141
|
if (other.kind !== JS.Kind.PropertyAssignment) {
|
|
2023
|
-
return this.
|
|
2142
|
+
return this.kindMismatch();
|
|
2024
2143
|
}
|
|
2025
2144
|
|
|
2026
2145
|
const otherProp = other as JS.PropertyAssignment;
|
|
@@ -2053,7 +2172,7 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2053
2172
|
return propertyAssignment;
|
|
2054
2173
|
} else {
|
|
2055
2174
|
// Not equivalent (e.g., { x: y })
|
|
2056
|
-
return this.
|
|
2175
|
+
return this.structuralMismatch('initializer');
|
|
2057
2176
|
}
|
|
2058
2177
|
}
|
|
2059
2178
|
|
|
@@ -2109,7 +2228,7 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2109
2228
|
* When lenientTypeMatching is enabled, null vs Type comparisons are allowed
|
|
2110
2229
|
* (where one value is null/undefined and the other is a Type object).
|
|
2111
2230
|
*/
|
|
2112
|
-
protected override async visitProperty(j: any, other: any): Promise<any> {
|
|
2231
|
+
protected override async visitProperty(j: any, other: any, propertyName?: string): Promise<any> {
|
|
2113
2232
|
// Handle null/undefined with lenient type matching
|
|
2114
2233
|
if (this.lenientTypeMatching && (j == null || other == null)) {
|
|
2115
2234
|
if (j !== other) {
|
|
@@ -2121,7 +2240,7 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2121
2240
|
(otherKind && typeof otherKind === 'string' && otherKind.startsWith('org.openrewrite.java.tree.JavaType$'));
|
|
2122
2241
|
|
|
2123
2242
|
if (!isTypeComparison) {
|
|
2124
|
-
this.
|
|
2243
|
+
this.structuralMismatch(propertyName!);
|
|
2125
2244
|
}
|
|
2126
2245
|
}
|
|
2127
2246
|
return j;
|
|
@@ -2218,16 +2337,11 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2218
2337
|
*/
|
|
2219
2338
|
override async visitMethodInvocation(method: J.MethodInvocation, other: J): Promise<J | undefined> {
|
|
2220
2339
|
if (other.kind !== J.Kind.MethodInvocation) {
|
|
2221
|
-
return this.
|
|
2340
|
+
return this.kindMismatch();
|
|
2222
2341
|
}
|
|
2223
2342
|
|
|
2224
2343
|
const otherMethod = other as J.MethodInvocation;
|
|
2225
2344
|
|
|
2226
|
-
// Check argument length (always required)
|
|
2227
|
-
if (method.arguments.elements.length !== otherMethod.arguments.elements.length) {
|
|
2228
|
-
return this.abort(method);
|
|
2229
|
-
}
|
|
2230
|
-
|
|
2231
2345
|
// Check if we can skip name checking based on type attribution
|
|
2232
2346
|
// We can only skip the name check if both have method types AND they represent the SAME method
|
|
2233
2347
|
// (not just type-compatible methods, but the actual same function with same FQN)
|
|
@@ -2257,7 +2371,7 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2257
2371
|
// Check names unless we determined we can skip based on type FQN matching
|
|
2258
2372
|
if (!canSkipNameCheck) {
|
|
2259
2373
|
if (method.name.simpleName !== otherMethod.name.simpleName) {
|
|
2260
|
-
return this.
|
|
2374
|
+
return this.valueMismatch('name.simpleName', method.name.simpleName, otherMethod.name.simpleName);
|
|
2261
2375
|
}
|
|
2262
2376
|
|
|
2263
2377
|
// In strict mode, check type attribution requirements
|
|
@@ -2265,7 +2379,7 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2265
2379
|
// Strict mode: if one has type but the other doesn't, they don't match
|
|
2266
2380
|
if ((method.methodType && !otherMethod.methodType) ||
|
|
2267
2381
|
(!method.methodType && otherMethod.methodType)) {
|
|
2268
|
-
return this.
|
|
2382
|
+
return this.typeMismatch('methodType');
|
|
2269
2383
|
}
|
|
2270
2384
|
}
|
|
2271
2385
|
|
|
@@ -2288,7 +2402,7 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2288
2402
|
|
|
2289
2403
|
// Different declaring types = different methods, even with same name
|
|
2290
2404
|
if (methodFQN !== otherFQN) {
|
|
2291
|
-
return this.
|
|
2405
|
+
return this.valueMismatch('methodType.declaringType');
|
|
2292
2406
|
}
|
|
2293
2407
|
}
|
|
2294
2408
|
}
|
|
@@ -2299,33 +2413,24 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2299
2413
|
if (!canSkipNameCheck) {
|
|
2300
2414
|
// Types didn't provide a match - must compare receivers structurally
|
|
2301
2415
|
if ((method.select === undefined) !== (otherMethod.select === undefined)) {
|
|
2302
|
-
return this.
|
|
2416
|
+
return this.structuralMismatch('select');
|
|
2303
2417
|
}
|
|
2304
2418
|
|
|
2305
2419
|
if (method.select && otherMethod.select) {
|
|
2306
|
-
await this.
|
|
2307
|
-
if (!this.match)
|
|
2308
|
-
return this.abort(method);
|
|
2309
|
-
}
|
|
2420
|
+
await this.visitRightPaddedProperty('select', method.select, otherMethod.select as any);
|
|
2421
|
+
if (!this.match) return method;
|
|
2310
2422
|
}
|
|
2311
2423
|
}
|
|
2312
2424
|
// else: types matched, skip select comparison (allows namespace vs named imports)
|
|
2313
2425
|
|
|
2314
2426
|
// Compare type parameters
|
|
2315
2427
|
if ((method.typeParameters === undefined) !== (otherMethod.typeParameters === undefined)) {
|
|
2316
|
-
return this.
|
|
2428
|
+
return this.structuralMismatch('typeParameters');
|
|
2317
2429
|
}
|
|
2318
2430
|
|
|
2319
2431
|
if (method.typeParameters && otherMethod.typeParameters) {
|
|
2320
|
-
|
|
2321
|
-
|
|
2322
|
-
}
|
|
2323
|
-
for (let i = 0; i < method.typeParameters.elements.length; i++) {
|
|
2324
|
-
await this.visitRightPadded(method.typeParameters.elements[i], otherMethod.typeParameters.elements[i] as any);
|
|
2325
|
-
if (!this.match) {
|
|
2326
|
-
return this.abort(method);
|
|
2327
|
-
}
|
|
2328
|
-
}
|
|
2432
|
+
await this.visitContainerProperty('typeParameters', method.typeParameters, otherMethod.typeParameters);
|
|
2433
|
+
if (!this.match) return method;
|
|
2329
2434
|
}
|
|
2330
2435
|
|
|
2331
2436
|
// Compare name
|
|
@@ -2333,18 +2438,12 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2333
2438
|
// This allows matching aliased imports where names differ but types are the same
|
|
2334
2439
|
if (!canSkipNameCheck) {
|
|
2335
2440
|
await this.visit(method.name, otherMethod.name);
|
|
2336
|
-
if (!this.match)
|
|
2337
|
-
return this.abort(method);
|
|
2338
|
-
}
|
|
2441
|
+
if (!this.match) return method;
|
|
2339
2442
|
}
|
|
2340
2443
|
|
|
2341
|
-
// Compare arguments
|
|
2342
|
-
|
|
2343
|
-
|
|
2344
|
-
if (!this.match) {
|
|
2345
|
-
return this.abort(method);
|
|
2346
|
-
}
|
|
2347
|
-
}
|
|
2444
|
+
// Compare arguments
|
|
2445
|
+
await this.visitContainerProperty('arguments', method.arguments, otherMethod.arguments);
|
|
2446
|
+
if (!this.match) return method;
|
|
2348
2447
|
|
|
2349
2448
|
return method;
|
|
2350
2449
|
}
|
|
@@ -2362,26 +2461,26 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2362
2461
|
}
|
|
2363
2462
|
|
|
2364
2463
|
if (other.kind !== J.Kind.Identifier) {
|
|
2365
|
-
return this.
|
|
2464
|
+
return this.kindMismatch();
|
|
2366
2465
|
}
|
|
2367
2466
|
|
|
2368
2467
|
const otherIdentifier = other as J.Identifier;
|
|
2369
2468
|
|
|
2370
2469
|
// Check name matches
|
|
2371
2470
|
if (identifier.simpleName !== otherIdentifier.simpleName) {
|
|
2372
|
-
return this.
|
|
2471
|
+
return this.valueMismatch('simpleName');
|
|
2373
2472
|
}
|
|
2374
2473
|
|
|
2375
2474
|
// For identifiers with field types, check type attribution
|
|
2376
2475
|
if (identifier.fieldType && otherIdentifier.fieldType) {
|
|
2377
2476
|
if (!this.isOfType(identifier.fieldType, otherIdentifier.fieldType)) {
|
|
2378
|
-
return this.
|
|
2477
|
+
return this.typeMismatch('fieldType');
|
|
2379
2478
|
}
|
|
2380
2479
|
} else if (identifier.fieldType || otherIdentifier.fieldType) {
|
|
2381
2480
|
// Lenient mode: if either has no type, allow structural matching
|
|
2382
2481
|
if (!this.lenientTypeMatching) {
|
|
2383
2482
|
// Strict mode: if only one has a type, they don't match
|
|
2384
|
-
return this.
|
|
2483
|
+
return this.typeMismatch('fieldType');
|
|
2385
2484
|
}
|
|
2386
2485
|
}
|
|
2387
2486
|
|
|
@@ -2397,29 +2496,29 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2397
2496
|
const otherVariableDeclarations = other as J.VariableDeclarations;
|
|
2398
2497
|
|
|
2399
2498
|
// Visit leading annotations
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2404
|
-
|
|
2405
|
-
await this.visit(
|
|
2406
|
-
|
|
2407
|
-
|
|
2499
|
+
await this.visitArrayProperty(
|
|
2500
|
+
variableDeclarations,
|
|
2501
|
+
'leadingAnnotations',
|
|
2502
|
+
variableDeclarations.leadingAnnotations,
|
|
2503
|
+
otherVariableDeclarations.leadingAnnotations,
|
|
2504
|
+
async (ann1, ann2) => { await this.visit(ann1, ann2); }
|
|
2505
|
+
);
|
|
2506
|
+
if (!this.match) return variableDeclarations;
|
|
2408
2507
|
|
|
2409
2508
|
// Visit modifiers
|
|
2410
|
-
|
|
2411
|
-
|
|
2412
|
-
|
|
2413
|
-
|
|
2414
|
-
|
|
2415
|
-
await this.visit(
|
|
2416
|
-
|
|
2417
|
-
|
|
2509
|
+
await this.visitArrayProperty(
|
|
2510
|
+
variableDeclarations,
|
|
2511
|
+
'modifiers',
|
|
2512
|
+
variableDeclarations.modifiers,
|
|
2513
|
+
otherVariableDeclarations.modifiers,
|
|
2514
|
+
async (mod1, mod2) => { await this.visit(mod1, mod2); }
|
|
2515
|
+
);
|
|
2516
|
+
if (!this.match) return variableDeclarations;
|
|
2418
2517
|
|
|
2419
2518
|
// Compare typeExpression - lenient matching allows one to be undefined
|
|
2420
2519
|
if ((variableDeclarations.typeExpression === undefined) !== (otherVariableDeclarations.typeExpression === undefined)) {
|
|
2421
2520
|
if (!this.lenientTypeMatching) {
|
|
2422
|
-
return this.
|
|
2521
|
+
return this.structuralMismatch('typeExpression');
|
|
2423
2522
|
}
|
|
2424
2523
|
// In lenient mode, skip type comparison and continue
|
|
2425
2524
|
} else if (variableDeclarations.typeExpression && otherVariableDeclarations.typeExpression) {
|
|
@@ -2430,19 +2529,18 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2430
2529
|
|
|
2431
2530
|
// Compare varargs
|
|
2432
2531
|
if ((variableDeclarations.varargs === undefined) !== (otherVariableDeclarations.varargs === undefined)) {
|
|
2433
|
-
return this.
|
|
2532
|
+
return this.structuralMismatch('varargs');
|
|
2434
2533
|
}
|
|
2435
2534
|
|
|
2436
2535
|
// Compare variables
|
|
2437
|
-
|
|
2438
|
-
|
|
2439
|
-
|
|
2440
|
-
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2445
|
-
}
|
|
2536
|
+
await this.visitArrayProperty(
|
|
2537
|
+
variableDeclarations,
|
|
2538
|
+
'variables',
|
|
2539
|
+
variableDeclarations.variables,
|
|
2540
|
+
otherVariableDeclarations.variables,
|
|
2541
|
+
async (var1, var2) => { await this.visitRightPadded(var1, var2 as any); }
|
|
2542
|
+
);
|
|
2543
|
+
if (!this.match) return variableDeclarations;
|
|
2446
2544
|
|
|
2447
2545
|
return variableDeclarations;
|
|
2448
2546
|
}
|
|
@@ -2456,28 +2554,28 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2456
2554
|
const otherMethodDeclaration = other as J.MethodDeclaration;
|
|
2457
2555
|
|
|
2458
2556
|
// Visit leading annotations
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2463
|
-
|
|
2464
|
-
await this.visit(
|
|
2465
|
-
|
|
2466
|
-
|
|
2557
|
+
await this.visitArrayProperty(
|
|
2558
|
+
methodDeclaration,
|
|
2559
|
+
'leadingAnnotations',
|
|
2560
|
+
methodDeclaration.leadingAnnotations,
|
|
2561
|
+
otherMethodDeclaration.leadingAnnotations,
|
|
2562
|
+
async (ann1, ann2) => { await this.visit(ann1, ann2); }
|
|
2563
|
+
);
|
|
2564
|
+
if (!this.match) return methodDeclaration;
|
|
2467
2565
|
|
|
2468
2566
|
// Visit modifiers
|
|
2469
|
-
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2473
|
-
|
|
2474
|
-
await this.visit(
|
|
2475
|
-
|
|
2476
|
-
|
|
2567
|
+
await this.visitArrayProperty(
|
|
2568
|
+
methodDeclaration,
|
|
2569
|
+
'modifiers',
|
|
2570
|
+
methodDeclaration.modifiers,
|
|
2571
|
+
otherMethodDeclaration.modifiers,
|
|
2572
|
+
async (mod1, mod2) => { await this.visit(mod1, mod2); }
|
|
2573
|
+
);
|
|
2574
|
+
if (!this.match) return methodDeclaration;
|
|
2477
2575
|
|
|
2478
2576
|
// Visit type parameters if present
|
|
2479
2577
|
if (!!methodDeclaration.typeParameters !== !!otherMethodDeclaration.typeParameters) {
|
|
2480
|
-
return this.
|
|
2578
|
+
return this.structuralMismatch('typeParameters');
|
|
2481
2579
|
}
|
|
2482
2580
|
|
|
2483
2581
|
if (methodDeclaration.typeParameters && otherMethodDeclaration.typeParameters) {
|
|
@@ -2488,7 +2586,7 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2488
2586
|
// Compare returnTypeExpression - lenient matching allows one to be undefined
|
|
2489
2587
|
if ((methodDeclaration.returnTypeExpression === undefined) !== (otherMethodDeclaration.returnTypeExpression === undefined)) {
|
|
2490
2588
|
if (!this.lenientTypeMatching) {
|
|
2491
|
-
return this.
|
|
2589
|
+
return this.typeMismatch('returnTypeExpression');
|
|
2492
2590
|
}
|
|
2493
2591
|
// In lenient mode, skip type comparison and continue
|
|
2494
2592
|
} else if (methodDeclaration.returnTypeExpression && otherMethodDeclaration.returnTypeExpression) {
|
|
@@ -2502,36 +2600,22 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2502
2600
|
if (!this.match) return methodDeclaration;
|
|
2503
2601
|
|
|
2504
2602
|
// Compare parameters
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
}
|
|
2508
|
-
|
|
2509
|
-
// Visit each parameter in lock step
|
|
2510
|
-
for (let i = 0; i < methodDeclaration.parameters.elements.length; i++) {
|
|
2511
|
-
await this.visitRightPadded(methodDeclaration.parameters.elements[i], otherMethodDeclaration.parameters.elements[i] as any);
|
|
2512
|
-
if (!this.match) return methodDeclaration;
|
|
2513
|
-
}
|
|
2603
|
+
await this.visitContainer(methodDeclaration.parameters, otherMethodDeclaration.parameters as any);
|
|
2604
|
+
if (!this.match) return methodDeclaration;
|
|
2514
2605
|
|
|
2515
2606
|
// Visit throws if present
|
|
2516
2607
|
if (!!methodDeclaration.throws !== !!otherMethodDeclaration.throws) {
|
|
2517
|
-
return this.
|
|
2608
|
+
return this.structuralMismatch('throws');
|
|
2518
2609
|
}
|
|
2519
2610
|
|
|
2520
2611
|
if (methodDeclaration.throws && otherMethodDeclaration.throws) {
|
|
2521
|
-
|
|
2522
|
-
if (
|
|
2523
|
-
return this.abort(methodDeclaration);
|
|
2524
|
-
}
|
|
2525
|
-
|
|
2526
|
-
for (let i = 0; i < methodDeclaration.throws.elements.length; i++) {
|
|
2527
|
-
await this.visitRightPadded(methodDeclaration.throws.elements[i], otherMethodDeclaration.throws.elements[i] as any);
|
|
2528
|
-
if (!this.match) return methodDeclaration;
|
|
2529
|
-
}
|
|
2612
|
+
await this.visitContainer(methodDeclaration.throws, otherMethodDeclaration.throws as any);
|
|
2613
|
+
if (!this.match) return methodDeclaration;
|
|
2530
2614
|
}
|
|
2531
2615
|
|
|
2532
2616
|
// Visit body if present
|
|
2533
2617
|
if (!!methodDeclaration.body !== !!otherMethodDeclaration.body) {
|
|
2534
|
-
return this.
|
|
2618
|
+
return this.structuralMismatch('body');
|
|
2535
2619
|
}
|
|
2536
2620
|
|
|
2537
2621
|
if (methodDeclaration.body && otherMethodDeclaration.body) {
|
|
@@ -2553,9 +2637,7 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2553
2637
|
* - `void 1` matches `undefined`
|
|
2554
2638
|
*/
|
|
2555
2639
|
override async visitVoid(voidExpr: JS.Void, other: J): Promise<J | undefined> {
|
|
2556
|
-
if (!this.match)
|
|
2557
|
-
return voidExpr;
|
|
2558
|
-
}
|
|
2640
|
+
if (!this.match) return voidExpr;
|
|
2559
2641
|
|
|
2560
2642
|
// Check if the other is an undefined identifier
|
|
2561
2643
|
if ((other as any).kind === J.Kind.Identifier) {
|
|
@@ -2581,9 +2663,7 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2581
2663
|
* - `1000` matches `1e3`
|
|
2582
2664
|
*/
|
|
2583
2665
|
override async visitLiteral(literal: J.Literal, other: J): Promise<J | undefined> {
|
|
2584
|
-
if (!this.match)
|
|
2585
|
-
return literal;
|
|
2586
|
-
}
|
|
2666
|
+
if (!this.match) return literal;
|
|
2587
2667
|
|
|
2588
2668
|
if ((other as any).kind !== J.Kind.Literal) {
|
|
2589
2669
|
return await super.visitLiteral(literal, other);
|
|
@@ -2592,10 +2672,10 @@ export class JavaScriptSemanticComparatorVisitor extends JavaScriptComparatorVis
|
|
|
2592
2672
|
const otherLiteral = other as J.Literal;
|
|
2593
2673
|
|
|
2594
2674
|
// Only compare value and type, ignoring valueSource (text representation) and unicodeEscapes
|
|
2595
|
-
await this.visitProperty(literal.value, otherLiteral.value);
|
|
2675
|
+
await this.visitProperty(literal.value, otherLiteral.value, 'value');
|
|
2596
2676
|
if (!this.match) return literal;
|
|
2597
2677
|
|
|
2598
|
-
await this.visitProperty(literal.type, otherLiteral.type);
|
|
2678
|
+
await this.visitProperty(literal.type, otherLiteral.type, 'type');
|
|
2599
2679
|
if (!this.match) return literal;
|
|
2600
2680
|
|
|
2601
2681
|
return literal;
|