Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 48 additions & 30 deletions src/main/org/codehaus/groovy/classgen/ExtendedVerifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -172,47 +172,65 @@ private void visitDeprecation(AnnotatedNode node, AnnotationNode visited) {
private void visitOverride(AnnotatedNode node, AnnotationNode visited) {
ClassNode annotationClassNode = visited.getClassNode();
if (annotationClassNode.isResolved() && annotationClassNode.getName().equals("java.lang.Override")) {
if (node instanceof MethodNode) {
if (node instanceof MethodNode && !Boolean.TRUE.equals(node.getNodeMetaData(Verifier.DEFAULT_PARAMETER_GENERATED))) {
boolean override = false;
MethodNode origMethod = (MethodNode) node;
ClassNode cNode = node.getDeclaringClass();
ClassNode next = cNode;
outer:
while (next != null) {
Map genericsSpec = createGenericsSpec(next);
MethodNode mn = correctToGenericsSpec(genericsSpec, origMethod);
if (next != cNode) {
ClassNode correctedNext = correctToGenericsSpecRecurse(genericsSpec, next);
MethodNode found = getDeclaredMethodCorrected(genericsSpec, mn, correctedNext);
if (found != null) break;
}
List<ClassNode> ifaces = new ArrayList<ClassNode>();
ifaces.addAll(Arrays.asList(next.getInterfaces()));
Map updatedGenericsSpec = new HashMap(genericsSpec);
while (!ifaces.isEmpty()) {
ClassNode origInterface = ifaces.remove(0);
if (!origInterface.equals(ClassHelper.OBJECT_TYPE)) {
updatedGenericsSpec = createGenericsSpec(origInterface, updatedGenericsSpec);
ClassNode iNode = correctToGenericsSpecRecurse(updatedGenericsSpec, origInterface);
MethodNode found2 = getDeclaredMethodCorrected(updatedGenericsSpec, mn, iNode);
if (found2 != null) break outer;
ifaces.addAll(Arrays.asList(iNode.getInterfaces()));
ClassNode cNode = origMethod.getDeclaringClass();
if (origMethod.hasDefaultValue()) {
List<MethodNode> variants = cNode.getDeclaredMethods(origMethod.getName());
for (MethodNode m : variants) {
if (m.getAnnotations().contains(visited) && isOverrideMethod(m)) {
override = true;
break;
}
}
ClassNode superClass = next.getUnresolvedSuperClass();
if (superClass!=null) {
next = correctToGenericsSpecRecurse(updatedGenericsSpec, superClass);
} else {
next = null;
}
} else {
override = isOverrideMethod(origMethod);
}
if (next == null) {

if (!override) {
addError("Method '" + origMethod.getName() + "' from class '" + cNode.getName() + "' does not override " +
"method from its superclass or interfaces but is annotated with @Override.", visited);
}
}
}
}

private boolean isOverrideMethod(MethodNode method) {
ClassNode cNode = method.getDeclaringClass();
ClassNode next = cNode;
outer:
while (next != null) {
Map genericsSpec = createGenericsSpec(next);
MethodNode mn = correctToGenericsSpec(genericsSpec, method);
if (next != cNode) {
ClassNode correctedNext = correctToGenericsSpecRecurse(genericsSpec, next);
MethodNode found = getDeclaredMethodCorrected(genericsSpec, mn, correctedNext);
if (found != null) break;
}
List<ClassNode> ifaces = new ArrayList<ClassNode>();
ifaces.addAll(Arrays.asList(next.getInterfaces()));
Map updatedGenericsSpec = new HashMap(genericsSpec);
while (!ifaces.isEmpty()) {
ClassNode origInterface = ifaces.remove(0);
if (!origInterface.equals(ClassHelper.OBJECT_TYPE)) {
updatedGenericsSpec = createGenericsSpec(origInterface, updatedGenericsSpec);
ClassNode iNode = correctToGenericsSpecRecurse(updatedGenericsSpec, origInterface);
MethodNode found2 = getDeclaredMethodCorrected(updatedGenericsSpec, mn, iNode);
if (found2 != null) break outer;
ifaces.addAll(Arrays.asList(iNode.getInterfaces()));
}
}
ClassNode superClass = next.getUnresolvedSuperClass();
if (superClass != null) {
next = correctToGenericsSpecRecurse(updatedGenericsSpec, superClass);
} else {
next = null;
}
}
return next != null;
}

private MethodNode getDeclaredMethodCorrected(Map genericsSpec, MethodNode mn, ClassNode correctedNext) {
for (MethodNode orig : correctedNext.getDeclaredMethods(mn.getName())) {
MethodNode method = correctToGenericsSpec(genericsSpec, orig);
Expand Down
2 changes: 2 additions & 0 deletions src/main/org/codehaus/groovy/classgen/Verifier.java
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public class Verifier implements GroovyClassVisitor, Opcodes {
public static final String STATIC_METACLASS_BOOL = "__$stMC";
public static final String SWAP_INIT = "__$swapInit";
public static final String INITIAL_EXPRESSION = "INITIAL_EXPRESSION";
public static final String DEFAULT_PARAMETER_GENERATED = "DEFAULT_PARAMETER_GENERATED";

// NOTE: timeStamp constants shouldn't belong to Verifier but kept here
// for binary compatibility
Expand Down Expand Up @@ -809,6 +810,7 @@ public void visitVariableExpression(VariableExpression expression) {
}
addPropertyMethod(newMethod);
newMethod.setGenericsTypes(method.getGenericsTypes());
newMethod.putNodeMetaData(DEFAULT_PARAMETER_GENERATED, true);
}
});
}
Expand Down
32 changes: 32 additions & 0 deletions src/test/groovy/OverrideTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,36 @@ def d = new Derived()
"""
assert message.contains("Method 'methodTakesObject' from class 'HasMethodWithBadArgType' does not override method from its superclass or interfaces but is annotated with @Override.")
}

void testOverrideOnMethodWithDefaultParameters() {
assertScript '''
interface TemplatedInterface {
String execute(Map argument)
}

class TemplatedInterfaceImplementation implements TemplatedInterface {
@Override
String execute(Map argument = [:]) {
return null
}
}
new TemplatedInterfaceImplementation()
'''
}

void testOverrideOnMethodWithDefaultParametersVariant() {
assertScript '''
interface TemplatedInterface {
String execute(Map argument)
}

class TemplatedInterfaceImplementation implements TemplatedInterface {
@Override
String execute(Map argument, String foo = null) {
return foo
}
}
new TemplatedInterfaceImplementation()
'''
}
}