21
21
import groovy .transform .Sealed ;
22
22
import org .apache .groovy .ast .tools .AnnotatedNodeUtils ;
23
23
import org .apache .groovy .ast .tools .ClassNodeUtils ;
24
+ import org .apache .groovy .ast .tools .MethodNodeUtils ;
24
25
import org .codehaus .groovy .ast .ASTNode ;
25
26
import org .codehaus .groovy .ast .ClassCodeVisitorSupport ;
26
27
import org .codehaus .groovy .ast .ClassHelper ;
55
56
import java .util .LinkedHashSet ;
56
57
import java .util .List ;
57
58
import java .util .Map ;
59
+ import java .util .Set ;
58
60
59
61
import static java .lang .reflect .Modifier .isFinal ;
60
62
import static java .lang .reflect .Modifier .isNative ;
65
67
import static java .lang .reflect .Modifier .isSynchronized ;
66
68
import static java .lang .reflect .Modifier .isTransient ;
67
69
import static java .lang .reflect .Modifier .isVolatile ;
70
+ import static org .codehaus .groovy .ast .tools .GenericsUtils .addMethodGenerics ;
71
+ import static org .codehaus .groovy .ast .tools .GenericsUtils .buildWildcardType ;
72
+ import static org .codehaus .groovy .ast .tools .GenericsUtils .correctToGenericsSpecRecurse ;
73
+ import static org .codehaus .groovy .ast .tools .GenericsUtils .createGenericsSpec ;
68
74
import static org .objectweb .asm .Opcodes .ACC_ABSTRACT ;
69
75
import static org .objectweb .asm .Opcodes .ACC_FINAL ;
70
76
import static org .objectweb .asm .Opcodes .ACC_NATIVE ;
@@ -123,6 +129,7 @@ public void visitClass(final ClassNode node) {
123
129
checkMethodsForIncorrectName (node );
124
130
checkMethodsForWeakerAccess (node );
125
131
checkMethodsForOverridingFinal (node );
132
+ checkMethodsForOverridingIssue (node );
126
133
checkNoAbstractMethodsNonAbstractClass (node );
127
134
checkClassExtendsOrImplementsSelfTypes (node );
128
135
checkNoStaticMethodWithSameSignatureAsNonStatic (node );
@@ -279,7 +286,7 @@ private static String getDescription(final ClassNode node) {
279
286
}
280
287
281
288
private static String getDescription (final MethodNode node ) {
282
- return "method '" + node . getTypeDescriptor ( ) + "'" ;
289
+ return "method '" + MethodNodeUtils . methodDescriptor ( node , true ) + "'" ;
283
290
}
284
291
285
292
private static String getDescription (final FieldNode node ) {
@@ -299,7 +306,7 @@ private void checkAbstractDeclaration(final MethodNode methodNode) {
299
306
300
307
addError ("Can't have an abstract method in a non-abstract class." +
301
308
" The " + getDescription (currentClass ) + " must be declared abstract or the method '" +
302
- methodNode . getTypeDescriptor ( ) + "' must not be abstract." , methodNode );
309
+ MethodNodeUtils . methodDescriptor ( methodNode , true ) + "' must not be abstract." , methodNode );
303
310
}
304
311
305
312
private void checkClassForExtendingFinalOrSealed (final ClassNode cn ) {
@@ -404,28 +411,97 @@ private void checkMethodsForWeakerAccess(final ClassNode cn) {
404
411
}
405
412
406
413
private void checkMethodsForOverridingFinal (final ClassNode cn ) {
414
+ final int skips = ACC_SYNTHETIC | ACC_STATIC | ACC_PRIVATE ;
407
415
for (MethodNode method : cn .getMethods ()) {
408
- if ((method .getModifiers () & ACC_SYNTHETIC ) != 0 ) continue ; // GROOVY-11579: bridge method
416
+ if ((method .getModifiers () & skips ) != 0 ) continue ; // GROOVY-11579
409
417
410
- ClassNode sc = cn .getSuperClass ();
411
418
Parameter [] params = method .getParameters ();
412
- for (MethodNode superMethod : sc .getMethods (method .getName ())) {
413
- if (superMethod .isFinal ()
419
+ for (MethodNode superMethod : cn . getSuperClass () .getMethods (method .getName ())) {
420
+ if (( superMethod .getModifiers () & skips + ACC_FINAL ) == ACC_FINAL
414
421
&& ParameterUtils .parametersEqual (params , superMethod .getParameters ())) {
415
422
StringBuilder sb = new StringBuilder ();
416
423
sb .append ("You are not allowed to override the final method " );
417
- sb .append (method .getName ());
424
+ if (method .getName ().contains (" " )) {
425
+ sb .append ('"' ).append (method .getName ()).append ('"' );
426
+ } else {
427
+ sb .append (method .getName ());
428
+ }
418
429
appendParamsDescription (params , sb );
419
430
sb .append (" from " );
420
- sb .append (getDescription (sc ));
431
+ sb .append (getDescription (superMethod . getDeclaringClass () ));
421
432
sb .append ("." );
422
433
423
434
addError (sb .toString (), method .getLineNumber () > 0 ? method : cn );
435
+ break ;
424
436
}
425
437
}
426
438
}
427
439
}
428
440
441
+ private void checkMethodsForOverridingIssue (final ClassNode cn ) {
442
+ Set <ClassNode > superTypes = getAllSuperTypes (cn );
443
+ superTypes .remove (ClassHelper .GROOVY_OBJECT_TYPE );
444
+ superTypes .remove (ClassHelper .OBJECT_TYPE );
445
+ if (superTypes .isEmpty ()) return ;
446
+
447
+ for (MethodNode mn : cn .getMethods ()) {
448
+ Parameter [] pa = mn .getParameters ();
449
+ if (pa .length == 0 || (mn .getModifiers () & ACC_SYNTHETIC + ACC_STATIC + ACC_PRIVATE ) != 0 ) continue ;
450
+
451
+ out : for (ClassNode sc : superTypes ) {
452
+ Map <String , ClassNode > cspec = createGenericsSpec (sc );
453
+ for (MethodNode sm : sc .getDeclaredMethods (mn .getName ())) {
454
+ if (!sm .isStatic () && !sm .isPrivate () && ParameterUtils .parametersEqual (pa , sm .getParameters ())) {
455
+ Map <String , ClassNode > mspec = addMethodGenerics (sm , cspec );
456
+ for (int i = 0 , n = pa .length ; i < n ; i += 1 ) {
457
+ var t0 = sm .getParameters ()[i ].getType ();
458
+ var t1 = pa [i ].getType ();
459
+ if (!t0 .isGenericsPlaceHolder () && !ClassHelper .isPrimitiveType (t0 )
460
+ && !t1 .isGenericsPlaceHolder () && !ClassHelper .isPrimitiveType (t1 )
461
+ && !buildWildcardType (t0 = correctToGenericsSpecRecurse (mspec , t0 )).isCompatibleWith (t1 )) {
462
+ StringBuilder sb = new StringBuilder ();
463
+ sb .append ("name clash: " );
464
+ if (mn .getName ().contains (" " )) {
465
+ sb .append ('"' ).append (mn .getName ()).append ('"' );
466
+ } else {
467
+ sb .append (mn .getName ());
468
+ }
469
+ appendParamsDescription (pa , sb );
470
+ sb .append (" in " );
471
+ sb .append (getDescription (cn ));
472
+ sb .append (" and " );
473
+ if (sm .getName ().contains (" " )) {
474
+ sb .append ('"' ).append (sm .getName ()).append ('"' );
475
+ } else {
476
+ sb .append (sm .getName ());
477
+ }
478
+ appendParamsDescription (sm .getParameters (), sb );
479
+ sb .append (" in " );
480
+ sb .append (getDescription (sc ));
481
+ sb .append (" have the same erasure, yet neither overrides the other." );
482
+
483
+ addError (sb .toString (), mn .getLineNumber () > 0 ? mn : cn );
484
+ break ;
485
+ }
486
+ }
487
+ break out ;
488
+ }
489
+ }
490
+ }
491
+ }
492
+ }
493
+
494
+ private static Set <ClassNode > getAllSuperTypes (ClassNode cn ) {
495
+ Set <ClassNode > interfaces = GeneralUtils .getInterfacesAndSuperInterfaces (cn );
496
+ Set <ClassNode > superTypes = new LinkedHashSet <>();
497
+ interfaces .remove (cn );
498
+ while ((cn = cn .getSuperClass ()) != null ) {
499
+ superTypes .add (cn );
500
+ }
501
+ superTypes .addAll (interfaces );
502
+ return superTypes ;
503
+ }
504
+
429
505
private void appendParamsDescription (final Parameter [] parameters , final StringBuilder msg ) {
430
506
msg .append ('(' );
431
507
boolean needsComma = false ;
@@ -435,7 +511,7 @@ private void appendParamsDescription(final Parameter[] parameters, final StringB
435
511
} else {
436
512
needsComma = true ;
437
513
}
438
- msg .append (parameter .getType ());
514
+ msg .append (parameter .getType (). toString ( false ) );
439
515
}
440
516
msg .append (')' );
441
517
}
0 commit comments