@openrewrite/rewrite 8.66.0 → 8.66.2

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 (113) hide show
  1. package/dist/java/tree.d.ts +10 -1
  2. package/dist/java/tree.d.ts.map +1 -1
  3. package/dist/java/tree.js +21 -5
  4. package/dist/java/tree.js.map +1 -1
  5. package/dist/java/type-visitor.d.ts +1 -1
  6. package/dist/java/type-visitor.d.ts.map +1 -1
  7. package/dist/java/visitor.d.ts +2 -2
  8. package/dist/java/visitor.d.ts.map +1 -1
  9. package/dist/java/visitor.js +8 -2
  10. package/dist/java/visitor.js.map +1 -1
  11. package/dist/javascript/assertions.d.ts +6 -0
  12. package/dist/javascript/assertions.d.ts.map +1 -1
  13. package/dist/javascript/assertions.js +14 -6
  14. package/dist/javascript/assertions.js.map +1 -1
  15. package/dist/javascript/comparator.d.ts +217 -7
  16. package/dist/javascript/comparator.d.ts.map +1 -1
  17. package/dist/javascript/comparator.js +1020 -2848
  18. package/dist/javascript/comparator.js.map +1 -1
  19. package/dist/javascript/format.d.ts +5 -3
  20. package/dist/javascript/format.d.ts.map +1 -1
  21. package/dist/javascript/format.js +87 -44
  22. package/dist/javascript/format.js.map +1 -1
  23. package/dist/javascript/index.d.ts +2 -1
  24. package/dist/javascript/index.d.ts.map +1 -1
  25. package/dist/javascript/index.js +2 -1
  26. package/dist/javascript/index.js.map +1 -1
  27. package/dist/javascript/parser.d.ts +2 -1
  28. package/dist/javascript/parser.d.ts.map +1 -1
  29. package/dist/javascript/parser.js +54 -43
  30. package/dist/javascript/parser.js.map +1 -1
  31. package/dist/javascript/templating/capture.d.ts +293 -0
  32. package/dist/javascript/templating/capture.d.ts.map +1 -0
  33. package/dist/javascript/templating/capture.js +461 -0
  34. package/dist/javascript/templating/capture.js.map +1 -0
  35. package/dist/javascript/templating/comparator.d.ts +171 -0
  36. package/dist/javascript/templating/comparator.d.ts.map +1 -0
  37. package/dist/javascript/templating/comparator.js +1221 -0
  38. package/dist/javascript/templating/comparator.js.map +1 -0
  39. package/dist/javascript/templating/engine.d.ts +108 -0
  40. package/dist/javascript/templating/engine.d.ts.map +1 -0
  41. package/dist/javascript/templating/engine.js +661 -0
  42. package/dist/javascript/templating/engine.js.map +1 -0
  43. package/dist/javascript/templating/index.d.ts +6 -0
  44. package/dist/javascript/templating/index.d.ts.map +1 -0
  45. package/dist/javascript/templating/index.js +44 -0
  46. package/dist/javascript/templating/index.js.map +1 -0
  47. package/dist/javascript/templating/pattern.d.ts +276 -0
  48. package/dist/javascript/templating/pattern.d.ts.map +1 -0
  49. package/dist/javascript/templating/pattern.js +952 -0
  50. package/dist/javascript/templating/pattern.js.map +1 -0
  51. package/dist/javascript/templating/placeholder-replacement.d.ts +83 -0
  52. package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -0
  53. package/dist/javascript/templating/placeholder-replacement.js +467 -0
  54. package/dist/javascript/templating/placeholder-replacement.js.map +1 -0
  55. package/dist/javascript/templating/rewrite.d.ts +84 -0
  56. package/dist/javascript/templating/rewrite.d.ts.map +1 -0
  57. package/dist/javascript/templating/rewrite.js +208 -0
  58. package/dist/javascript/templating/rewrite.js.map +1 -0
  59. package/dist/javascript/templating/template.d.ts +230 -0
  60. package/dist/javascript/templating/template.d.ts.map +1 -0
  61. package/dist/javascript/templating/template.js +367 -0
  62. package/dist/javascript/templating/template.js.map +1 -0
  63. package/dist/javascript/templating/types.d.ts +610 -0
  64. package/dist/javascript/templating/types.d.ts.map +1 -0
  65. package/dist/javascript/templating/types.js +3 -0
  66. package/dist/javascript/templating/types.js.map +1 -0
  67. package/dist/javascript/templating/utils.d.ts +135 -0
  68. package/dist/javascript/templating/utils.d.ts.map +1 -0
  69. package/dist/javascript/templating/utils.js +251 -0
  70. package/dist/javascript/templating/utils.js.map +1 -0
  71. package/dist/javascript/type-mapping.d.ts.map +1 -1
  72. package/dist/javascript/type-mapping.js +21 -11
  73. package/dist/javascript/type-mapping.js.map +1 -1
  74. package/dist/json/rpc.js +2 -2
  75. package/dist/json/rpc.js.map +1 -1
  76. package/dist/recipe/order-imports.js.map +1 -1
  77. package/dist/test/rewrite-test.d.ts.map +1 -1
  78. package/dist/test/rewrite-test.js +10 -6
  79. package/dist/test/rewrite-test.js.map +1 -1
  80. package/dist/version.txt +1 -1
  81. package/dist/visitor.d.ts +4 -4
  82. package/dist/visitor.d.ts.map +1 -1
  83. package/dist/visitor.js +8 -3
  84. package/dist/visitor.js.map +1 -1
  85. package/package.json +5 -2
  86. package/src/java/tree.ts +10 -3
  87. package/src/java/type-visitor.ts +1 -1
  88. package/src/java/visitor.ts +11 -5
  89. package/src/javascript/assertions.ts +9 -3
  90. package/src/javascript/comparator.ts +1095 -3373
  91. package/src/javascript/format.ts +72 -33
  92. package/src/javascript/index.ts +2 -1
  93. package/src/javascript/parser.ts +67 -45
  94. package/src/javascript/templating/capture.ts +595 -0
  95. package/src/javascript/templating/comparator.ts +1383 -0
  96. package/src/javascript/templating/engine.ts +750 -0
  97. package/src/javascript/templating/index.ts +67 -0
  98. package/src/javascript/templating/pattern.ts +1101 -0
  99. package/src/javascript/templating/placeholder-replacement.ts +475 -0
  100. package/src/javascript/templating/rewrite.ts +229 -0
  101. package/src/javascript/templating/template.ts +414 -0
  102. package/src/javascript/templating/types.ts +674 -0
  103. package/src/javascript/templating/utils.ts +298 -0
  104. package/src/javascript/type-mapping.ts +20 -11
  105. package/src/json/rpc.ts +2 -2
  106. package/src/recipe/order-imports.ts +1 -1
  107. package/src/test/rewrite-test.ts +12 -7
  108. package/src/visitor.ts +14 -6
  109. package/dist/javascript/templating.d.ts +0 -265
  110. package/dist/javascript/templating.d.ts.map +0 -1
  111. package/dist/javascript/templating.js +0 -1027
  112. package/dist/javascript/templating.js.map +0 -1
  113. package/src/javascript/templating.ts +0 -1226
@@ -0,0 +1,1221 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.DebugPatternMatchingComparator = exports.PatternMatchingComparator = void 0;
13
+ /*
14
+ * Copyright 2025 the original author or authors.
15
+ * <p>
16
+ * Licensed under the Moderne Source Available License (the "License");
17
+ * you may not use this file except in compliance with the License.
18
+ * You may obtain a copy of the License at
19
+ * <p>
20
+ * https://docs.moderne.io/licensing/moderne-source-available-license
21
+ * <p>
22
+ * Unless required by applicable law or agreed to in writing, software
23
+ * distributed under the License is distributed on an "AS IS" BASIS,
24
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
25
+ * See the License for the specific language governing permissions and
26
+ * limitations under the License.
27
+ */
28
+ const __1 = require("../..");
29
+ const java_1 = require("../../java");
30
+ const index_1 = require("../index");
31
+ const comparator_1 = require("../comparator");
32
+ const utils_1 = require("./utils");
33
+ /**
34
+ * A comparator for pattern matching that is lenient about optional properties.
35
+ * Allows patterns without type annotations to match actual code with type annotations.
36
+ * Uses semantic comparison to match semantically equivalent code (e.g., isDate() and util.isDate()).
37
+ */
38
+ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparatorVisitor {
39
+ constructor(matcher, lenientTypeMatching = true) {
40
+ // Enable lenient type matching based on pattern configuration (default: true for backward compatibility)
41
+ super(lenientTypeMatching);
42
+ this.matcher = matcher;
43
+ }
44
+ visit(j, p, parent) {
45
+ const _super = Object.create(null, {
46
+ visit: { get: () => super.visit }
47
+ });
48
+ return __awaiter(this, void 0, void 0, function* () {
49
+ // Check if the pattern node is a capture - this handles unwrapped captures
50
+ // (Wrapped captures in J.RightPadded are handled by visitRightPadded override)
51
+ // Note: targetCursor will be pushed by parent's visit() method after this check
52
+ const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(j);
53
+ if (captureMarker) {
54
+ // Push targetCursor to position it at the captured node for constraint evaluation
55
+ // Only create cursor if targetCursor was initialized (meaning user provided one)
56
+ const savedTargetCursor = this.targetCursor;
57
+ const cursorAtCapturedNode = this.targetCursor !== undefined
58
+ ? new __1.Cursor(p, this.targetCursor)
59
+ : new __1.Cursor(p);
60
+ this.targetCursor = cursorAtCapturedNode;
61
+ try {
62
+ // Evaluate constraint with cursor at the captured node (always defined)
63
+ // Skip constraint for variadic captures - they're evaluated in matchSequence with the full array
64
+ if (captureMarker.constraint && !captureMarker.variadicOptions && !captureMarker.constraint(p, cursorAtCapturedNode)) {
65
+ const captureName = captureMarker.captureName || 'unnamed';
66
+ const targetKind = p.kind || 'unknown';
67
+ return this.constraintFailed(captureName, targetKind);
68
+ }
69
+ const success = this.matcher.handleCapture(captureMarker, p, undefined);
70
+ if (!success) {
71
+ const captureName = captureMarker.captureName || 'unnamed';
72
+ return this.captureConflict(captureName);
73
+ }
74
+ return j;
75
+ }
76
+ finally {
77
+ this.targetCursor = savedTargetCursor;
78
+ }
79
+ }
80
+ if (!this.match) {
81
+ return j;
82
+ }
83
+ // Continue with parent's visit which will push targetCursor and traverse
84
+ return yield _super.visit.call(this, j, p, parent);
85
+ });
86
+ }
87
+ hasSameKind(j, other) {
88
+ return super.hasSameKind(j, other) ||
89
+ (j.kind == java_1.J.Kind.Identifier && utils_1.PlaceholderUtils.isCapture(j));
90
+ }
91
+ /**
92
+ * Additional specialized abort methods for pattern matching scenarios.
93
+ */
94
+ constraintFailed(captureName, targetKind) {
95
+ var _a;
96
+ const pattern = (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.value;
97
+ return this.abort(pattern, 'constraint-failed', `capture[${captureName}]`, 'constraint satisfied', `constraint failed for ${targetKind}`);
98
+ }
99
+ captureConflict(captureName) {
100
+ var _a;
101
+ const pattern = (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.value;
102
+ return this.abort(pattern, 'capture-conflict', `capture[${captureName}]`, 'compatible binding', 'conflicting binding');
103
+ }
104
+ /**
105
+ * Override visitRightPadded to check if this wrapper has a CaptureMarker.
106
+ * If so, capture the entire wrapper (to preserve markers like semicolons).
107
+ */
108
+ visitRightPadded(right, p) {
109
+ const _super = Object.create(null, {
110
+ visitRightPadded: { get: () => super.visitRightPadded }
111
+ });
112
+ return __awaiter(this, void 0, void 0, function* () {
113
+ if (!this.match) {
114
+ return right;
115
+ }
116
+ // Check if this RightPadded has a CaptureMarker (attached during pattern construction)
117
+ // Note: Markers are now only at the wrapper level, not at the element level
118
+ const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(right);
119
+ if (captureMarker) {
120
+ // Extract the target wrapper if it's also a RightPadded
121
+ const isRightPadded = p.kind === java_1.J.Kind.RightPadded;
122
+ const targetWrapper = isRightPadded ? p : undefined;
123
+ const targetElement = isRightPadded ? targetWrapper.element : p;
124
+ // Push targetCursor to position it at the captured element for constraint evaluation
125
+ const savedTargetCursor = this.targetCursor;
126
+ const cursorAtCapturedNode = this.targetCursor !== undefined
127
+ ? (targetWrapper ? new __1.Cursor(targetWrapper, this.targetCursor) : new __1.Cursor(targetElement, this.targetCursor))
128
+ : (targetWrapper ? new __1.Cursor(targetWrapper) : new __1.Cursor(targetElement));
129
+ this.targetCursor = cursorAtCapturedNode;
130
+ try {
131
+ // Evaluate constraint with cursor at the captured node (always defined)
132
+ // Skip constraint for variadic captures - they're evaluated in matchSequence with the full array
133
+ if (captureMarker.constraint && !captureMarker.variadicOptions && !captureMarker.constraint(targetElement, cursorAtCapturedNode)) {
134
+ const captureName = captureMarker.captureName || 'unnamed';
135
+ const targetKind = targetElement.kind || 'unknown';
136
+ return this.constraintFailed(captureName, targetKind);
137
+ }
138
+ // Handle the capture with the wrapper - use the element for pattern matching
139
+ const success = this.matcher.handleCapture(captureMarker, targetElement, targetWrapper);
140
+ if (!success) {
141
+ const captureName = captureMarker.captureName || 'unnamed';
142
+ return this.captureConflict(captureName);
143
+ }
144
+ return right;
145
+ }
146
+ finally {
147
+ this.targetCursor = savedTargetCursor;
148
+ }
149
+ }
150
+ // Not a capture wrapper - use parent implementation
151
+ return _super.visitRightPadded.call(this, right, p);
152
+ });
153
+ }
154
+ visitContainer(container, p) {
155
+ const _super = Object.create(null, {
156
+ visitContainer: { get: () => super.visitContainer }
157
+ });
158
+ return __awaiter(this, void 0, void 0, function* () {
159
+ // Check if any elements are variadic captures
160
+ const hasVariadicCapture = container.elements.some(elem => utils_1.PlaceholderUtils.isVariadicCapture(elem));
161
+ // If no variadic captures, use parent implementation
162
+ if (!hasVariadicCapture) {
163
+ return _super.visitContainer.call(this, container, p);
164
+ }
165
+ // Otherwise, handle variadic captures ourselves
166
+ if (!this.match) {
167
+ return container;
168
+ }
169
+ // Extract the other container
170
+ const isContainer = p.kind === java_1.J.Kind.Container;
171
+ if (!isContainer) {
172
+ // Set up cursors temporarily for kindMismatch to use
173
+ const savedCursor = this.cursor;
174
+ const savedTargetCursor = this.targetCursor;
175
+ this.cursor = new __1.Cursor(container, this.cursor);
176
+ this.targetCursor = new __1.Cursor(p, this.targetCursor);
177
+ try {
178
+ return this.kindMismatch();
179
+ }
180
+ finally {
181
+ this.cursor = savedCursor;
182
+ this.targetCursor = savedTargetCursor;
183
+ }
184
+ }
185
+ const otherContainer = p;
186
+ // Push wrappers onto both cursors
187
+ const savedCursor = this.cursor;
188
+ const savedTargetCursor = this.targetCursor;
189
+ this.cursor = new __1.Cursor(container, this.cursor);
190
+ this.targetCursor = new __1.Cursor(otherContainer, this.targetCursor);
191
+ try {
192
+ // Use matchSequence for variadic matching
193
+ // filterEmpty=true to skip J.Empty elements (they represent missing elements in destructuring)
194
+ if (!(yield this.matchSequence(container.elements, otherContainer.elements, true))) {
195
+ return this.structuralMismatch('elements');
196
+ }
197
+ }
198
+ finally {
199
+ this.cursor = savedCursor;
200
+ this.targetCursor = savedTargetCursor;
201
+ }
202
+ return container;
203
+ });
204
+ }
205
+ /**
206
+ * Visit a single element in a container (for non-variadic matching).
207
+ * Extracted to allow debug subclass to add path tracking.
208
+ *
209
+ * @param element The pattern element
210
+ * @param otherElement The target element
211
+ * @param index The index in the container
212
+ * @returns true if matching should continue, false if it failed
213
+ */
214
+ visitContainerElement(element, otherElement, index) {
215
+ return __awaiter(this, void 0, void 0, function* () {
216
+ yield this.visitRightPadded(element, otherElement);
217
+ return this.match;
218
+ });
219
+ }
220
+ visitMethodInvocation(methodInvocation, other) {
221
+ const _super = Object.create(null, {
222
+ visitMethodInvocation: { get: () => super.visitMethodInvocation }
223
+ });
224
+ return __awaiter(this, void 0, void 0, function* () {
225
+ // Check if any arguments are variadic captures
226
+ const hasVariadicCapture = methodInvocation.arguments.elements.some(arg => utils_1.PlaceholderUtils.isVariadicCapture(arg));
227
+ // If no variadic captures, use parent implementation (which includes semantic/type-aware matching)
228
+ if (!hasVariadicCapture) {
229
+ return _super.visitMethodInvocation.call(this, methodInvocation, other);
230
+ }
231
+ // Otherwise, handle variadic captures ourselves
232
+ if (!this.match) {
233
+ return this.abort(methodInvocation);
234
+ }
235
+ if (other.kind !== java_1.J.Kind.MethodInvocation) {
236
+ // Set up cursors for kindMismatch
237
+ const savedCursor = this.cursor;
238
+ const savedTargetCursor = this.targetCursor;
239
+ this.cursor = new __1.Cursor(methodInvocation, this.cursor);
240
+ this.targetCursor = new __1.Cursor(other, this.targetCursor);
241
+ try {
242
+ return this.kindMismatch();
243
+ }
244
+ finally {
245
+ this.cursor = savedCursor;
246
+ this.targetCursor = savedTargetCursor;
247
+ }
248
+ }
249
+ const otherMethodInvocation = other;
250
+ // Set up cursors for the entire method
251
+ const savedCursor = this.cursor;
252
+ const savedTargetCursor = this.targetCursor;
253
+ this.cursor = new __1.Cursor(methodInvocation, this.cursor);
254
+ this.targetCursor = new __1.Cursor(otherMethodInvocation, this.targetCursor);
255
+ try {
256
+ // Compare select
257
+ if ((methodInvocation.select === undefined) !== (otherMethodInvocation.select === undefined)) {
258
+ return this.structuralMismatch('select');
259
+ }
260
+ // Visit select if present
261
+ if (methodInvocation.select && otherMethodInvocation.select) {
262
+ yield this.visit(methodInvocation.select.element, otherMethodInvocation.select.element);
263
+ if (!this.match)
264
+ return methodInvocation;
265
+ }
266
+ // Compare typeParameters
267
+ if ((methodInvocation.typeParameters === undefined) !== (otherMethodInvocation.typeParameters === undefined)) {
268
+ return this.structuralMismatch('typeParameters');
269
+ }
270
+ // Visit typeParameters if present
271
+ if (methodInvocation.typeParameters && otherMethodInvocation.typeParameters) {
272
+ if (methodInvocation.typeParameters.elements.length !== otherMethodInvocation.typeParameters.elements.length) {
273
+ return this.arrayLengthMismatch('typeParameters.elements');
274
+ }
275
+ // Visit each type parameter in lock step (visit RightPadded to check for markers)
276
+ for (let i = 0; i < methodInvocation.typeParameters.elements.length; i++) {
277
+ yield this.visitRightPadded(methodInvocation.typeParameters.elements[i], otherMethodInvocation.typeParameters.elements[i]);
278
+ if (!this.match)
279
+ return methodInvocation;
280
+ }
281
+ }
282
+ // Visit name
283
+ yield this.visit(methodInvocation.name, otherMethodInvocation.name);
284
+ if (!this.match) {
285
+ return methodInvocation;
286
+ }
287
+ // Special handling for variadic captures in arguments
288
+ if (!(yield this.matchArguments(methodInvocation.arguments.elements, otherMethodInvocation.arguments.elements))) {
289
+ return this.structuralMismatch('arguments');
290
+ }
291
+ return methodInvocation;
292
+ }
293
+ finally {
294
+ this.cursor = savedCursor;
295
+ this.targetCursor = savedTargetCursor;
296
+ }
297
+ });
298
+ }
299
+ visitBlock(block, other) {
300
+ const _super = Object.create(null, {
301
+ visitBlock: { get: () => super.visitBlock }
302
+ });
303
+ return __awaiter(this, void 0, void 0, function* () {
304
+ // Check if any statements have CaptureMarker indicating they're variadic
305
+ const hasVariadicCapture = block.statements.some(stmt => {
306
+ const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(stmt);
307
+ return (captureMarker === null || captureMarker === void 0 ? void 0 : captureMarker.variadicOptions) !== undefined;
308
+ });
309
+ // If no variadic captures, use parent implementation
310
+ if (!hasVariadicCapture) {
311
+ return _super.visitBlock.call(this, block, other);
312
+ }
313
+ // Otherwise, handle variadic captures ourselves
314
+ if (!this.match) {
315
+ return this.abort(block);
316
+ }
317
+ if (other.kind !== java_1.J.Kind.Block) {
318
+ // Set up cursors for kindMismatch
319
+ const savedCursor = this.cursor;
320
+ const savedTargetCursor = this.targetCursor;
321
+ this.cursor = new __1.Cursor(block, this.cursor);
322
+ this.targetCursor = new __1.Cursor(other, this.targetCursor);
323
+ try {
324
+ return this.kindMismatch();
325
+ }
326
+ finally {
327
+ this.cursor = savedCursor;
328
+ this.targetCursor = savedTargetCursor;
329
+ }
330
+ }
331
+ const otherBlock = other;
332
+ // Set up cursors for structural comparison
333
+ const savedCursor = this.cursor;
334
+ const savedTargetCursor = this.targetCursor;
335
+ this.cursor = new __1.Cursor(block, this.cursor);
336
+ this.targetCursor = new __1.Cursor(otherBlock, this.targetCursor);
337
+ try {
338
+ // Special handling for variadic captures in statements
339
+ if (!(yield this.matchSequence(block.statements, otherBlock.statements, false))) {
340
+ return this.structuralMismatch('statements');
341
+ }
342
+ return block;
343
+ }
344
+ finally {
345
+ this.cursor = savedCursor;
346
+ this.targetCursor = savedTargetCursor;
347
+ }
348
+ });
349
+ }
350
+ visitJsCompilationUnit(compilationUnit, other) {
351
+ const _super = Object.create(null, {
352
+ visitJsCompilationUnit: { get: () => super.visitJsCompilationUnit }
353
+ });
354
+ return __awaiter(this, void 0, void 0, function* () {
355
+ // Check if any statements are variadic captures
356
+ const hasVariadicCapture = compilationUnit.statements.some(stmt => {
357
+ return utils_1.PlaceholderUtils.isVariadicCapture(stmt);
358
+ });
359
+ // If no variadic captures, use parent implementation
360
+ if (!hasVariadicCapture) {
361
+ return _super.visitJsCompilationUnit.call(this, compilationUnit, other);
362
+ }
363
+ // Otherwise, handle variadic captures ourselves
364
+ if (!this.match) {
365
+ return this.abort(compilationUnit);
366
+ }
367
+ if (other.kind !== index_1.JS.Kind.CompilationUnit) {
368
+ // Set up cursors for kindMismatch
369
+ const savedCursor = this.cursor;
370
+ const savedTargetCursor = this.targetCursor;
371
+ this.cursor = new __1.Cursor(compilationUnit, this.cursor);
372
+ this.targetCursor = new __1.Cursor(other, this.targetCursor);
373
+ try {
374
+ return this.kindMismatch();
375
+ }
376
+ finally {
377
+ this.cursor = savedCursor;
378
+ this.targetCursor = savedTargetCursor;
379
+ }
380
+ }
381
+ const otherCompilationUnit = other;
382
+ // Set up cursors for structural comparison
383
+ const savedCursor = this.cursor;
384
+ const savedTargetCursor = this.targetCursor;
385
+ this.cursor = new __1.Cursor(compilationUnit, this.cursor);
386
+ this.targetCursor = new __1.Cursor(otherCompilationUnit, this.targetCursor);
387
+ try {
388
+ // Special handling for variadic captures in top-level statements
389
+ if (!(yield this.matchSequence(compilationUnit.statements, otherCompilationUnit.statements, false))) {
390
+ return this.structuralMismatch('statements');
391
+ }
392
+ return compilationUnit;
393
+ }
394
+ finally {
395
+ this.cursor = savedCursor;
396
+ this.targetCursor = savedTargetCursor;
397
+ }
398
+ });
399
+ }
400
+ /**
401
+ * Matches argument lists, with special handling for variadic captures.
402
+ * A variadic capture can match zero or more consecutive arguments.
403
+ */
404
+ matchArguments(patternArgs, targetArgs) {
405
+ return __awaiter(this, void 0, void 0, function* () {
406
+ return yield this.matchSequence(patternArgs, targetArgs, true);
407
+ });
408
+ }
409
+ /**
410
+ * Generic sequence matching with variadic capture support.
411
+ * Works for any sequence of JRightPadded elements (arguments, statements, etc.).
412
+ * A variadic capture can match zero or more consecutive elements.
413
+ *
414
+ * Uses pivot detection to optimize matching, with backtracking as fallback.
415
+ *
416
+ * @param patternElements The pattern elements (JRightPadded)
417
+ * @param targetElements The target elements to match against (JRightPadded)
418
+ * @param filterEmpty Whether to filter out J.Empty elements when capturing (true for arguments, false for statements)
419
+ * @returns true if the sequence matches, false otherwise
420
+ */
421
+ matchSequence(patternElements, targetElements, filterEmpty) {
422
+ return __awaiter(this, void 0, void 0, function* () {
423
+ return yield this.matchSequenceOptimized(patternElements, targetElements, 0, 0, filterEmpty);
424
+ });
425
+ }
426
+ /**
427
+ * Optimized sequence matcher with pivot detection and backtracking.
428
+ * For variadic patterns, tries to detect pivots (where next pattern matches) to avoid
429
+ * unnecessary backtracking. Falls back to full backtracking when pivots are ambiguous.
430
+ *
431
+ * @param patternElements The pattern elements (JRightPadded)
432
+ * @param targetElements The target elements to match against (JRightPadded)
433
+ * @param patternIdx Current position in pattern
434
+ * @param targetIdx Current position in target
435
+ * @param filterEmpty Whether to filter out J.Empty elements when capturing
436
+ * @returns true if the remaining sequence matches, false otherwise
437
+ */
438
+ matchSequenceOptimized(patternElements, targetElements, patternIdx, targetIdx, filterEmpty) {
439
+ return __awaiter(this, void 0, void 0, function* () {
440
+ var _a, _b;
441
+ // Base case: all patterns matched
442
+ if (patternIdx >= patternElements.length) {
443
+ return targetIdx >= targetElements.length; // Success if all targets consumed
444
+ }
445
+ // Check for markers at wrapper level only (markers are now only at the outermost level)
446
+ const patternWrapper = patternElements[patternIdx];
447
+ const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(patternWrapper);
448
+ const isVariadic = (captureMarker === null || captureMarker === void 0 ? void 0 : captureMarker.variadicOptions) !== undefined;
449
+ if (isVariadic) {
450
+ // Variadic pattern: try different consumption amounts with backtracking
451
+ const variadicOptions = captureMarker.variadicOptions;
452
+ const min = (_a = variadicOptions === null || variadicOptions === void 0 ? void 0 : variadicOptions.min) !== null && _a !== void 0 ? _a : 0;
453
+ const max = (_b = variadicOptions === null || variadicOptions === void 0 ? void 0 : variadicOptions.max) !== null && _b !== void 0 ? _b : Infinity;
454
+ // Calculate maximum possible consumption and check if remaining patterns are deterministic
455
+ let nonVariadicRemainingPatterns = 0;
456
+ let allRemainingPatternsAreDeterministic = true;
457
+ for (let i = patternIdx + 1; i < patternElements.length; i++) {
458
+ const nextCaptureMarker = utils_1.PlaceholderUtils.getCaptureMarker(patternElements[i]);
459
+ const nextIsVariadic = (nextCaptureMarker === null || nextCaptureMarker === void 0 ? void 0 : nextCaptureMarker.variadicOptions) !== undefined;
460
+ if (!nextIsVariadic) {
461
+ nonVariadicRemainingPatterns++;
462
+ }
463
+ // A pattern is deterministic if it's not a capture at all (i.e., a literal/fixed structure)
464
+ // Variadic captures and non-variadic captures are both non-deterministic
465
+ if (nextCaptureMarker) {
466
+ allRemainingPatternsAreDeterministic = false;
467
+ }
468
+ }
469
+ const remainingTargetElements = targetElements.length - targetIdx;
470
+ const maxPossible = Math.min(remainingTargetElements - nonVariadicRemainingPatterns, max);
471
+ // Pivot detection optimization: try to find where next pattern matches
472
+ // This avoids unnecessary backtracking when constraints make the split point obvious
473
+ let pivotDetected = false;
474
+ let pivotAt = -1;
475
+ // Skip pivot detection if we're using deterministic optimization
476
+ // (when all remaining patterns are literals, there's only ONE valid consumption amount)
477
+ const useDeterministicOptimization = allRemainingPatternsAreDeterministic && maxPossible >= min && maxPossible <= max;
478
+ if (!useDeterministicOptimization && patternIdx + 1 < patternElements.length && min <= maxPossible) {
479
+ const nextPattern = patternElements[patternIdx + 1];
480
+ // Scan through possible consumption amounts starting from min
481
+ for (let tryConsume = min; tryConsume <= maxPossible; tryConsume++) {
482
+ // Check if element after our consumption would match next pattern
483
+ if (targetIdx + tryConsume < targetElements.length) {
484
+ const candidateElement = targetElements[targetIdx + tryConsume];
485
+ // Skip J.Empty for arguments
486
+ if (filterEmpty && candidateElement.element.kind === java_1.J.Kind.Empty) {
487
+ continue;
488
+ }
489
+ // Test if next pattern matches this element
490
+ const savedMatch = this.match;
491
+ const savedState = this.matcher.saveState();
492
+ yield this.visitRightPadded(nextPattern, candidateElement);
493
+ const matchesNext = this.match;
494
+ this.match = savedMatch;
495
+ this.matcher.restoreState(savedState);
496
+ if (matchesNext) {
497
+ // Found pivot! Try this consumption amount first
498
+ pivotDetected = true;
499
+ pivotAt = tryConsume;
500
+ break;
501
+ }
502
+ }
503
+ }
504
+ }
505
+ // Determine consumption order
506
+ const consumptionOrder = [];
507
+ // OPTIMIZATION: If all remaining patterns are deterministic (literals, not captures),
508
+ // there's only ONE mathematically valid consumption amount. Skip backtracking entirely.
509
+ // Example: foo(${args}, 999) matching foo(1,2,42) -> args MUST be [1,2], only try consume=2
510
+ if (useDeterministicOptimization) {
511
+ consumptionOrder.push(maxPossible);
512
+ }
513
+ else if (pivotDetected && pivotAt >= 0) {
514
+ // Try pivot first, then others as fallback
515
+ consumptionOrder.push(pivotAt);
516
+ for (let c = maxPossible; c >= min; c--) {
517
+ if (c !== pivotAt) {
518
+ consumptionOrder.push(c);
519
+ }
520
+ }
521
+ }
522
+ else {
523
+ // Greedy approach: max to min
524
+ for (let c = maxPossible; c >= min; c--) {
525
+ consumptionOrder.push(c);
526
+ }
527
+ }
528
+ for (const consume of consumptionOrder) {
529
+ // Capture elements for this consumption amount
530
+ // For empty argument lists, there will be a single J.Empty element that we need to filter out
531
+ const rawWrappers = targetElements.slice(targetIdx, targetIdx + consume);
532
+ const capturedWrappers = filterEmpty
533
+ ? rawWrappers.filter(w => w.element.kind !== java_1.J.Kind.Empty)
534
+ : rawWrappers;
535
+ const capturedElements = capturedWrappers.map(w => w.element);
536
+ // Check min/max constraints against filtered elements
537
+ if (capturedElements.length < min || capturedElements.length > max) {
538
+ continue;
539
+ }
540
+ // Evaluate constraint for variadic capture
541
+ // For variadic captures, constraint receives the entire array of captured elements
542
+ // The targetCursor points to the parent container (always defined in container matching)
543
+ if (captureMarker.constraint) {
544
+ const cursor = this.targetCursor || new __1.Cursor(targetElements[0]);
545
+ if (!captureMarker.constraint(capturedElements, cursor)) {
546
+ continue; // Try next consumption amount
547
+ }
548
+ }
549
+ // Save current state for backtracking
550
+ const savedState = this.matcher.saveState();
551
+ // Handle the variadic capture
552
+ const success = this.matcher.handleVariadicCapture(captureMarker, capturedElements, capturedWrappers);
553
+ if (!success) {
554
+ // Restore state and try next amount
555
+ this.matcher.restoreState(savedState);
556
+ continue;
557
+ }
558
+ // Try to match the rest of the pattern
559
+ const restMatches = yield this.matchSequenceOptimized(patternElements, targetElements, patternIdx + 1, targetIdx + consume, filterEmpty);
560
+ if (restMatches) {
561
+ return true; // Found a valid matching
562
+ }
563
+ // Backtrack: restore state and try next amount
564
+ this.matcher.restoreState(savedState);
565
+ }
566
+ return false; // No consumption amount worked
567
+ }
568
+ else {
569
+ // Regular non-variadic element - must match exactly one target element
570
+ if (targetIdx >= targetElements.length) {
571
+ return false; // Pattern has more elements than target
572
+ }
573
+ const targetWrapper = targetElements[targetIdx];
574
+ const targetElement = targetWrapper.element;
575
+ // For arguments, J.Empty represents no argument, so regular captures should not match it
576
+ if (filterEmpty && targetElement.kind === java_1.J.Kind.Empty) {
577
+ return false;
578
+ }
579
+ if (!(yield this.visitSequenceElement(patternWrapper, targetWrapper, targetIdx))) {
580
+ return false;
581
+ }
582
+ // Continue matching the rest
583
+ return yield this.matchSequenceOptimized(patternElements, targetElements, patternIdx + 1, targetIdx + 1, filterEmpty);
584
+ }
585
+ });
586
+ }
587
+ /**
588
+ * Visit a single element in a sequence during non-variadic matching.
589
+ * Extracted to allow debug subclass to add path tracking.
590
+ *
591
+ * @param patternWrapper The pattern element
592
+ * @param targetWrapper The target element
593
+ * @param targetIdx The index in the target sequence
594
+ * @returns true if matching succeeded, false otherwise
595
+ */
596
+ visitSequenceElement(patternWrapper, targetWrapper, targetIdx) {
597
+ return __awaiter(this, void 0, void 0, function* () {
598
+ // Save current state for backtracking (both match state and capture bindings)
599
+ const savedMatch = this.match;
600
+ const savedState = this.matcher.saveState();
601
+ yield this.visitRightPadded(patternWrapper, targetWrapper);
602
+ if (!this.match) {
603
+ // Restore state on match failure
604
+ this.match = savedMatch;
605
+ this.matcher.restoreState(savedState);
606
+ return false;
607
+ }
608
+ return true;
609
+ });
610
+ }
611
+ }
612
+ exports.PatternMatchingComparator = PatternMatchingComparator;
613
+ /**
614
+ * Debug-instrumented version of PatternMatchingComparator.
615
+ * Overrides methods to add path tracking, logging, and explanation capture.
616
+ * Zero cost when not instantiated - production code uses the base class.
617
+ */
618
+ class DebugPatternMatchingComparator extends PatternMatchingComparator {
619
+ get debug() {
620
+ return this.matcher.debug;
621
+ }
622
+ /**
623
+ * Extracts the last segment of a kind string (after the last dot).
624
+ * For example: "org.openrewrite.java.tree.J.MethodInvocation" -> "MethodInvocation"
625
+ */
626
+ formatKind(kind) {
627
+ return kind.substring(kind.lastIndexOf('.') + 1);
628
+ }
629
+ /**
630
+ * Formats a value for display in error messages.
631
+ */
632
+ formatValue(value) {
633
+ if (value === null)
634
+ return 'null';
635
+ if (value === undefined)
636
+ return 'undefined';
637
+ if (typeof value === 'string')
638
+ return `"${value}"`;
639
+ if (typeof value === 'number' || typeof value === 'boolean')
640
+ return String(value);
641
+ // For objects with a kind property (LST nodes)
642
+ if (value && typeof value === 'object' && value.kind) {
643
+ const kind = this.formatKind(value.kind);
644
+ // Show key identifying properties for common node types
645
+ if (value.simpleName)
646
+ return `${kind}("${value.simpleName}")`;
647
+ if (value.value !== undefined)
648
+ return `${kind}(${this.formatValue(value.value)})`;
649
+ return kind;
650
+ }
651
+ return String(value);
652
+ }
653
+ /**
654
+ * Override abort to capture explanation when debug is enabled.
655
+ * Only sets explanation on the first abort call (when this.match is still true).
656
+ * This preserves the most specific explanation closest to the actual mismatch.
657
+ */
658
+ abort(t, reason, propertyName, expected, actual) {
659
+ // If already aborted, don't overwrite the explanation
660
+ // The first abort is typically the most specific
661
+ if (!this.match) {
662
+ return t;
663
+ }
664
+ // If we have context about the mismatch, capture it
665
+ if (reason && this.debug && (expected !== undefined || actual !== undefined)) {
666
+ const expectedStr = this.formatValue(expected);
667
+ const actualStr = this.formatValue(actual);
668
+ this.debug.setExplanation(reason, expectedStr, actualStr, 'Property values do not match');
669
+ }
670
+ // Set `this.match = false`
671
+ return super.abort(t, reason, propertyName, expected, actual);
672
+ }
673
+ /**
674
+ * Override helper methods to extract detailed context from cursors.
675
+ */
676
+ kindMismatch() {
677
+ var _a, _b;
678
+ const pattern = (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.value;
679
+ const target = (_b = this.targetCursor) === null || _b === void 0 ? void 0 : _b.value;
680
+ // Pass the full kind strings - formatValue() will detect and format them
681
+ return this.abort(pattern, 'kind-mismatch', 'kind', this.formatKind(pattern === null || pattern === void 0 ? void 0 : pattern.kind), this.formatKind(target === null || target === void 0 ? void 0 : target.kind));
682
+ }
683
+ structuralMismatch(propertyName) {
684
+ var _a, _b;
685
+ const pattern = (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.value;
686
+ const target = (_b = this.targetCursor) === null || _b === void 0 ? void 0 : _b.value;
687
+ const expectedValue = pattern === null || pattern === void 0 ? void 0 : pattern[propertyName];
688
+ const actualValue = target === null || target === void 0 ? void 0 : target[propertyName];
689
+ return this.abort(pattern, 'structural-mismatch', propertyName, expectedValue, actualValue);
690
+ }
691
+ arrayLengthMismatch(propertyName) {
692
+ var _a, _b;
693
+ const pattern = (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.value;
694
+ const target = (_b = this.targetCursor) === null || _b === void 0 ? void 0 : _b.value;
695
+ const expectedArray = pattern === null || pattern === void 0 ? void 0 : pattern[propertyName];
696
+ const actualArray = target === null || target === void 0 ? void 0 : target[propertyName];
697
+ const expectedLen = Array.isArray(expectedArray) ? expectedArray.length : 'not an array';
698
+ const actualLen = Array.isArray(actualArray) ? actualArray.length : 'not an array';
699
+ return this.abort(pattern, 'array-length-mismatch', propertyName, expectedLen, actualLen);
700
+ }
701
+ valueMismatch(propertyName, expected, actual) {
702
+ var _a, _b;
703
+ const pattern = (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.value;
704
+ const target = (_b = this.targetCursor) === null || _b === void 0 ? void 0 : _b.value;
705
+ // Track number of paths pushed for cleanup
706
+ let pathsPushed = 0;
707
+ // Handle path tracking only if propertyName is provided
708
+ if (propertyName) {
709
+ // Split dotted property paths (e.g., "name.simpleName" → ["name", "simpleName"])
710
+ const pathParts = propertyName.split('.');
711
+ pathsPushed = pathParts.length;
712
+ // Add each property to path with kind information for nested objects
713
+ const kindStr = this.formatKind(pattern === null || pattern === void 0 ? void 0 : pattern.kind);
714
+ this.debug.pushPath(`${kindStr}#${pathParts[0]}`);
715
+ // For nested properties, try to get the kind of intermediate objects
716
+ let currentObj = pattern === null || pattern === void 0 ? void 0 : pattern[pathParts[0]];
717
+ for (let i = 1; i < pathParts.length; i++) {
718
+ if (currentObj && typeof currentObj === 'object' && currentObj.kind) {
719
+ // Include the kind of the nested object
720
+ const nestedKind = this.formatKind(currentObj.kind);
721
+ this.debug.pushPath(`${nestedKind}#${pathParts[i]}`);
722
+ }
723
+ else {
724
+ // Fallback to just the property name if no kind available
725
+ this.debug.pushPath(pathParts[i]);
726
+ }
727
+ currentObj = currentObj === null || currentObj === void 0 ? void 0 : currentObj[pathParts[i]];
728
+ }
729
+ }
730
+ try {
731
+ // If expected/actual provided, use them directly
732
+ if (expected !== undefined || actual !== undefined) {
733
+ return this.abort(pattern, 'value-mismatch', propertyName, expected, actual);
734
+ }
735
+ // Otherwise, try to extract from cursors (fallback for older code)
736
+ if (propertyName) {
737
+ // Navigate dotted property paths
738
+ const getNestedValue = (obj, path) => {
739
+ return path.split('.').reduce((current, prop) => current === null || current === void 0 ? void 0 : current[prop], obj);
740
+ };
741
+ const expectedValue = getNestedValue(pattern, propertyName);
742
+ const actualValue = getNestedValue(target, propertyName);
743
+ return this.abort(pattern, 'value-mismatch', propertyName, expectedValue, actualValue);
744
+ }
745
+ else {
746
+ // No property name - compare whole objects
747
+ return this.abort(pattern, 'value-mismatch', propertyName, pattern, target);
748
+ }
749
+ }
750
+ finally {
751
+ // Pop all the path components we pushed
752
+ for (let i = 0; i < pathsPushed; i++) {
753
+ this.debug.popPath();
754
+ }
755
+ }
756
+ }
757
+ visit(j, p, parent) {
758
+ const _super = Object.create(null, {
759
+ visit: { get: () => super.visit }
760
+ });
761
+ return __awaiter(this, void 0, void 0, function* () {
762
+ const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(j);
763
+ if (captureMarker) {
764
+ const savedTargetCursor = this.targetCursor;
765
+ const cursorAtCapturedNode = this.targetCursor !== undefined
766
+ ? new __1.Cursor(p, this.targetCursor)
767
+ : new __1.Cursor(p);
768
+ this.targetCursor = cursorAtCapturedNode;
769
+ try {
770
+ if (captureMarker.constraint && !captureMarker.variadicOptions) {
771
+ this.debug.log('debug', 'constraint', `Evaluating constraint for capture: ${captureMarker.captureName}`);
772
+ const constraintResult = captureMarker.constraint(p, cursorAtCapturedNode);
773
+ if (!constraintResult) {
774
+ this.debug.log('info', 'constraint', `Constraint failed for capture: ${captureMarker.captureName}`);
775
+ this.debug.setExplanation('constraint-failed', `Capture ${captureMarker.captureName} with valid constraint`, `Constraint failed for ${p.kind}`, `Constraint evaluation returned false`);
776
+ return this.abort(j);
777
+ }
778
+ this.debug.log('debug', 'constraint', `Constraint passed for capture: ${captureMarker.captureName}`);
779
+ }
780
+ const success = this.matcher.handleCapture(captureMarker, p, undefined);
781
+ if (!success) {
782
+ return this.abort(j);
783
+ }
784
+ return j;
785
+ }
786
+ finally {
787
+ this.targetCursor = savedTargetCursor;
788
+ }
789
+ }
790
+ return yield _super.visit.call(this, j, p, parent);
791
+ });
792
+ }
793
+ visitElement(j, other) {
794
+ return __awaiter(this, void 0, void 0, function* () {
795
+ if (!this.match) {
796
+ return j;
797
+ }
798
+ const kindStr = this.formatKind(j.kind);
799
+ if (j.kind !== other.kind) {
800
+ return this.abort(j, 'kind-mismatch', 'kind', kindStr, this.formatKind(other.kind));
801
+ }
802
+ for (const key of Object.keys(j)) {
803
+ if (key.startsWith('_') || key === 'kind' || key === 'id' || key === 'markers' || key === 'prefix') {
804
+ continue;
805
+ }
806
+ const jValue = j[key];
807
+ const otherValue = other[key];
808
+ if (Array.isArray(jValue)) {
809
+ if (!Array.isArray(otherValue) || jValue.length !== otherValue.length) {
810
+ this.debug.pushPath(`${kindStr}#${key}`);
811
+ const result = this.abort(j, 'array-length-mismatch', key, jValue.length, Array.isArray(otherValue) ? otherValue.length : otherValue);
812
+ this.debug.popPath();
813
+ return result;
814
+ }
815
+ for (let i = 0; i < jValue.length; i++) {
816
+ this.debug.pushPath(`${kindStr}#${key}`);
817
+ this.debug.pushPath(i.toString());
818
+ try {
819
+ yield this.visitProperty(jValue[i], otherValue[i]);
820
+ if (!this.match) {
821
+ return j;
822
+ }
823
+ }
824
+ finally {
825
+ this.debug.popPath();
826
+ this.debug.popPath();
827
+ }
828
+ }
829
+ }
830
+ else {
831
+ this.debug.pushPath(`${kindStr}#${key}`);
832
+ try {
833
+ yield this.visitProperty(jValue, otherValue);
834
+ if (!this.match) {
835
+ return j;
836
+ }
837
+ }
838
+ finally {
839
+ this.debug.popPath();
840
+ }
841
+ }
842
+ }
843
+ return j;
844
+ });
845
+ }
846
+ visitRightPadded(right, p) {
847
+ const _super = Object.create(null, {
848
+ visitRightPadded: { get: () => super.visitRightPadded }
849
+ });
850
+ return __awaiter(this, void 0, void 0, function* () {
851
+ if (!this.match) {
852
+ return right;
853
+ }
854
+ const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(right);
855
+ if (captureMarker) {
856
+ const isRightPadded = p.kind === java_1.J.Kind.RightPadded;
857
+ const targetWrapper = isRightPadded ? p : undefined;
858
+ const targetElement = isRightPadded ? targetWrapper.element : p;
859
+ const savedTargetCursor = this.targetCursor;
860
+ const cursorAtCapturedNode = this.targetCursor !== undefined
861
+ ? (targetWrapper ? new __1.Cursor(targetWrapper, this.targetCursor) : new __1.Cursor(targetElement, this.targetCursor))
862
+ : (targetWrapper ? new __1.Cursor(targetWrapper) : new __1.Cursor(targetElement));
863
+ this.targetCursor = cursorAtCapturedNode;
864
+ try {
865
+ if (captureMarker.constraint && !captureMarker.variadicOptions) {
866
+ this.debug.log('debug', 'constraint', `Evaluating constraint for wrapped capture: ${captureMarker.captureName}`);
867
+ const constraintResult = captureMarker.constraint(targetElement, cursorAtCapturedNode);
868
+ if (!constraintResult) {
869
+ this.debug.log('info', 'constraint', `Constraint failed for wrapped capture: ${captureMarker.captureName}`);
870
+ this.debug.setExplanation('constraint-failed', `Capture ${captureMarker.captureName} with valid constraint`, `Constraint failed for ${targetElement.kind}`, `Constraint evaluation returned false`);
871
+ return this.abort(right);
872
+ }
873
+ this.debug.log('debug', 'constraint', `Constraint passed for wrapped capture: ${captureMarker.captureName}`);
874
+ }
875
+ const success = this.matcher.handleCapture(captureMarker, targetElement, targetWrapper);
876
+ if (!success) {
877
+ return this.abort(right);
878
+ }
879
+ return right;
880
+ }
881
+ finally {
882
+ this.targetCursor = savedTargetCursor;
883
+ }
884
+ }
885
+ return yield _super.visitRightPadded.call(this, right, p);
886
+ });
887
+ }
888
+ visitContainer(container, p) {
889
+ return __awaiter(this, void 0, void 0, function* () {
890
+ if (!this.match) {
891
+ return container;
892
+ }
893
+ const isContainer = p.kind === java_1.J.Kind.Container;
894
+ if (!isContainer) {
895
+ return this.abort(container);
896
+ }
897
+ const otherContainer = p;
898
+ const hasVariadicCapture = container.elements.some(elem => utils_1.PlaceholderUtils.isVariadicCapture(elem));
899
+ const savedCursor = this.cursor;
900
+ const savedTargetCursor = this.targetCursor;
901
+ this.cursor = new __1.Cursor(container, this.cursor);
902
+ this.targetCursor = new __1.Cursor(otherContainer, this.targetCursor);
903
+ try {
904
+ if (hasVariadicCapture) {
905
+ if (!(yield this.matchSequence(container.elements, otherContainer.elements, true))) {
906
+ return this.arrayLengthMismatch('elements');
907
+ }
908
+ }
909
+ else {
910
+ // Non-variadic path - track indices
911
+ if (container.elements.length !== otherContainer.elements.length) {
912
+ return this.arrayLengthMismatch('elements');
913
+ }
914
+ for (let i = 0; i < container.elements.length; i++) {
915
+ this.debug.pushPath(i.toString());
916
+ try {
917
+ if (!(yield this.visitContainerElement(container.elements[i], otherContainer.elements[i], i))) {
918
+ return container;
919
+ }
920
+ }
921
+ finally {
922
+ this.debug.popPath();
923
+ }
924
+ }
925
+ }
926
+ }
927
+ finally {
928
+ this.cursor = savedCursor;
929
+ this.targetCursor = savedTargetCursor;
930
+ }
931
+ return container;
932
+ });
933
+ }
934
+ /**
935
+ * Override visitContainerProperty to add path tracking with property context.
936
+ */
937
+ visitContainerProperty(propertyName, container, otherContainer) {
938
+ return __awaiter(this, void 0, void 0, function* () {
939
+ // Get parent from cursor
940
+ const parent = this.cursor.value;
941
+ // Push path for the property
942
+ const kindStr = this.formatKind(parent.kind);
943
+ this.debug.pushPath(`${kindStr}#${propertyName}`);
944
+ try {
945
+ yield this.visitContainer(container, otherContainer);
946
+ return container;
947
+ }
948
+ finally {
949
+ this.debug.popPath();
950
+ }
951
+ });
952
+ }
953
+ /**
954
+ * Override visitRightPaddedProperty to add path tracking with property context.
955
+ */
956
+ visitRightPaddedProperty(propertyName, rightPadded, otherRightPadded) {
957
+ return __awaiter(this, void 0, void 0, function* () {
958
+ // Get parent from cursor
959
+ const parent = this.cursor.value;
960
+ // Push path for the property
961
+ const kindStr = this.formatKind(parent.kind);
962
+ this.debug.pushPath(`${kindStr}#${propertyName}`);
963
+ try {
964
+ return yield this.visitRightPadded(rightPadded, otherRightPadded);
965
+ }
966
+ finally {
967
+ this.debug.popPath();
968
+ }
969
+ });
970
+ }
971
+ /**
972
+ * Override visitLeftPaddedProperty to add path tracking with property context.
973
+ */
974
+ visitLeftPaddedProperty(propertyName, leftPadded, otherLeftPadded) {
975
+ return __awaiter(this, void 0, void 0, function* () {
976
+ // Get parent from cursor
977
+ const parent = this.cursor.value;
978
+ // Push path for the property
979
+ const kindStr = this.formatKind(parent.kind);
980
+ this.debug.pushPath(`${kindStr}#${propertyName}`);
981
+ try {
982
+ return yield this.visitLeftPadded(leftPadded, otherLeftPadded);
983
+ }
984
+ finally {
985
+ this.debug.popPath();
986
+ }
987
+ });
988
+ }
989
+ visitContainerElement(element, otherElement, index) {
990
+ const _super = Object.create(null, {
991
+ visitContainerElement: { get: () => super.visitContainerElement }
992
+ });
993
+ return __awaiter(this, void 0, void 0, function* () {
994
+ // Don't push index here - it should be handled by the caller with proper context
995
+ return yield _super.visitContainerElement.call(this, element, otherElement, index);
996
+ });
997
+ }
998
+ visitArrayProperty(parent, propertyName, array1, array2, visitor) {
999
+ return __awaiter(this, void 0, void 0, function* () {
1000
+ // Push path for the property
1001
+ const kindStr = this.formatKind(parent.kind);
1002
+ this.debug.pushPath(`${kindStr}#${propertyName}`);
1003
+ try {
1004
+ // Check length mismatch (will have path context)
1005
+ if (array1.length !== array2.length) {
1006
+ this.arrayLengthMismatch(propertyName);
1007
+ return;
1008
+ }
1009
+ // Visit each element with index tracking
1010
+ for (let i = 0; i < array1.length; i++) {
1011
+ this.debug.pushPath(i.toString());
1012
+ try {
1013
+ yield visitor(array1[i], array2[i], i);
1014
+ if (!this.match) {
1015
+ return;
1016
+ }
1017
+ }
1018
+ finally {
1019
+ this.debug.popPath();
1020
+ }
1021
+ }
1022
+ }
1023
+ finally {
1024
+ this.debug.popPath();
1025
+ }
1026
+ });
1027
+ }
1028
+ matchSequence(patternElements, targetElements, filterEmpty) {
1029
+ const _super = Object.create(null, {
1030
+ matchSequence: { get: () => super.matchSequence }
1031
+ });
1032
+ return __awaiter(this, void 0, void 0, function* () {
1033
+ var _a, _b;
1034
+ // Push path component for the container
1035
+ // Extract kind from cursors if available
1036
+ const pattern = (_a = this.cursor) === null || _a === void 0 ? void 0 : _a.value;
1037
+ if (pattern && pattern.kind) {
1038
+ const kindStr = this.formatKind(pattern.kind);
1039
+ // Determine property name based on the kind
1040
+ let propertyName = 'elements';
1041
+ if (pattern.kind.includes('MethodInvocation')) {
1042
+ propertyName = 'arguments';
1043
+ }
1044
+ else if (pattern.kind.includes('Block')) {
1045
+ propertyName = 'statements';
1046
+ }
1047
+ this.debug.pushPath(`${kindStr}#${propertyName}`);
1048
+ }
1049
+ try {
1050
+ return yield _super.matchSequence.call(this, patternElements, targetElements, filterEmpty);
1051
+ }
1052
+ finally {
1053
+ if ((_b = this.cursor) === null || _b === void 0 ? void 0 : _b.value) {
1054
+ this.debug.popPath();
1055
+ }
1056
+ }
1057
+ });
1058
+ }
1059
+ visitSequenceElement(patternWrapper, targetWrapper, targetIdx) {
1060
+ return __awaiter(this, void 0, void 0, function* () {
1061
+ this.debug.pushPath(targetIdx.toString());
1062
+ try {
1063
+ // Save current state for backtracking (both match state and capture bindings)
1064
+ const savedMatch = this.match;
1065
+ const savedState = this.matcher.saveState();
1066
+ yield this.visitRightPadded(patternWrapper, targetWrapper);
1067
+ if (!this.match) {
1068
+ // Preserve explanation before restoring state
1069
+ const explanation = this.debug.getExplanation();
1070
+ // Restore state on match failure
1071
+ this.match = savedMatch;
1072
+ this.matcher.restoreState(savedState);
1073
+ // Restore the explanation if one was set during matching
1074
+ if (explanation) {
1075
+ this.debug.restoreExplanation(explanation);
1076
+ }
1077
+ return false;
1078
+ }
1079
+ return true;
1080
+ }
1081
+ finally {
1082
+ this.debug.popPath();
1083
+ }
1084
+ });
1085
+ }
1086
+ matchSequenceOptimized(patternElements, targetElements, patternIdx, targetIdx, filterEmpty) {
1087
+ return __awaiter(this, void 0, void 0, function* () {
1088
+ var _a, _b;
1089
+ if (patternIdx >= patternElements.length) {
1090
+ return targetIdx >= targetElements.length;
1091
+ }
1092
+ const patternWrapper = patternElements[patternIdx];
1093
+ const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(patternWrapper);
1094
+ const isVariadic = (captureMarker === null || captureMarker === void 0 ? void 0 : captureMarker.variadicOptions) !== undefined;
1095
+ if (isVariadic) {
1096
+ const variadicOptions = captureMarker.variadicOptions;
1097
+ const min = (_a = variadicOptions === null || variadicOptions === void 0 ? void 0 : variadicOptions.min) !== null && _a !== void 0 ? _a : 0;
1098
+ const max = (_b = variadicOptions === null || variadicOptions === void 0 ? void 0 : variadicOptions.max) !== null && _b !== void 0 ? _b : Infinity;
1099
+ let nonVariadicRemainingPatterns = 0;
1100
+ let allRemainingPatternsAreDeterministic = true;
1101
+ for (let i = patternIdx + 1; i < patternElements.length; i++) {
1102
+ const nextCaptureMarker = utils_1.PlaceholderUtils.getCaptureMarker(patternElements[i]);
1103
+ const nextIsVariadic = (nextCaptureMarker === null || nextCaptureMarker === void 0 ? void 0 : nextCaptureMarker.variadicOptions) !== undefined;
1104
+ if (!nextIsVariadic) {
1105
+ nonVariadicRemainingPatterns++;
1106
+ }
1107
+ if (nextCaptureMarker) {
1108
+ allRemainingPatternsAreDeterministic = false;
1109
+ }
1110
+ }
1111
+ const remainingTargetElements = targetElements.length - targetIdx;
1112
+ const maxPossible = Math.min(remainingTargetElements - nonVariadicRemainingPatterns, max);
1113
+ let pivotDetected = false;
1114
+ let pivotAt = -1;
1115
+ // Skip pivot detection if we're using deterministic optimization
1116
+ // (when all remaining patterns are literals, there's only ONE valid consumption amount)
1117
+ const useDeterministicOptimization = allRemainingPatternsAreDeterministic && maxPossible >= min && maxPossible <= max;
1118
+ if (!useDeterministicOptimization && patternIdx + 1 < patternElements.length && min <= maxPossible) {
1119
+ const nextPattern = patternElements[patternIdx + 1];
1120
+ for (let tryConsume = min; tryConsume <= maxPossible; tryConsume++) {
1121
+ if (targetIdx + tryConsume < targetElements.length) {
1122
+ const candidateElement = targetElements[targetIdx + tryConsume];
1123
+ if (filterEmpty && candidateElement.element.kind === java_1.J.Kind.Empty) {
1124
+ continue;
1125
+ }
1126
+ const savedMatch = this.match;
1127
+ const savedState = this.matcher.saveState();
1128
+ yield this.visitRightPadded(nextPattern, candidateElement);
1129
+ const matchesNext = this.match;
1130
+ this.match = savedMatch;
1131
+ this.matcher.restoreState(savedState);
1132
+ if (matchesNext) {
1133
+ pivotDetected = true;
1134
+ pivotAt = tryConsume;
1135
+ break;
1136
+ }
1137
+ }
1138
+ }
1139
+ }
1140
+ const consumptionOrder = [];
1141
+ // OPTIMIZATION: If all remaining patterns are deterministic (literals, not captures),
1142
+ // there's only ONE mathematically valid consumption amount. Skip backtracking entirely.
1143
+ // Example: foo(${args}, 999) matching foo(1,2,42) -> args MUST be [1,2], only try consume=2
1144
+ if (useDeterministicOptimization) {
1145
+ consumptionOrder.push(maxPossible);
1146
+ }
1147
+ else if (pivotDetected && pivotAt >= 0) {
1148
+ consumptionOrder.push(pivotAt);
1149
+ for (let c = maxPossible; c >= min; c--) {
1150
+ if (c !== pivotAt) {
1151
+ consumptionOrder.push(c);
1152
+ }
1153
+ }
1154
+ }
1155
+ else {
1156
+ for (let c = maxPossible; c >= min; c--) {
1157
+ consumptionOrder.push(c);
1158
+ }
1159
+ }
1160
+ for (const consume of consumptionOrder) {
1161
+ // Capture elements for this consumption amount
1162
+ // For empty argument lists, there will be a single J.Empty element that we need to filter out
1163
+ const rawWrappers = targetElements.slice(targetIdx, targetIdx + consume);
1164
+ const capturedWrappers = filterEmpty
1165
+ ? rawWrappers.filter(w => w.element.kind !== java_1.J.Kind.Empty)
1166
+ : rawWrappers;
1167
+ const capturedElements = capturedWrappers.map(w => w.element);
1168
+ // Check min/max constraints against filtered elements
1169
+ if (capturedElements.length < min || capturedElements.length > max) {
1170
+ continue;
1171
+ }
1172
+ if (captureMarker.constraint) {
1173
+ this.debug.log('debug', 'constraint', `Evaluating variadic constraint for capture: ${captureMarker.captureName} (${capturedElements.length} elements)`);
1174
+ const cursor = this.targetCursor || new __1.Cursor(targetElements[0]);
1175
+ const constraintResult = captureMarker.constraint(capturedElements, cursor);
1176
+ if (!constraintResult) {
1177
+ this.debug.log('info', 'constraint', `Variadic constraint failed for capture: ${captureMarker.captureName}`);
1178
+ continue;
1179
+ }
1180
+ this.debug.log('debug', 'constraint', `Variadic constraint passed for capture: ${captureMarker.captureName}`);
1181
+ }
1182
+ const savedState = this.matcher.saveState();
1183
+ const success = this.matcher.handleVariadicCapture(captureMarker, capturedElements, capturedWrappers);
1184
+ if (!success) {
1185
+ this.matcher.restoreState(savedState);
1186
+ continue;
1187
+ }
1188
+ const restMatches = yield this.matchSequenceOptimized(patternElements, targetElements, patternIdx + 1, targetIdx + consume, filterEmpty);
1189
+ if (restMatches) {
1190
+ return true;
1191
+ }
1192
+ // Preserve explanation from this failed attempt before restoring state
1193
+ // This is especially important when using deterministic optimization (only one attempt)
1194
+ const currentExplanation = this.debug.getExplanation();
1195
+ this.matcher.restoreState(savedState);
1196
+ // Restore the explanation if one was set during this attempt
1197
+ if (currentExplanation) {
1198
+ this.debug.restoreExplanation(currentExplanation);
1199
+ }
1200
+ }
1201
+ return false;
1202
+ }
1203
+ else {
1204
+ if (targetIdx >= targetElements.length) {
1205
+ return false;
1206
+ }
1207
+ const targetWrapper = targetElements[targetIdx];
1208
+ const targetElement = targetWrapper.element;
1209
+ if (filterEmpty && targetElement.kind === java_1.J.Kind.Empty) {
1210
+ return false;
1211
+ }
1212
+ if (!(yield this.visitSequenceElement(patternWrapper, targetWrapper, targetIdx))) {
1213
+ return false;
1214
+ }
1215
+ return yield this.matchSequenceOptimized(patternElements, targetElements, patternIdx + 1, targetIdx + 1, filterEmpty);
1216
+ }
1217
+ });
1218
+ }
1219
+ }
1220
+ exports.DebugPatternMatchingComparator = DebugPatternMatchingComparator;
1221
+ //# sourceMappingURL=comparator.js.map