@openrewrite/rewrite 8.66.0-20251031-152250 → 8.66.0-20251031-160320

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.
@@ -17,9 +17,9 @@ import {JS} from '.';
17
17
  import {JavaScriptParser} from './parser';
18
18
  import {JavaScriptVisitor} from './visitor';
19
19
  import {Cursor, isTree, Tree} from '..';
20
- import {J, Type} from '../java';
20
+ import {J} from '../java';
21
21
  import {produce} from "immer";
22
- import {JavaScriptComparatorVisitor} from "./comparator";
22
+ import {JavaScriptSemanticComparatorVisitor} from "./comparator";
23
23
  import {DependencyWorkspace} from './dependency-workspace';
24
24
  import {Marker} from '../markers';
25
25
  import {randomId} from '../uuid';
@@ -264,150 +264,6 @@ export class MatchResult implements Pick<Map<string, J>, "get"> {
264
264
  }
265
265
  }
266
266
 
267
- /**
268
- * A comparator visitor that checks semantic equality including type attribution.
269
- * This ensures that patterns only match code with compatible types, not just
270
- * structurally similar code.
271
- */
272
- class JavaScriptTemplateSemanticallyEqualVisitor extends JavaScriptComparatorVisitor {
273
- /**
274
- * Checks if two types are semantically equal.
275
- * For method types, this checks that the declaring type and method name match.
276
- */
277
- private isOfType(target?: Type, source?: Type): boolean {
278
- if (!target || !source) {
279
- return target === source;
280
- }
281
-
282
- if (target.kind !== source.kind) {
283
- return false;
284
- }
285
-
286
- // For method types, check declaring type
287
- // Note: We don't check the name field because it might not be fully resolved in patterns
288
- // The method invocation visitor already checks that simple names match
289
- if (target.kind === Type.Kind.Method && source.kind === Type.Kind.Method) {
290
- const targetMethod = target as Type.Method;
291
- const sourceMethod = source as Type.Method;
292
-
293
- // Only check that declaring types match
294
- return this.isOfType(targetMethod.declaringType, sourceMethod.declaringType);
295
- }
296
-
297
- // For fully qualified types, check the fully qualified name
298
- if (Type.isFullyQualified(target) && Type.isFullyQualified(source)) {
299
- return Type.FullyQualified.getFullyQualifiedName(target) ===
300
- Type.FullyQualified.getFullyQualifiedName(source);
301
- }
302
-
303
- // Default: types are equal if they're the same kind
304
- return true;
305
- }
306
-
307
- /**
308
- * Override method invocation comparison to include type attribution checking.
309
- * When types match semantically, we allow matching even if one has a receiver
310
- * and the other doesn't (e.g., `isDate(x)` vs `util.isDate(x)`).
311
- */
312
- override async visitMethodInvocation(method: J.MethodInvocation, other: J): Promise<J | undefined> {
313
- if (other.kind !== J.Kind.MethodInvocation) {
314
- return method;
315
- }
316
-
317
- const otherMethod = other as J.MethodInvocation;
318
-
319
- // Check basic structural equality first
320
- if (method.name.simpleName !== otherMethod.name.simpleName ||
321
- method.arguments.elements.length !== otherMethod.arguments.elements.length) {
322
- this.abort();
323
- return method;
324
- }
325
-
326
- // Check type attribution
327
- // Both must have method types for semantic equality
328
- if (!method.methodType || !otherMethod.methodType) {
329
- // If template has type but target doesn't, they don't match
330
- if (method.methodType || otherMethod.methodType) {
331
- this.abort();
332
- return method;
333
- }
334
- // If neither has type, fall through to structural comparison
335
- return super.visitMethodInvocation(method, other);
336
- }
337
-
338
- // Both have types - check they match semantically
339
- const typesMatch = this.isOfType(method.methodType, otherMethod.methodType);
340
- if (!typesMatch) {
341
- // Types don't match - abort comparison
342
- this.abort();
343
- return method;
344
- }
345
-
346
- // Types match! Now we can ignore receiver differences and just compare arguments.
347
- // This allows pattern `isDate(x)` to match both `isDate(x)` and `util.isDate(x)`
348
- // when they have the same type attribution.
349
-
350
- // Compare type parameters
351
- if ((method.typeParameters === undefined) !== (otherMethod.typeParameters === undefined)) {
352
- this.abort();
353
- return method;
354
- }
355
-
356
- if (method.typeParameters && otherMethod.typeParameters) {
357
- if (method.typeParameters.elements.length !== otherMethod.typeParameters.elements.length) {
358
- this.abort();
359
- return method;
360
- }
361
- for (let i = 0; i < method.typeParameters.elements.length; i++) {
362
- await this.visit(method.typeParameters.elements[i].element, otherMethod.typeParameters.elements[i].element);
363
- if (!this.match) return method;
364
- }
365
- }
366
-
367
- // Compare name (already checked simpleName above, but visit for markers/prefix)
368
- await this.visit(method.name, otherMethod.name);
369
- if (!this.match) return method;
370
-
371
- // Compare arguments
372
- for (let i = 0; i < method.arguments.elements.length; i++) {
373
- await this.visit(method.arguments.elements[i].element, otherMethod.arguments.elements[i].element);
374
- if (!this.match) return method;
375
- }
376
-
377
- return method;
378
- }
379
-
380
- /**
381
- * Override identifier comparison to include type checking for field access.
382
- */
383
- override async visitIdentifier(identifier: J.Identifier, other: J): Promise<J | undefined> {
384
- if (other.kind !== J.Kind.Identifier) {
385
- return identifier;
386
- }
387
-
388
- const otherIdentifier = other as J.Identifier;
389
-
390
- // Check name matches
391
- if (identifier.simpleName !== otherIdentifier.simpleName) {
392
- return identifier;
393
- }
394
-
395
- // For identifiers with field types, check type attribution
396
- if (identifier.fieldType && otherIdentifier.fieldType) {
397
- if (!this.isOfType(identifier.fieldType, otherIdentifier.fieldType)) {
398
- this.abort();
399
- return identifier;
400
- }
401
- } else if (identifier.fieldType || otherIdentifier.fieldType) {
402
- // If only one has a type, they don't match
403
- this.abort();
404
- return identifier;
405
- }
406
-
407
- return super.visitIdentifier(identifier, other);
408
- }
409
- }
410
-
411
267
  /**
412
268
  * Matcher for checking if a pattern matches an AST node and extracting captured nodes.
413
269
  */
@@ -474,7 +330,7 @@ class Matcher {
474
330
  }
475
331
 
476
332
  const matcher = this;
477
- return await ((new class extends JavaScriptTemplateSemanticallyEqualVisitor {
333
+ return await ((new class extends JavaScriptSemanticComparatorVisitor {
478
334
  protected hasSameKind(j: J, other: J): boolean {
479
335
  return super.hasSameKind(j, other) || j.kind == J.Kind.Identifier && this.matchesParameter(j as J.Identifier, other);
480
336
  }