@openrewrite/rewrite 8.66.1 → 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.
- package/dist/java/tree.d.ts +10 -1
- package/dist/java/tree.d.ts.map +1 -1
- package/dist/java/tree.js +21 -5
- package/dist/java/tree.js.map +1 -1
- package/dist/java/type-visitor.d.ts +1 -1
- package/dist/java/type-visitor.d.ts.map +1 -1
- package/dist/java/visitor.d.ts +2 -2
- package/dist/java/visitor.d.ts.map +1 -1
- package/dist/java/visitor.js +8 -2
- package/dist/java/visitor.js.map +1 -1
- package/dist/javascript/assertions.d.ts +6 -0
- package/dist/javascript/assertions.d.ts.map +1 -1
- package/dist/javascript/assertions.js +14 -6
- package/dist/javascript/assertions.js.map +1 -1
- package/dist/javascript/comparator.d.ts +154 -7
- package/dist/javascript/comparator.d.ts.map +1 -1
- package/dist/javascript/comparator.js +623 -180
- package/dist/javascript/comparator.js.map +1 -1
- package/dist/javascript/format.d.ts +5 -3
- package/dist/javascript/format.d.ts.map +1 -1
- package/dist/javascript/format.js +85 -43
- package/dist/javascript/format.js.map +1 -1
- package/dist/javascript/index.d.ts +1 -0
- package/dist/javascript/index.d.ts.map +1 -1
- package/dist/javascript/index.js +1 -0
- package/dist/javascript/index.js.map +1 -1
- package/dist/javascript/parser.d.ts +2 -1
- package/dist/javascript/parser.d.ts.map +1 -1
- package/dist/javascript/parser.js +39 -30
- package/dist/javascript/parser.js.map +1 -1
- package/dist/javascript/templating/capture.d.ts +81 -14
- package/dist/javascript/templating/capture.d.ts.map +1 -1
- package/dist/javascript/templating/capture.js +98 -8
- package/dist/javascript/templating/capture.js.map +1 -1
- package/dist/javascript/templating/comparator.d.ts +125 -15
- package/dist/javascript/templating/comparator.d.ts.map +1 -1
- package/dist/javascript/templating/comparator.js +946 -118
- package/dist/javascript/templating/comparator.js.map +1 -1
- package/dist/javascript/templating/engine.d.ts +58 -25
- package/dist/javascript/templating/engine.d.ts.map +1 -1
- package/dist/javascript/templating/engine.js +527 -94
- package/dist/javascript/templating/engine.js.map +1 -1
- package/dist/javascript/templating/index.d.ts +3 -3
- package/dist/javascript/templating/index.d.ts.map +1 -1
- package/dist/javascript/templating/index.js +3 -1
- package/dist/javascript/templating/index.js.map +1 -1
- package/dist/javascript/templating/pattern.d.ts +121 -16
- package/dist/javascript/templating/pattern.d.ts.map +1 -1
- package/dist/javascript/templating/pattern.js +528 -257
- package/dist/javascript/templating/pattern.js.map +1 -1
- package/dist/javascript/templating/placeholder-replacement.d.ts +30 -5
- package/dist/javascript/templating/placeholder-replacement.d.ts.map +1 -1
- package/dist/javascript/templating/placeholder-replacement.js +183 -81
- package/dist/javascript/templating/placeholder-replacement.js.map +1 -1
- package/dist/javascript/templating/rewrite.d.ts +56 -11
- package/dist/javascript/templating/rewrite.d.ts.map +1 -1
- package/dist/javascript/templating/rewrite.js +143 -16
- package/dist/javascript/templating/rewrite.js.map +1 -1
- package/dist/javascript/templating/template.d.ts +31 -5
- package/dist/javascript/templating/template.d.ts.map +1 -1
- package/dist/javascript/templating/template.js +89 -15
- package/dist/javascript/templating/template.js.map +1 -1
- package/dist/javascript/templating/types.d.ts +359 -12
- package/dist/javascript/templating/types.d.ts.map +1 -1
- package/dist/javascript/templating/utils.d.ts +52 -35
- package/dist/javascript/templating/utils.d.ts.map +1 -1
- package/dist/javascript/templating/utils.js +107 -109
- package/dist/javascript/templating/utils.js.map +1 -1
- package/dist/javascript/type-mapping.d.ts.map +1 -1
- package/dist/javascript/type-mapping.js +21 -11
- package/dist/javascript/type-mapping.js.map +1 -1
- package/dist/json/rpc.js +2 -2
- package/dist/json/rpc.js.map +1 -1
- package/dist/recipe/order-imports.js.map +1 -1
- package/dist/test/rewrite-test.d.ts.map +1 -1
- package/dist/test/rewrite-test.js +10 -6
- package/dist/test/rewrite-test.js.map +1 -1
- package/dist/version.txt +1 -1
- package/dist/visitor.d.ts +4 -4
- package/dist/visitor.d.ts.map +1 -1
- package/dist/visitor.js +8 -3
- package/dist/visitor.js.map +1 -1
- package/package.json +4 -2
- package/src/java/tree.ts +10 -3
- package/src/java/type-visitor.ts +1 -1
- package/src/java/visitor.ts +11 -5
- package/src/javascript/assertions.ts +9 -3
- package/src/javascript/comparator.ts +676 -185
- package/src/javascript/format.ts +72 -34
- package/src/javascript/index.ts +1 -0
- package/src/javascript/parser.ts +51 -31
- package/src/javascript/templating/capture.ts +107 -15
- package/src/javascript/templating/comparator.ts +1087 -134
- package/src/javascript/templating/engine.ts +601 -103
- package/src/javascript/templating/index.ts +9 -2
- package/src/javascript/templating/pattern.ts +655 -281
- package/src/javascript/templating/placeholder-replacement.ts +183 -80
- package/src/javascript/templating/rewrite.ts +152 -18
- package/src/javascript/templating/template.ts +110 -22
- package/src/javascript/templating/types.ts +386 -12
- package/src/javascript/templating/utils.ts +116 -102
- package/src/javascript/type-mapping.ts +20 -11
- package/src/json/rpc.ts +2 -2
- package/src/recipe/order-imports.ts +1 -1
- package/src/test/rewrite-test.ts +12 -7
- package/src/visitor.ts +14 -6
|
@@ -9,7 +9,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
});
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.PatternMatchingComparator = void 0;
|
|
12
|
+
exports.DebugPatternMatchingComparator = exports.PatternMatchingComparator = void 0;
|
|
13
13
|
/*
|
|
14
14
|
* Copyright 2025 the original author or authors.
|
|
15
15
|
* <p>
|
|
@@ -41,55 +41,180 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
41
41
|
super(lenientTypeMatching);
|
|
42
42
|
this.matcher = matcher;
|
|
43
43
|
}
|
|
44
|
-
/**
|
|
45
|
-
* Extracts the wrapper from the cursor if the parent is a RightPadded.
|
|
46
|
-
*/
|
|
47
|
-
getWrapperFromCursor(cursor) {
|
|
48
|
-
if (!cursor.parent) {
|
|
49
|
-
return undefined;
|
|
50
|
-
}
|
|
51
|
-
const parent = cursor.parent.value;
|
|
52
|
-
// Check if parent is a RightPadded by checking its kind
|
|
53
|
-
if (parent.kind === java_1.J.Kind.RightPadded) {
|
|
54
|
-
return parent;
|
|
55
|
-
}
|
|
56
|
-
return undefined;
|
|
57
|
-
}
|
|
58
44
|
visit(j, p, parent) {
|
|
59
45
|
const _super = Object.create(null, {
|
|
60
46
|
visit: { get: () => super.visit }
|
|
61
47
|
});
|
|
62
48
|
return __awaiter(this, void 0, void 0, function* () {
|
|
63
|
-
// Check if the pattern node is a capture - this handles captures
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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;
|
|
69
78
|
}
|
|
70
|
-
return j;
|
|
71
79
|
}
|
|
72
80
|
if (!this.match) {
|
|
73
81
|
return j;
|
|
74
82
|
}
|
|
75
|
-
|
|
83
|
+
// Continue with parent's visit which will push targetCursor and traverse
|
|
84
|
+
return yield _super.visit.call(this, j, p, parent);
|
|
76
85
|
});
|
|
77
86
|
}
|
|
78
87
|
hasSameKind(j, other) {
|
|
79
88
|
return super.hasSameKind(j, other) ||
|
|
80
89
|
(j.kind == java_1.J.Kind.Identifier && utils_1.PlaceholderUtils.isCapture(j));
|
|
81
90
|
}
|
|
82
|
-
|
|
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) {
|
|
83
109
|
const _super = Object.create(null, {
|
|
84
|
-
|
|
110
|
+
visitRightPadded: { get: () => super.visitRightPadded }
|
|
85
111
|
});
|
|
86
112
|
return __awaiter(this, void 0, void 0, function* () {
|
|
87
|
-
if (
|
|
88
|
-
|
|
89
|
-
const success = this.matcher.handleCapture(identifier, other, wrapper);
|
|
90
|
-
return success ? identifier : this.abort(identifier);
|
|
113
|
+
if (!this.match) {
|
|
114
|
+
return right;
|
|
91
115
|
}
|
|
92
|
-
|
|
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;
|
|
93
218
|
});
|
|
94
219
|
}
|
|
95
220
|
visitMethodInvocation(methodInvocation, other) {
|
|
@@ -98,51 +223,77 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
98
223
|
});
|
|
99
224
|
return __awaiter(this, void 0, void 0, function* () {
|
|
100
225
|
// Check if any arguments are variadic captures
|
|
101
|
-
const hasVariadicCapture = methodInvocation.arguments.elements.some(arg => utils_1.PlaceholderUtils.isVariadicCapture(arg
|
|
226
|
+
const hasVariadicCapture = methodInvocation.arguments.elements.some(arg => utils_1.PlaceholderUtils.isVariadicCapture(arg));
|
|
102
227
|
// If no variadic captures, use parent implementation (which includes semantic/type-aware matching)
|
|
103
228
|
if (!hasVariadicCapture) {
|
|
104
229
|
return _super.visitMethodInvocation.call(this, methodInvocation, other);
|
|
105
230
|
}
|
|
106
231
|
// Otherwise, handle variadic captures ourselves
|
|
107
|
-
if (!this.match
|
|
108
|
-
return this.abort(methodInvocation);
|
|
109
|
-
}
|
|
110
|
-
const otherMethodInvocation = other;
|
|
111
|
-
// Compare select
|
|
112
|
-
if ((methodInvocation.select === undefined) !== (otherMethodInvocation.select === undefined)) {
|
|
232
|
+
if (!this.match) {
|
|
113
233
|
return this.abort(methodInvocation);
|
|
114
234
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
+
}
|
|
124
248
|
}
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
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');
|
|
129
259
|
}
|
|
130
|
-
// Visit
|
|
131
|
-
|
|
132
|
-
yield this.visit(methodInvocation.
|
|
260
|
+
// Visit select if present
|
|
261
|
+
if (methodInvocation.select && otherMethodInvocation.select) {
|
|
262
|
+
yield this.visit(methodInvocation.select.element, otherMethodInvocation.select.element);
|
|
133
263
|
if (!this.match)
|
|
134
264
|
return methodInvocation;
|
|
135
265
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
+
}
|
|
140
291
|
return methodInvocation;
|
|
141
|
-
// Special handling for variadic captures in arguments
|
|
142
|
-
if (!(yield this.matchArguments(methodInvocation.arguments.elements, otherMethodInvocation.arguments.elements))) {
|
|
143
|
-
return this.abort(methodInvocation);
|
|
144
292
|
}
|
|
145
|
-
|
|
293
|
+
finally {
|
|
294
|
+
this.cursor = savedCursor;
|
|
295
|
+
this.targetCursor = savedTargetCursor;
|
|
296
|
+
}
|
|
146
297
|
});
|
|
147
298
|
}
|
|
148
299
|
visitBlock(block, other) {
|
|
@@ -152,7 +303,7 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
152
303
|
return __awaiter(this, void 0, void 0, function* () {
|
|
153
304
|
// Check if any statements have CaptureMarker indicating they're variadic
|
|
154
305
|
const hasVariadicCapture = block.statements.some(stmt => {
|
|
155
|
-
const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(stmt
|
|
306
|
+
const captureMarker = utils_1.PlaceholderUtils.getCaptureMarker(stmt);
|
|
156
307
|
return (captureMarker === null || captureMarker === void 0 ? void 0 : captureMarker.variadicOptions) !== undefined;
|
|
157
308
|
});
|
|
158
309
|
// If no variadic captures, use parent implementation
|
|
@@ -160,15 +311,40 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
160
311
|
return _super.visitBlock.call(this, block, other);
|
|
161
312
|
}
|
|
162
313
|
// Otherwise, handle variadic captures ourselves
|
|
163
|
-
if (!this.match
|
|
314
|
+
if (!this.match) {
|
|
164
315
|
return this.abort(block);
|
|
165
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
|
+
}
|
|
166
331
|
const otherBlock = other;
|
|
167
|
-
//
|
|
168
|
-
|
|
169
|
-
|
|
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;
|
|
170
347
|
}
|
|
171
|
-
return block;
|
|
172
348
|
});
|
|
173
349
|
}
|
|
174
350
|
visitJsCompilationUnit(compilationUnit, other) {
|
|
@@ -176,25 +352,49 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
176
352
|
visitJsCompilationUnit: { get: () => super.visitJsCompilationUnit }
|
|
177
353
|
});
|
|
178
354
|
return __awaiter(this, void 0, void 0, function* () {
|
|
179
|
-
// Check if any statements are variadic captures
|
|
355
|
+
// Check if any statements are variadic captures
|
|
180
356
|
const hasVariadicCapture = compilationUnit.statements.some(stmt => {
|
|
181
|
-
|
|
182
|
-
return utils_1.PlaceholderUtils.isVariadicCapture(unwrapped);
|
|
357
|
+
return utils_1.PlaceholderUtils.isVariadicCapture(stmt);
|
|
183
358
|
});
|
|
184
359
|
// If no variadic captures, use parent implementation
|
|
185
360
|
if (!hasVariadicCapture) {
|
|
186
361
|
return _super.visitJsCompilationUnit.call(this, compilationUnit, other);
|
|
187
362
|
}
|
|
188
363
|
// Otherwise, handle variadic captures ourselves
|
|
189
|
-
if (!this.match
|
|
364
|
+
if (!this.match) {
|
|
190
365
|
return this.abort(compilationUnit);
|
|
191
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
|
+
}
|
|
192
381
|
const otherCompilationUnit = other;
|
|
193
|
-
//
|
|
194
|
-
|
|
195
|
-
|
|
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;
|
|
196
397
|
}
|
|
197
|
-
return compilationUnit;
|
|
198
398
|
});
|
|
199
399
|
}
|
|
200
400
|
/**
|
|
@@ -203,7 +403,7 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
203
403
|
*/
|
|
204
404
|
matchArguments(patternArgs, targetArgs) {
|
|
205
405
|
return __awaiter(this, void 0, void 0, function* () {
|
|
206
|
-
return this.matchSequence(patternArgs, targetArgs, true);
|
|
406
|
+
return yield this.matchSequence(patternArgs, targetArgs, true);
|
|
207
407
|
});
|
|
208
408
|
}
|
|
209
409
|
/**
|
|
@@ -242,23 +442,29 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
242
442
|
if (patternIdx >= patternElements.length) {
|
|
243
443
|
return targetIdx >= targetElements.length; // Success if all targets consumed
|
|
244
444
|
}
|
|
245
|
-
|
|
246
|
-
const
|
|
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);
|
|
247
448
|
const isVariadic = (captureMarker === null || captureMarker === void 0 ? void 0 : captureMarker.variadicOptions) !== undefined;
|
|
248
449
|
if (isVariadic) {
|
|
249
450
|
// Variadic pattern: try different consumption amounts with backtracking
|
|
250
451
|
const variadicOptions = captureMarker.variadicOptions;
|
|
251
452
|
const min = (_a = variadicOptions === null || variadicOptions === void 0 ? void 0 : variadicOptions.min) !== null && _a !== void 0 ? _a : 0;
|
|
252
453
|
const max = (_b = variadicOptions === null || variadicOptions === void 0 ? void 0 : variadicOptions.max) !== null && _b !== void 0 ? _b : Infinity;
|
|
253
|
-
// Calculate maximum possible consumption
|
|
454
|
+
// Calculate maximum possible consumption and check if remaining patterns are deterministic
|
|
254
455
|
let nonVariadicRemainingPatterns = 0;
|
|
456
|
+
let allRemainingPatternsAreDeterministic = true;
|
|
255
457
|
for (let i = patternIdx + 1; i < patternElements.length; i++) {
|
|
256
|
-
const
|
|
257
|
-
const nextCaptureMarker = utils_1.PlaceholderUtils.getCaptureMarker(nextPatternElement);
|
|
458
|
+
const nextCaptureMarker = utils_1.PlaceholderUtils.getCaptureMarker(patternElements[i]);
|
|
258
459
|
const nextIsVariadic = (nextCaptureMarker === null || nextCaptureMarker === void 0 ? void 0 : nextCaptureMarker.variadicOptions) !== undefined;
|
|
259
460
|
if (!nextIsVariadic) {
|
|
260
461
|
nonVariadicRemainingPatterns++;
|
|
261
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
|
+
}
|
|
262
468
|
}
|
|
263
469
|
const remainingTargetElements = targetElements.length - targetIdx;
|
|
264
470
|
const maxPossible = Math.min(remainingTargetElements - nonVariadicRemainingPatterns, max);
|
|
@@ -266,21 +472,24 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
266
472
|
// This avoids unnecessary backtracking when constraints make the split point obvious
|
|
267
473
|
let pivotDetected = false;
|
|
268
474
|
let pivotAt = -1;
|
|
269
|
-
|
|
270
|
-
|
|
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];
|
|
271
480
|
// Scan through possible consumption amounts starting from min
|
|
272
481
|
for (let tryConsume = min; tryConsume <= maxPossible; tryConsume++) {
|
|
273
482
|
// Check if element after our consumption would match next pattern
|
|
274
483
|
if (targetIdx + tryConsume < targetElements.length) {
|
|
275
|
-
const candidateElement = targetElements[targetIdx + tryConsume]
|
|
484
|
+
const candidateElement = targetElements[targetIdx + tryConsume];
|
|
276
485
|
// Skip J.Empty for arguments
|
|
277
|
-
if (filterEmpty && candidateElement.kind === java_1.J.Kind.Empty) {
|
|
486
|
+
if (filterEmpty && candidateElement.element.kind === java_1.J.Kind.Empty) {
|
|
278
487
|
continue;
|
|
279
488
|
}
|
|
280
489
|
// Test if next pattern matches this element
|
|
281
490
|
const savedMatch = this.match;
|
|
282
491
|
const savedState = this.matcher.saveState();
|
|
283
|
-
yield this.
|
|
492
|
+
yield this.visitRightPadded(nextPattern, candidateElement);
|
|
284
493
|
const matchesNext = this.match;
|
|
285
494
|
this.match = savedMatch;
|
|
286
495
|
this.matcher.restoreState(savedState);
|
|
@@ -293,10 +502,15 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
293
502
|
}
|
|
294
503
|
}
|
|
295
504
|
}
|
|
296
|
-
//
|
|
297
|
-
// If pivot detected, try that first; otherwise use greedy approach (max to min)
|
|
505
|
+
// Determine consumption order
|
|
298
506
|
const consumptionOrder = [];
|
|
299
|
-
|
|
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) {
|
|
300
514
|
// Try pivot first, then others as fallback
|
|
301
515
|
consumptionOrder.push(pivotAt);
|
|
302
516
|
for (let c = maxPossible; c >= min; c--) {
|
|
@@ -313,26 +527,29 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
313
527
|
}
|
|
314
528
|
for (const consume of consumptionOrder) {
|
|
315
529
|
// Capture elements for this consumption amount
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
// For statements, include all elements
|
|
322
|
-
if (!filterEmpty || element.kind !== java_1.J.Kind.Empty) {
|
|
323
|
-
capturedWrappers.push(wrapped);
|
|
324
|
-
}
|
|
325
|
-
}
|
|
326
|
-
// Extract just the elements for the constraint check
|
|
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;
|
|
327
535
|
const capturedElements = capturedWrappers.map(w => w.element);
|
|
328
|
-
//
|
|
536
|
+
// Check min/max constraints against filtered elements
|
|
329
537
|
if (capturedElements.length < min || capturedElements.length > max) {
|
|
330
|
-
continue;
|
|
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
|
+
}
|
|
331
548
|
}
|
|
332
549
|
// Save current state for backtracking
|
|
333
550
|
const savedState = this.matcher.saveState();
|
|
334
551
|
// Handle the variadic capture
|
|
335
|
-
const success = this.matcher.handleVariadicCapture(
|
|
552
|
+
const success = this.matcher.handleVariadicCapture(captureMarker, capturedElements, capturedWrappers);
|
|
336
553
|
if (!success) {
|
|
337
554
|
// Restore state and try next amount
|
|
338
555
|
this.matcher.restoreState(savedState);
|
|
@@ -359,35 +576,646 @@ class PatternMatchingComparator extends comparator_1.JavaScriptSemanticComparato
|
|
|
359
576
|
if (filterEmpty && targetElement.kind === java_1.J.Kind.Empty) {
|
|
360
577
|
return false;
|
|
361
578
|
}
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
//
|
|
366
|
-
|
|
367
|
-
|
|
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;
|
|
368
769
|
try {
|
|
369
|
-
|
|
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;
|
|
370
785
|
}
|
|
371
786
|
finally {
|
|
372
|
-
this.
|
|
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;
|
|
373
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);
|
|
374
1067
|
if (!this.match) {
|
|
1068
|
+
// Preserve explanation before restoring state
|
|
1069
|
+
const explanation = this.debug.getExplanation();
|
|
375
1070
|
// Restore state on match failure
|
|
376
1071
|
this.match = savedMatch;
|
|
377
1072
|
this.matcher.restoreState(savedState);
|
|
1073
|
+
// Restore the explanation if one was set during matching
|
|
1074
|
+
if (explanation) {
|
|
1075
|
+
this.debug.restoreExplanation(explanation);
|
|
1076
|
+
}
|
|
378
1077
|
return false;
|
|
379
1078
|
}
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
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();
|
|
385
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
|
+
}
|
|
386
1200
|
}
|
|
387
|
-
return
|
|
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);
|
|
388
1216
|
}
|
|
389
1217
|
});
|
|
390
1218
|
}
|
|
391
1219
|
}
|
|
392
|
-
exports.
|
|
1220
|
+
exports.DebugPatternMatchingComparator = DebugPatternMatchingComparator;
|
|
393
1221
|
//# sourceMappingURL=comparator.js.map
|