@@ -117,4 +117,352 @@ describe('Inner Class Validation', () => {
117
117
118
118
expect ( innerClassErrors . length ) . toBe ( 0 ) ;
119
119
} ) ;
120
+
121
+ it ( 'should parse ClassWithVirtualInnerClass with complex inner class hierarchy' , ( ) => {
122
+ const fileContent = `
123
+ public class ClassWithVirtualInnerClass{
124
+
125
+ public virtual class VirtualInnerClass extends Metadata {
126
+
127
+ public String color;
128
+
129
+ public Boolean default_x;
130
+
131
+ public String description;
132
+
133
+ public Boolean isActive;
134
+
135
+ }
136
+
137
+ public class InnerClassExtendsVirtualClass extends VirtualInnerClass {
138
+
139
+ public String type = 'InnerClassExtendsVirtualClass';
140
+
141
+ public String fullName;
142
+
143
+ private String[] fullName_type_info = new String[]{
144
+ 'fullName','SOAP_M_URI',null,'0','1','false'
145
+ };
146
+
147
+ public String color;
148
+
149
+ public Boolean default_x;
150
+
151
+ public String description;
152
+
153
+ public Boolean isActive;
154
+
155
+ private String[] color_type_info = new String[]{
156
+ 'color','SOAP_M_URI',null,'0','1','false'
157
+ };
158
+
159
+ private String[] default_x_type_info = new String[]{
160
+ 'default','SOAP_M_URI',null,'1','1','false'
161
+ };
162
+
163
+ private String[] description_type_info = new String[]{
164
+ 'description','SOAP_M_URI',null,'0','1','false'
165
+ };
166
+
167
+ private String[] isActive_type_info = new String[]{
168
+ 'isActive','SOAP_M_URI',null,'0','1','false'
169
+ };
170
+
171
+ public Boolean allowEmail;
172
+
173
+ public Boolean closed;
174
+
175
+ public String[] controllingFieldValues;
176
+
177
+ public Boolean converted;
178
+
179
+ public Boolean cssExposed;
180
+
181
+ public String forecastCategory;
182
+
183
+ public Boolean highPriority;
184
+
185
+ public Integer probability;
186
+
187
+ public String reverseRole;
188
+
189
+ public Boolean reviewed;
190
+
191
+ public Boolean won;
192
+
193
+ private String[] allowEmail_type_info = new String[]{
194
+ 'allowEmail','SOAP_M_URI',null,'0','1','false'
195
+ };
196
+
197
+ private String[] closed_type_info = new String[]{
198
+ 'closed','SOAP_M_URI',null,'0','1','false'
199
+ };
200
+
201
+ private String[] controllingFieldValues_type_info = new String[]{
202
+ 'controllingFieldValues','SOAP_M_URI',null,'0','-1','false'
203
+ };
204
+
205
+ private String[] converted_type_info = new String[]{
206
+ 'converted','SOAP_M_URI',null,'0','1','false'
207
+ };
208
+
209
+ private String[] cssExposed_type_info = new String[]{
210
+ 'cssExposed','SOAP_M_URI',null,'0','1','false'
211
+ };
212
+
213
+ private String[] forecastCategory_type_info = new String[]{
214
+ 'forecastCategory','SOAP_M_URI',null,'0','1','false'
215
+ };
216
+
217
+ private String[] highPriority_type_info = new String[]{
218
+ 'highPriority','SOAP_M_URI',null,'0','1','false'
219
+ };
220
+
221
+ private String[] probability_type_info = new String[]{
222
+ 'probability','SOAP_M_URI',null,'0','1','false'
223
+ };
224
+
225
+ private String[] reverseRole_type_info = new String[]{
226
+ 'reverseRole','SOAP_M_URI',null,'0','1','false'
227
+ };
228
+
229
+ private String[] reviewed_type_info = new String[]{
230
+ 'reviewed','SOAP_M_URI',null,'0','1','false'
231
+ };
232
+
233
+ private String[] won_type_info = new String[]{
234
+ 'won','SOAP_M_URI',null,'0','1','false'
235
+ };
236
+
237
+ private String[] apex_schema_type_info = new String[]{
238
+ 'SOAP_M_URI','true','false'
239
+ };
240
+
241
+ private String[] type_att_info = new String[]{'xsi:type'};
242
+
243
+ private String[] field_order_type_info = new String[]{
244
+ 'fullName','color','default_x','description','isActive',
245
+ 'allowEmail','closed','controllingFieldValues','converted',
246
+ 'cssExposed','forecastCategory','highPriority','probability',
247
+ 'reverseRole','reviewed','won'
248
+ };
249
+
250
+ }
251
+
252
+ public virtual class Metadata {
253
+
254
+ public String fullName;
255
+
256
+ }
257
+
258
+ }
259
+ ` ;
260
+
261
+ const result : CompilationResult < SymbolTable > = compilerService . compile (
262
+ fileContent ,
263
+ 'ClassWithVirtualInnerClass.cls' ,
264
+ listener ,
265
+ ) ;
266
+
267
+ // Check for any semantic errors related to inner classes
268
+ const innerClassErrors = result . errors . filter (
269
+ ( e ) =>
270
+ e . type === ErrorType . Semantic &&
271
+ ( e . message . includes ( 'inner class' ) ||
272
+ e . message . includes ( 'Inner class' ) ) ,
273
+ ) ;
274
+
275
+ expect ( innerClassErrors . length ) . toBe ( 0 ) ;
276
+
277
+ // Verify that the parser successfully created the symbol table
278
+ expect ( result . result ) . toBeDefined ( ) ;
279
+ expect (
280
+ result . result ?. getCurrentScope ( ) . getAllSymbols ( ) . length ,
281
+ ) . toBeGreaterThan ( 0 ) ;
282
+ } ) ;
283
+ it ( 'should parse ClassWithVirtualInnerClass with complex inner class hierarchy w/errors' , ( ) => {
284
+ const fileContent = `
285
+ public class ClassWithVirtualInnerClass{
286
+
287
+ public virtial String badBunny;
288
+
289
+ public virtual class VirtualInnerClass extends Metadata {
290
+
291
+ public String color;
292
+
293
+ public Boolean default_x;
294
+
295
+ public String description;
296
+
297
+ public Boolean isActive;
298
+
299
+ }
300
+
301
+ public class InnerClassExtendsVirtualClass extends VirtualInnerClass {
302
+
303
+ public String type = 'InnerClassExtendsVirtualClass';
304
+
305
+ public String fullName;
306
+
307
+ private String[] fullName_type_info = new String[]{
308
+ 'fullName','SOAP_M_URI',null,'0','1','false'
309
+ };
310
+
311
+ public String color;
312
+
313
+ public Boolean default_x;
314
+
315
+ public String description;
316
+
317
+ public Boolean isActive;
318
+
319
+ private String[] color_type_info = new String[]{
320
+ 'color','SOAP_M_URI',null,'0','1','false'
321
+ };
322
+
323
+ private String[] default_x_type_info = new String[]{
324
+ 'default','SOAP_M_URI',null,'1','1','false'
325
+ };
326
+
327
+ private String[] description_type_info = new String[]{
328
+ 'description','SOAP_M_URI',null,'0','1','false'
329
+ };
330
+
331
+ private String[] isActive_type_info = new String[]{
332
+ 'isActive','SOAP_M_URI',null,'0','1','false'
333
+ };
334
+
335
+ public Boolean allowEmail;
336
+
337
+ public Boolean closed;
338
+
339
+ public String[] controllingFieldValues;
340
+
341
+ public Boolean converted;
342
+
343
+ public Boolean cssExposed;
344
+
345
+ public String forecastCategory;
346
+
347
+ public Boolean highPriority;
348
+
349
+ public Integer probability;
350
+
351
+ public String reverseRole;
352
+
353
+ public Boolean reviewed;
354
+
355
+ public Boolean won;
356
+
357
+ private String[] allowEmail_type_info = new String[]{
358
+ 'allowEmail','SOAP_M_URI',null,'0','1','false'
359
+ };
360
+
361
+ private String[] closed_type_info = new String[]{
362
+ 'closed','SOAP_M_URI',null,'0','1','false'
363
+ };
364
+
365
+ private String[] controllingFieldValues_type_info = new String[]{
366
+ 'controllingFieldValues','SOAP_M_URI',null,'0','-1','false'
367
+ };
368
+
369
+ private String[] converted_type_info = new String[]{
370
+ 'converted','SOAP_M_URI',null,'0','1','false'
371
+ };
372
+
373
+ private String[] cssExposed_type_info = new String[]{
374
+ 'cssExposed','SOAP_M_URI',null,'0','1','false'
375
+ };
376
+
377
+ private String[] forecastCategory_type_info = new String[]{
378
+ 'forecastCategory','SOAP_M_URI',null,'0','1','false'
379
+ };
380
+
381
+ private String[] highPriority_type_info = new String[]{
382
+ 'highPriority','SOAP_M_URI',null,'0','1','false'
383
+ };
384
+
385
+ private String[] probability_type_info = new String[]{
386
+ 'probability','SOAP_M_URI',null,'0','1','false'
387
+ };
388
+
389
+ private String[] reverseRole_type_info = new String[]{
390
+ 'reverseRole','SOAP_M_URI',null,'0','1','false'
391
+ };
392
+
393
+ private String[] reviewed_type_info = new String[]{
394
+ 'reviewed','SOAP_M_URI',null,'0','1','false'
395
+ };
396
+
397
+ private String[] won_type_info = new String[]{
398
+ 'won','SOAP_M_URI',null,'0','1','false'
399
+ };
400
+
401
+ private String[] apex_schema_type_info = new String[]{
402
+ 'SOAP_M_URI','true','false'
403
+ };
404
+
405
+ private String[] type_att_info = new String[]{'xsi:type'};
406
+
407
+ private String[] field_order_type_info = new String[]{
408
+ 'fullName','color','default_x','description','isActive',
409
+ 'allowEmail','closed','controllingFieldValues','converted',
410
+ 'cssExposed','forecastCategory','highPriority','probability',
411
+ 'reverseRole','reviewed','won'
412
+ };
413
+
414
+ }
415
+
416
+ public virtual class Metadata {
417
+
418
+ public String fullName;
419
+
420
+ }
421
+
422
+ }
423
+ ` ;
424
+
425
+ const result : CompilationResult < SymbolTable > = compilerService . compile (
426
+ fileContent ,
427
+ 'ClassWithVirtualInnerClass.cls' ,
428
+ listener ,
429
+ ) ;
430
+
431
+ // Check for expected semantic errors
432
+ const virtialSyntaxErrors = result . errors . filter (
433
+ ( e ) =>
434
+ e . type === ErrorType . Semantic &&
435
+ e . message . includes ( 'Invalid syntax: virtial' ) ,
436
+ ) ;
437
+
438
+ const virtualFieldErrors = result . errors . filter (
439
+ ( e ) =>
440
+ e . type === ErrorType . Semantic &&
441
+ e . message . includes ( "Field cannot be declared as 'virtual'" ) ,
442
+ ) ;
443
+
444
+ const syntaxErrors = result . errors . filter (
445
+ ( e ) =>
446
+ e . type === ErrorType . Syntax &&
447
+ e . message . includes (
448
+ "no viable alternative at input 'virtial String badBunny'" ,
449
+ ) ,
450
+ ) ;
451
+
452
+ // Verify that the expected errors are present
453
+ expect ( virtialSyntaxErrors . length ) . toBe ( 1 ) ;
454
+ expect ( virtualFieldErrors . length ) . toBe ( 2 ) ;
455
+ expect ( syntaxErrors . length ) . toBe ( 1 ) ;
456
+ expect ( result . errors . length ) . toBe ( 4 ) ;
457
+
458
+ // Verify inner class structure is not affected by these errors
459
+ const innerClassErrors = result . errors . filter (
460
+ ( e ) =>
461
+ e . type === ErrorType . Semantic &&
462
+ ( e . message . includes ( 'inner class' ) ||
463
+ e . message . includes ( 'Inner class' ) ) ,
464
+ ) ;
465
+
466
+ expect ( innerClassErrors . length ) . toBe ( 0 ) ;
467
+ } ) ;
120
468
} ) ;
0 commit comments