Repository: groovy
Updated Branches:
  refs/heads/master 4738970c6 -> 3d60dbd79


GROOVY-7620: No error if abstract getter is not implemented but static field 
exists (closes #342)


Project: http://git-wip-us.apache.org/repos/asf/groovy/repo
Commit: http://git-wip-us.apache.org/repos/asf/groovy/commit/973197fd
Tree: http://git-wip-us.apache.org/repos/asf/groovy/tree/973197fd
Diff: http://git-wip-us.apache.org/repos/asf/groovy/diff/973197fd

Branch: refs/heads/master
Commit: 973197fdb5e27df579f5ae9b918d3fa13213f482
Parents: 4738970
Author: paulk <pa...@asert.com.au>
Authored: Tue May 31 21:14:54 2016 +1000
Committer: paulk <pa...@asert.com.au>
Committed: Wed Jun 1 13:20:06 2016 +1000

----------------------------------------------------------------------
 src/main/org/codehaus/groovy/ast/ClassNode.java | 23 ++++---
 .../classgen/ClassCompletionVerifier.java       | 71 +++++++++++---------
 src/test/groovy/bugs/Groovy7081Bug.groovy       |  2 +-
 src/test/groovy/bugs/Groovy7620Bug.groovy       | 59 ++++++++++++++++
 4 files changed, 114 insertions(+), 41 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/groovy/blob/973197fd/src/main/org/codehaus/groovy/ast/ClassNode.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/ast/ClassNode.java 
b/src/main/org/codehaus/groovy/ast/ClassNode.java
index 6601ac7..68ac22d 100644
--- a/src/main/org/codehaus/groovy/ast/ClassNode.java
+++ b/src/main/org/codehaus/groovy/ast/ClassNode.java
@@ -425,30 +425,33 @@ public class ClassNode extends AnnotatedNode implements 
Opcodes {
     public Map<String, MethodNode> getDeclaredMethodsMap() {
         // Start off with the methods from the superclass.
         ClassNode parent = getSuperClass();
-        Map<String, MethodNode> result = null;
+        Map<String, MethodNode> result;
         if (parent != null) {
             result = parent.getDeclaredMethodsMap();
         } else {
             result = new HashMap<String, MethodNode>();
         }
+        addInterfaceMethods(result);
 
+        // And add in the methods implemented in this class.
+        for (MethodNode method : getMethods()) {
+            String sig = method.getTypeDescriptor();
+            result.put(sig, method);
+        }
+        return result;
+    }
+
+    public void addInterfaceMethods(Map<String, MethodNode> methodsMap) {
         // add in unimplemented abstract methods from the interfaces
         for (ClassNode iface : getInterfaces()) {
             Map<String, MethodNode> ifaceMethodsMap = 
iface.getDeclaredMethodsMap();
             for (String methSig : ifaceMethodsMap.keySet()) {
-                if (!result.containsKey(methSig)) {
+                if (!methodsMap.containsKey(methSig)) {
                     MethodNode methNode = ifaceMethodsMap.get(methSig);
-                    result.put(methSig, methNode);
+                    methodsMap.put(methSig, methNode);
                 }
             }
         }
-
-        // And add in the methods implemented in this class.
-        for (MethodNode method : getMethods()) {
-            String sig = method.getTypeDescriptor();
-            result.put(sig, method);
-        }
-        return result;
     }
 
     public String getName() {

http://git-wip-us.apache.org/repos/asf/groovy/blob/973197fd/src/main/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
----------------------------------------------------------------------
diff --git a/src/main/org/codehaus/groovy/classgen/ClassCompletionVerifier.java 
b/src/main/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
index 47e3175..c29c705 100644
--- a/src/main/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
+++ b/src/main/org/codehaus/groovy/classgen/ClassCompletionVerifier.java
@@ -84,33 +84,48 @@ public class ClassCompletionVerifier extends 
ClassCodeVisitorSupport {
     }
 
     private void checkNoStaticMethodWithSameSignatureAsNonStatic(final 
ClassNode node) {
-        Map<String, MethodNode> result = new HashMap<String, MethodNode>();
-        // add in unimplemented abstract methods from the interfaces
-        for (ClassNode iface : node.getInterfaces()) {
-            Map<String, MethodNode> ifaceMethodsMap = 
iface.getDeclaredMethodsMap();
-            for (String methSig : ifaceMethodsMap.keySet()) {
-                if (!result.containsKey(methSig)) {
-                    MethodNode methNode = ifaceMethodsMap.get(methSig);
-                    result.put(methSig, methNode);
-                }
-            }
+        ClassNode parent = node.getSuperClass();
+        Map<String, MethodNode> result;
+        // start with methods from the parent if any
+        if (parent != null) {
+            result = parent.getDeclaredMethodsMap();
+        } else {
+            result = new HashMap<String, MethodNode>();
         }
+        // add in unimplemented abstract methods from the interfaces
+        node.addInterfaceMethods(result);
         for (MethodNode methodNode : node.getMethods()) {
             MethodNode mn = result.get(methodNode.getTypeDescriptor());
-            if (mn!=null && methodNode.isStatic() && 
!methodNode.isStaticConstructor()) {
+            if (mn != null && (mn.isStatic() ^ methodNode.isStatic()) && 
!methodNode.isStaticConstructor()) {
+                if (!mn.isAbstract()) continue;
                 ClassNode declaringClass = mn.getDeclaringClass();
                 ClassNode cn = declaringClass.getOuterClass();
-                if (cn==null && declaringClass.isResolved()) {
+                if (cn == null && declaringClass.isResolved()) {
                     // in case of a precompiled class, the outerclass is 
unknown
                     Class typeClass = declaringClass.getTypeClass();
                     typeClass = typeClass.getEnclosingClass();
-                    if (typeClass!=null) {
+                    if (typeClass != null) {
                         cn = ClassHelper.make(typeClass);
                     }
                 }
-                if (cn==null || !Traits.isTrait(cn)) {
-                    addError("Method '" + mn.getName() + "' is already defined 
in " + getDescription(node) + ". You cannot have " +
-                            "both a static and a non static method with the 
same signature", methodNode);
+                if (cn == null || !Traits.isTrait(cn)) {
+                    ASTNode errorNode = methodNode;
+                    String name = mn.getName();
+                    if (errorNode.getLineNumber() == -1) {
+                        // try to get a better error message location based on 
the property
+                        for (PropertyNode propertyNode : node.getProperties()) 
{
+                            if (name.startsWith("set") || 
name.startsWith("get") || name.startsWith("is")) {
+                                String propName = 
Verifier.capitalize(propertyNode.getField().getName());
+                                String shortName = 
name.substring(name.startsWith("is") ? 2 : 3);
+                                if (propName.equals(shortName)) {
+                                    errorNode = propertyNode;
+                                    break;
+                                }
+                            }
+                        }
+                    }
+                    addError("The " + getDescription(methodNode) + " is 
already defined in " + getDescription(node) +
+                            ". You cannot have both a static and an instance 
method with the same signature", errorNode);
                 }
             }
             result.put(methodNode.getTypeDescriptor(), methodNode);
@@ -307,6 +322,13 @@ public class ClassCompletionVerifier extends 
ClassCodeVisitorSupport {
     private void addInvalidUseOfFinalError(MethodNode method, Parameter[] 
parameters, ClassNode superCN) {
         StringBuilder msg = new StringBuilder();
         msg.append("You are not allowed to override the final method 
").append(method.getName());
+        appendParamsDescription(parameters, msg);
+        msg.append(" from ").append(getDescription(superCN));
+        msg.append(".");
+        addError(msg.toString(), method);
+    }
+
+    private void appendParamsDescription(Parameter[] parameters, StringBuilder 
msg) {
         msg.append("(");
         boolean needsComma = false;
         for (Parameter parameter : parameters) {
@@ -317,25 +339,14 @@ public class ClassCompletionVerifier extends 
ClassCodeVisitorSupport {
             }
             msg.append(parameter.getType());
         }
-        msg.append(") from ").append(getDescription(superCN));
-        msg.append(".");
-        addError(msg.toString(), method);
+        msg.append(")");
     }
 
     private void addWeakerAccessError(ClassNode cn, MethodNode method, 
Parameter[] parameters, MethodNode superMethod) {
         StringBuilder msg = new StringBuilder();
         msg.append(method.getName());
-        msg.append("(");
-        boolean needsComma = false;
-        for (Parameter parameter : parameters) {
-            if (needsComma) {
-                msg.append(",");
-            } else {
-                needsComma = true;
-            }
-            msg.append(parameter.getType());
-        }
-        msg.append(") in ");
+        appendParamsDescription(parameters, msg);
+        msg.append(" in ");
         msg.append(cn.getName());
         msg.append(" cannot override ");
         msg.append(superMethod.getName());

http://git-wip-us.apache.org/repos/asf/groovy/blob/973197fd/src/test/groovy/bugs/Groovy7081Bug.groovy
----------------------------------------------------------------------
diff --git a/src/test/groovy/bugs/Groovy7081Bug.groovy 
b/src/test/groovy/bugs/Groovy7081Bug.groovy
index ba6c44e..1415318 100644
--- a/src/test/groovy/bugs/Groovy7081Bug.groovy
+++ b/src/test/groovy/bugs/Groovy7081Bug.groovy
@@ -31,7 +31,7 @@ class Groovy7081Bug extends GroovyTestCase {
             new Foo().name
             '''
 
-        assert msg.contains("Method 'getName' is already defined in class 
'Foo'")
+        assert msg.contains("The method 'java.lang.String getName()' is 
already defined in class 'Foo'")
     }
 
     void testShouldSeeConflictInTypeSignature() {

http://git-wip-us.apache.org/repos/asf/groovy/blob/973197fd/src/test/groovy/bugs/Groovy7620Bug.groovy
----------------------------------------------------------------------
diff --git a/src/test/groovy/bugs/Groovy7620Bug.groovy 
b/src/test/groovy/bugs/Groovy7620Bug.groovy
new file mode 100644
index 0000000..44525ef
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy7620Bug.groovy
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+ */
+
+package groovy.bugs
+
+class Groovy7620Bug extends GroovyTestCase {
+    void testShouldSeeThatMethodIsNotImplemented() {
+        def msg = shouldFail '''
+            abstract class A {
+               abstract Object getFoo()
+
+               void test() {
+                   println getFoo()
+               }
+            }
+
+            class B extends A {
+               static Object foo
+            }
+
+            new B().test()
+            '''
+
+        assert msg.contains("The method 'java.lang.Object getFoo()' is already 
defined in class 'B'")
+    }
+
+    void testShouldSeeConflictInTypeSignature() {
+        def msg = shouldFail '''
+            interface C {
+               Object getFoo()
+            }
+
+            class D implements C {
+               static Object foo
+            }
+
+            new B().test()
+            '''
+
+        assert msg.contains("The method 'java.lang.Object getFoo()' is already 
defined in class 'D'")
+    }
+}

Reply via email to