Index: groovy-git/src/main/org/codehaus/groovy/control/ResolveVisitor.java
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
<+>/*\n * Copyright 2003-2010 the original author or authors.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *     http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage org.codehaus.groovy.control;\n\nimport groovy.lang.GroovyClassLoader;\nimport org.codehaus.groovy.ast.*;\nimport org.codehaus.groovy.ast.expr.*;\nimport org.codehaus.groovy.ast.stmt.BlockStatement;\nimport org.codehaus.groovy.ast.stmt.CatchStatement;\nimport org.codehaus.groovy.ast.stmt.ForStatement;\nimport org.codehaus.groovy.ast.stmt.Statement;\nimport org.codehaus.groovy.classgen.Verifier;\nimport org.codehaus.groovy.control.messages.ExceptionMessage;\nimport org.codehaus.groovy.syntax.Types;\nimport org.codehaus.groovy.GroovyBugError;\nimport org.objectweb.asm.Opcodes;\n\nimport java.io.File;\nimport java.io.IOException;\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.RetentionPolicy;\nimport java.lang.reflect.Modifier;\nimport java.net.MalformedURLException;\nimport java.net.URL;\nimport java.net.URLConnection;\nimport java.util.*;\n\n/**\n * Visitor to resolve Types and convert VariableExpression to\n * ClassExpressions if needed. The ResolveVisitor will try to\n * find the Class for a ClassExpression and prints an error if\n * it fails to do so. Constructions like C[], foo as C, (C) foo\n * will force creation of a ClassExpression for C\n * <p/>\n * Note: the method to start the resolving is  startResolving(ClassNode, SourceUnit).\n *\n * @author Jochen Theodorou\n * @author Roshan Dawrani\n * @author Alex Tkachman\n */\npublic class ResolveVisitor extends ClassCodeExpressionTransformer {\n    private ClassNode currentClass;\n    // note: BigInteger and BigDecimal are also imported by default\n    public static final String[] DEFAULT_IMPORTS = {\"java.lang.\", \"java.io.\", \"java.net.\", \"java.util.\", \"groovy.lang.\", \"groovy.util.\"};\n    private CompilationUnit compilationUnit;\n    private Map cachedClasses = new HashMap();\n    private static final Object NO_CLASS = new Object();\n    private SourceUnit source;\n    private VariableScope currentScope;\n\n    private boolean isTopLevelProperty = true;\n    private boolean inPropertyExpression = false;\n    private boolean inClosure = false;\n\n    private Map<String, GenericsType> genericParameterNames = new HashMap<String, GenericsType>();\n    private Set<FieldNode> fieldTypesChecked = new HashSet<FieldNode>();\n    private boolean checkingVariableTypeInDeclaration = false;\n    private ImportNode currImportNode = null;\n    private MethodNode currentMethod;\n\n    /**\n     * we use ConstructedClassWithPackage to limit the resolving the compiler\n     * does when combining package names and class names. The idea\n     * that if we use a package, then we do not want to replace the\n     * '.' with a '$' for the package part, only for the class name\n     * part. There is also the case of a imported class, so this logic\n     * can't be done in these cases...\n     */\n    private static class ConstructedClassWithPackage extends ClassNode {\n        String prefix;\n        String className;\n        public ConstructedClassWithPackage(String pkg, String name) {\n            super(pkg+name, Opcodes.ACC_PUBLIC,ClassHelper.OBJECT_TYPE);\n            isPrimaryNode = false;\n            this.prefix = pkg;\n            this.className = name;\n        }\n        public String getName() {\n            if (redirect()!=this) return super.getName();\n            return prefix+className;\n        }\n        public boolean hasPackageName() {\n            if (redirect()!=this) return super.hasPackageName();\n            return className.indexOf('.')!=-1;\n        }\n        public String setName(String name) {\n            if (redirect()!=this) {\n                return super.setName(name);\n            } else {\n                throw new GroovyBugError(\"ConstructedClassWithPackage#setName should not be called\");\n            }\n        }\n    }\n\n     /**\n     * we use LowerCaseClass to limit the resolving the compiler\n     * does for vanilla names starting with a lower case letter. The idea\n     * that if we use a vanilla name with a lower case letter, that this\n     * is in most cases no class. If it is a class the class needs to be\n     * imported explicitly. The efffect is that in an expression like\n     * \"def foo = bar\" we do not have to use a loadClass call to check the\n     * name foo and bar for being classes. Instead we will ask the module\n     * for an alias for this name which is much faster.\n     */\n    private static class LowerCaseClass extends ClassNode {\n        String className;\n        public LowerCaseClass(String name) {\n            super(name, Opcodes.ACC_PUBLIC,ClassHelper.OBJECT_TYPE);\n            isPrimaryNode = false;\n            this.className = name;\n        }\n        public String getName() {\n            if (redirect()!=this) return super.getName();\n            return className;\n        }\n        public boolean hasPackageName() {\n            if (redirect()!=this) return super.hasPackageName();\n            return false;\n        }\n        public String setName(String name) {\n            if (redirect()!=this) {\n                return super.setName(name);\n            } else {\n                throw new GroovyBugError(\"ConstructedClassWithPackage#setName should not be called\");\n            }\n        }\n    }\n\n    public ResolveVisitor(CompilationUnit cu) {\n        compilationUnit = cu;\n    }\n\n    public void startResolving(ClassNode node, SourceUnit source) {\n        this.source = source;\n        visitClass(node);\n    }\n\n    protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {\n        VariableScope oldScope = currentScope;\n        currentScope = node.getVariableScope();\n        Map<String, GenericsType> oldPNames = genericParameterNames;\n        genericParameterNames = new HashMap<String, GenericsType>(genericParameterNames);\n\n        resolveGenericsHeader(node.getGenericsTypes());\n\n        Parameter[] paras = node.getParameters();\n        for (Parameter p : paras) {\n            p.setInitialExpression(transform(p.getInitialExpression()));\n            resolveOrFail(p.getType(), p.getType());\n            visitAnnotations(p);\n        }\n        ClassNode[] exceptions = node.getExceptions();\n        for (ClassNode t : exceptions) {\n            resolveOrFail(t, node);\n        }\n        resolveOrFail(node.getReturnType(), node);\n\n        MethodNode oldCurrentMethod = currentMethod;\n        currentMethod = node;\n        super.visitConstructorOrMethod(node, isConstructor);\n        currentMethod = oldCurrentMethod;\n\n        genericParameterNames = oldPNames;\n        currentScope = oldScope;\n    }\n\n    public void visitField(FieldNode node) {\n        ClassNode t = node.getType();\n        if(!fieldTypesChecked.contains(node)) {\n            resolveOrFail(t, node);\n        }\n        super.visitField(node);\n    }\n\n    public void visitProperty(PropertyNode node) {\n        ClassNode t = node.getType();\n        resolveOrFail(t, node);\n        super.visitProperty(node);\n        fieldTypesChecked.add(node.getField());\n    }\n\n    private boolean resolveToInner (ClassNode type) {\n        // we do not do our name mangling to find an inner class\n        // if the type is a ConstructedClassWithPackage, because in this case we\n        // are resolving the name at a different place already\n        if (type instanceof ConstructedClassWithPackage) return false;\n        String name = type.getName();\n        String saved = name;\n        while (true) {\n            int len = name.lastIndexOf('.');\n            if (len == -1) break;\n            name = name.substring(0,len) + \"$\" + name.substring(len+1);\n            type.setName(name);\n            if (resolve(type)) return true;\n        }\n        if(resolveToInnerEnum (type)) return true;\n        \n        type.setName(saved);\n        return false;\n    }\n\n    private boolean resolveToInnerEnum (ClassNode type) {\n        // GROOVY-3110: It may be an inner enum defined by this class itself, in which case it does not need to be\n        // explicitly qualified by the currentClass name\n        String name = type.getName();\n        if(currentClass != type && !name.contains(\".\") && type.getClass().equals(ClassNode.class)) {\n            type.setName(currentClass.getName() + \"$\" + name);\n            if (resolve(type)) return true;\n        }\n        return false;\n    }\n\n    private void resolveOrFail(ClassNode type, String msg, ASTNode node) {\n        if (resolve(type)) return;\n        if (resolveToInner(type)) return;\n        addError(\"unable to resolve class \" + type.getName() + \" \" + msg, node);\n    }\n\n    private void resolveOrFail(ClassNode type, ASTNode node, boolean prefereImports) {\n        resolveGenericsTypes(type.getGenericsTypes());\n        if (prefereImports && resolveAliasFromModule(type)) return;\n        resolveOrFail(type, node);\n    }\n\n    private void resolveOrFail(ClassNode type, ASTNode node) {\n        resolveOrFail(type, \"\", node);\n    }\n\n    private boolean resolve(ClassNode type) {\n        return resolve(type, true, true, true);\n    }\n\n    private boolean resolve(ClassNode type, boolean testModuleImports, boolean testDefaultImports, boolean testStaticInnerClasses) {\n        resolveGenericsTypes(type.getGenericsTypes());\n        if (type.isResolved() || type.isPrimaryClassNode()) return true;\n        if (type.isArray()) {\n            ClassNode element = type.getComponentType();\n            boolean resolved = resolve(element, testModuleImports, testDefaultImports, testStaticInnerClasses);\n            if (resolved) {\n                ClassNode cn = element.makeArray();\n                type.setRedirect(cn);\n            }\n            return resolved;\n        }\n\n        // test if vanilla name is current class name\n        if (currentClass == type) return true;\n\n        if (genericParameterNames.get(type.getName()) != null) {\n            GenericsType gt = genericParameterNames.get(type.getName());\n            type.setRedirect(gt.getType());\n            type.setGenericsTypes(new GenericsType[]{gt});\n            type.setGenericsPlaceHolder(true);\n            return true;\n        }\n\n        if (currentClass.getNameWithoutPackage().equals(type.getName())) {\n            type.setRedirect(currentClass);\n            return true;\n        }\n\n        return resolveNestedClass(type) ||\n                resolveFromModule(type, testModuleImports) ||\n                resolveFromCompileUnit(type) ||\n                resolveFromDefaultImports(type, testDefaultImports) ||\n                resolveFromStaticInnerClasses(type, testStaticInnerClasses) ||\n                resolveToClass(type);\n    }\n\n    private boolean resolveNestedClass(ClassNode type) {\n        // we have for example a class name A, are in class X\n        // and there is a nested class A$X. we want to be able \n        // to access that class directly, so A becomes a valid\n        // name in X.\n        // GROOVY-4043: Do this check up the hierarchy, if needed\n        Map<String, ClassNode> hierClasses = new LinkedHashMap<String, ClassNode>();\n        ClassNode val;\n        String name;\n        for(ClassNode classToCheck = currentClass; classToCheck != ClassHelper.OBJECT_TYPE; \n            classToCheck = classToCheck.getSuperClass()) {\n            if(classToCheck == null || hierClasses.containsKey(classToCheck.getName())) break;\n            hierClasses.put(classToCheck.getName(), classToCheck);\n        }\n        \n        for (ClassNode classToCheck : hierClasses.values()) {\n            name = classToCheck.getName()+\"$\"+type.getName();\n            val = ClassHelper.make(name);\n            if (resolveFromCompileUnit(val)) {\n                type.setRedirect(val);\n                return true;\n            }\n        }\n        \n        // another case we want to check here is if we are in a\n        // nested class A$B$C and want to access B without\n        // qualifying it by A.B. A alone will work, since that\n        // is the qualified (minus package) name of that class\n        // anyway. \n        \n        // That means if the current class is not an InnerClassNode\n        // there is nothing to be done.\n        if (!(currentClass instanceof InnerClassNode)) return false;\n        \n        // since we have B and want to get A we start with the most \n        // outer class, put them together and then see if that does\n        // already exist. In case of B from within A$B we are done \n        // after the first step already. In case of for example\n        // A.B.C.D.E.F and accessing E from F we test A$E=failed, \n        // A$B$E=failed, A$B$C$E=fail, A$B$C$D$E=success\n        \n        LinkedList<ClassNode> outerClasses = new LinkedList<ClassNode>();\n        ClassNode outer = currentClass.getOuterClass();\n        while (outer!=null) {\n            outerClasses.addFirst(outer);\n            outer = outer.getOuterClass();\n        }\n        // most outer class is now element 0\n        for (ClassNode testNode : outerClasses) {\n            name = testNode.getName()+\"$\"+type.getName();\n            val = ClassHelper.make(name);\n            if (resolveFromCompileUnit(val)) {\n                type.setRedirect(val);\n                return true;\n            }\n        }        \n        \n        return false;   \n    }\n\n    // NOTE: copied from GroovyClassLoader\n    private long getTimeStamp(Class cls) {\n        return Verifier.getTimestamp(cls);\n    }\n\n    // NOTE: copied from GroovyClassLoader\n    private boolean isSourceNewer(URL source, Class cls) {\n        try {\n            long lastMod;\n\n            // Special handling for file:// protocol, as getLastModified() often reports\n            // incorrect results (-1)\n            if (source.getProtocol().equals(\"file\")) {\n                // Coerce the file URL to a File\n                String path = source.getPath().replace('/', File.separatorChar).replace('|', ':');\n                File file = new File(path);\n                lastMod = file.lastModified();\n            } else {\n                URLConnection conn = source.openConnection();\n                lastMod = conn.getLastModified();\n                conn.getInputStream().close();\n            }\n            return lastMod > getTimeStamp(cls);\n        } catch (IOException e) {\n            // if the stream can't be opened, let's keep the old reference\n            return false;\n        }\n    }\n\n\n    private boolean resolveToScript(ClassNode type) {\n        String name = type.getName();\n\n        if (name.startsWith(\"java.\")) return type.isResolved();\n        //TODO: don't ignore inner static classes completely\n        if (name.indexOf('$') != -1) return type.isResolved();\n        ModuleNode module = currentClass.getModule();\n        if (module.hasPackageName() && name.indexOf('.') == -1) return type.isResolved();\n        // try to find a script from classpath\n        GroovyClassLoader gcl = compilationUnit.getClassLoader();\n        URL url = null;\n        try {\n            url = gcl.getResourceLoader().loadGroovySource(name);\n        } catch (MalformedURLException e) {\n            // fall through and let the URL be null\n        }\n        if (url != null) {\n            if (type.isResolved()) {\n                Class cls = type.getTypeClass();\n                // if the file is not newer we don't want to recompile\n                if (!isSourceNewer(url, cls)) return true;\n                // since we came to this, we want to recompile\n                cachedClasses.remove(type.getName());\n                type.setRedirect(null);\n            }\n            SourceUnit su = compilationUnit.addSource(url);\n            currentClass.getCompileUnit().addClassNodeToCompile(type, su);\n            return true;\n        }\n        // type may be resolved through the classloader before\n        return type.isResolved();\n    }\n\n    private String replaceLastPoint(String name) {\n        int lastPoint = name.lastIndexOf('.');\n        name = new StringBuffer()\n                .append(name.substring(0, lastPoint))\n                .append(\"$\")\n                .append(name.substring(lastPoint + 1))\n                .toString();\n        return name;\n    }\n\n    private boolean resolveFromStaticInnerClasses(ClassNode type, boolean testStaticInnerClasses) {\n        // a class consisting of a vanilla name can never be\n        // a static inner class, because at least one dot is\n        // required for this. Example: foo.bar -> foo$bar\n        if (type instanceof LowerCaseClass) return false;\n\n        // try to resolve a public static inner class' name\n        testStaticInnerClasses &= type.hasPackageName();\n        if (testStaticInnerClasses) {\n            if (type instanceof ConstructedClassWithPackage) {\n                // we replace '.' only in the className part\n                // with '$' to find an inner class. The case that\n                // the package is really a class is handled elsewhere\n                ConstructedClassWithPackage tmp = (ConstructedClassWithPackage) type;\n                String savedName = tmp.className;\n                tmp.className = replaceLastPoint(savedName);\n                if (resolve(tmp, false, true, true)) {\n                    type.setRedirect(tmp.redirect());\n                    return true;\n                }\n                tmp.className = savedName;\n            }   else {\n                String savedName = type.getName();\n                String replacedPointType = replaceLastPoint(savedName);\n                type.setName(replacedPointType);\n                if (resolve(type, false, true, true)) return true;\n                type.setName(savedName);\n            }\n        }\n        return false;\n    }\n\n    private boolean resolveFromDefaultImports(ClassNode type, boolean testDefaultImports) {\n        // test default imports\n        testDefaultImports &= !type.hasPackageName();\n        // we do not resolve a vanilla name starting with a lower case letter\n        // try to resolve against a default import, because we know that the\n        // default packages do not contain classes like these\n        testDefaultImports &= !(type instanceof LowerCaseClass);\n        if (testDefaultImports) {\n            for (int i = 0, size = DEFAULT_IMPORTS.length; i < size; i++) {\n                String packagePrefix = DEFAULT_IMPORTS[i];\n                String name = type.getName();\n                // We limit the inner class lookups here by using ConstructedClassWithPackage.\n                // This way only the name will change, the packagePrefix will\n                // not be included in the lookup. The case where the\n                // packagePrefix is really a class is handled elsewhere.\n                // WARNING: This code does not expect a class that has a static\n                //          inner class in DEFAULT_IMPORTS\n                ConstructedClassWithPackage tmp =  new ConstructedClassWithPackage(packagePrefix,name);\n                if (resolve(tmp, false, false, false)) {\n                    type.setRedirect(tmp.redirect());\n                    return true;\n                }\n            }\n            String name = type.getName();\n            if (name.equals(\"BigInteger\")) {\n                type.setRedirect(ClassHelper.BigInteger_TYPE);\n                return true;\n            } else if (name.equals(\"BigDecimal\")) {\n                type.setRedirect(ClassHelper.BigDecimal_TYPE);\n                return true;\n            }\n        }\n        return false;\n    }\n\n    private boolean resolveFromCompileUnit(ClassNode type) {\n        // look into the compile unit if there is a class with that name\n        CompileUnit compileUnit = currentClass.getCompileUnit();\n        if (compileUnit == null) return false;\n        ClassNode cuClass = compileUnit.getClass(type.getName());\n        if (cuClass != null) {\n            if (type != cuClass) type.setRedirect(cuClass);\n            return true;\n        }\n        return false;\n    }\n\n    private void ambiguousClass(ClassNode type, ClassNode iType, String name) {\n        if (type.getName().equals(iType.getName())) {\n            addError(\"reference to \" + name + \" is ambiguous, both class \" + type.getName() + \" and \" + iType.getName() + \" match\", type);\n        } else {\n            type.setRedirect(iType);\n        }\n    }\n\n    private boolean resolveAliasFromModule(ClassNode type) {\n        // In case of getting a ConstructedClassWithPackage here we do not do checks for partial\n        // matches with imported classes. The ConstructedClassWithPackage is already a constructed\n        // node and any subclass resolving will then take place elsewhere\n        if (type instanceof ConstructedClassWithPackage) return false;\n\n        ModuleNode module = currentClass.getModule();\n        if (module == null) return false;\n        String name = type.getName();\n\n        // check module node imports aliases\n        // the while loop enables a check for inner classes which are not fully imported,\n        // but visible as the surrounding class is imported and the inner class is public/protected static\n        String pname = name;\n        int index = name.length();\n        /*\n         * we have a name foo.bar and an import foo.foo. This means foo.bar is possibly\n         * foo.foo.bar rather than foo.bar. This means to cut at the dot in foo.bar and\n         * foo for import\n         */\n        while (true) {\n            pname = name.substring(0, index);\n            ClassNode aliasedNode = null;\n            ImportNode importNode = module.getImport(pname);\n            if (importNode != null && importNode != currImportNode) {\n                aliasedNode = importNode.getType();\n            }\n            if (aliasedNode == null) {\n                importNode = module.getStaticImports().get(pname);\n                if (importNode != null && importNode != currImportNode) {\n                    // static alias only for inner classes and must be at end of chain\n                    ClassNode tmp = ClassHelper.make(importNode.getType().getName() + \"$\" + importNode.getFieldName());\n                    if (resolve(tmp, false, false, true)) {\n                        if ((tmp.getModifiers() & Opcodes.ACC_STATIC) != 0) {\n                            type.setRedirect(tmp.redirect());\n                            return true;\n                        }\n                    }\n                }\n            }\n\n            if (aliasedNode != null) {\n                if (pname.length() == name.length()) {\n                    // full match\n\n                    // We can compare here by length, because pname is always\n                    // a substring of name, so same length means they are equal.\n                    type.setRedirect(aliasedNode);\n                    return true;\n                } else {\n                    //partial match\n\n                    // At this point we know that we have a match for pname. This may\n                    // mean, that name[pname.length()..<-1] is a static inner class.\n                    // For this the rest of the name does not need any dots in its name.\n                    // It is either completely a inner static class or it is not.\n                    // Since we do not want to have useless lookups we create the name\n                    // completely and use a ConstructedClassWithPackage to prevent lookups against the package.\n                    String className = aliasedNode.getNameWithoutPackage() + '$' +\n                            name.substring(pname.length() + 1).replace('.', '$');\n                    ConstructedClassWithPackage tmp = new ConstructedClassWithPackage(aliasedNode.getPackageName()+\".\", className);\n                    if (resolve(tmp, true, true, false)) {\n                        type.setRedirect(tmp.redirect());\n                        return true;\n                    }\n                }\n            }\n            index = pname.lastIndexOf('.');\n            if (index == -1) break;\n        }\n        return false;\n    }\n\n    private boolean resolveFromModule(ClassNode type, boolean testModuleImports) {\n        // we decided if we have a vanilla name starting with a lower case\n        // letter that we will not try to resolve this name against .*\n        // imports. Instead a full import is needed for these.\n        // resolveAliasFromModule will do this check for us. This method\n        // does also check the module contains a class in the same package\n        // of this name. This check is not done for vanilla names starting\n        // with a lower case letter anymore\n        if (type instanceof LowerCaseClass) {\n            return resolveAliasFromModule(type);\n        }\n\n        String name = type.getName();\n        ModuleNode module = currentClass.getModule();\n        if (module == null) return false;\n\n        boolean newNameUsed = false;\n        // we add a package if there is none yet and the module has one. But we\n        // do not add that if the type is a ConstructedClassWithPackage. The code in ConstructedClassWithPackage\n        // hasPackageName() will return true if ConstructedClassWithPackage#className has no dots.\n        // but since the prefix may have them and the code there does ignore that\n        // fact. We check here for ConstructedClassWithPackage.\n        if (!type.hasPackageName() && module.hasPackageName() && !(type instanceof ConstructedClassWithPackage)) {\n            type.setName(module.getPackageName() + name);\n            newNameUsed = true;\n        }\n        // look into the module node if there is a class with that name\n        List<ClassNode> moduleClasses = module.getClasses();\n        for (ClassNode mClass : moduleClasses) {\n            if (mClass.getName().equals(type.getName())) {\n                if (mClass != type) type.setRedirect(mClass);\n                return true;\n            }\n        }\n        if (newNameUsed) type.setName(name);\n\n        if (testModuleImports) {\n            if (resolveAliasFromModule(type)) return true;\n\n            if (module.hasPackageName()) {\n                // check package this class is defined in. The usage of ConstructedClassWithPackage here\n                // means, that the module package will not be involved when the\n                // compiler tries to find an inner class.\n                ConstructedClassWithPackage tmp =  new ConstructedClassWithPackage(module.getPackageName(), name);\n                if (resolve(tmp, false, false, false)) {\n                    ambiguousClass(type, tmp, name);\n                    type.setRedirect(tmp.redirect());\n                    return true;\n                }\n            }\n\n            // check module static imports (for static inner classes)\n            for (ImportNode importNode : module.getStaticImports().values()) {\n                if (importNode.getFieldName().equals(name)) {\n                    ClassNode tmp = ClassHelper.make(importNode.getType().getName() + \"$\" + name);\n                    if (resolve(tmp, false, false, true)) {\n                        if ((tmp.getModifiers() & Opcodes.ACC_STATIC) != 0) {\n                            type.setRedirect(tmp.redirect());\n                            return true;\n                        }\n                    }\n                }\n            }\n\n            // check module node import packages\n            for (ImportNode importNode : module.getStarImports()) {\n                String packagePrefix = importNode.getPackageName();\n                // We limit the inner class lookups here by using ConstructedClassWithPackage.\n                // This way only the name will change, the packagePrefix will\n                // not be included in the lookup. The case where the\n                // packagePrefix is really a class is handled elsewhere.\n                ConstructedClassWithPackage tmp = new ConstructedClassWithPackage(packagePrefix, name);\n                if (resolve(tmp, false, false, true)) {\n                    ambiguousClass(type, tmp, name);\n                    type.setRedirect(tmp.redirect());\n                    return true;\n                }\n            }\n\n            // check for star imports (import static pkg.Outer.*) matching static inner classes\n            for (ImportNode importNode : module.getStaticStarImports().values()) {\n                ClassNode tmp = ClassHelper.make(importNode.getClassName() + \"$\" + name);\n                if (resolve(tmp, false, false, true)) {\n                    if ((tmp.getModifiers() & Opcodes.ACC_STATIC) != 0) {\n                        ambiguousClass(type, tmp, name);\n                        type.setRedirect(tmp.redirect());\n                        return true;\n                    }\n                }\n\n            }\n        }\n        return false;\n    }\n\n    private boolean resolveToClass(ClassNode type) {\n        String name = type.getName();\n\n        // We use here the class cache cachedClasses to prevent\n        // calls to ClassLoader#loadClass. disabling this cache will\n        // cause a major performance hit. Unlike at the end of this\n        // method we do not return true or false depending on if we\n        // want to recompile or not. If the class was cached, then\n        // we do not want to recompile, recompilation is already\n        // scheduled then\n\n        Object cached = cachedClasses.get(name);\n        if (cached == NO_CLASS)\n            return false;\n\n        if (cached != null) {\n            // cached == SCRIPT should not happen here!\n            type.setRedirect((ClassNode) cached);\n            return true;\n        }\n\n\n        // We do not need to check instances of LowerCaseClass\n        // to be a Class, because unless there was an import for\n        // for this we  do not lookup these cases. This was a decision\n        // made on the mailing list. To ensure we will not visit this\n        // method again we set a NO_CLASS for this name\n        if (type instanceof LowerCaseClass) {\n            cachedClasses.put(name,NO_CLASS);\n            return false;\n        }\n\n        if (currentClass.getModule().hasPackageName() && name.indexOf('.') == -1) return false;\n        GroovyClassLoader loader = compilationUnit.getClassLoader();\n        Class cls;\n        try {\n            // NOTE: it's important to do no lookup against script files\n            // here since the GroovyClassLoader would create a new CompilationUnit\n            cls = loader.loadClass(name, false, true);\n        } catch (ClassNotFoundException cnfe) {\n            cachedClasses.put(name, NO_CLASS);\n            return resolveToScript(type);\n        } catch (CompilationFailedException cfe) {\n            compilationUnit.getErrorCollector().addErrorAndContinue(new ExceptionMessage(cfe, true, source));\n            return resolveToScript(type);\n        }\n        //TODO: the case of a NoClassDefFoundError needs a bit more research\n        // a simple recompilation is not possible it seems. The current class\n        // we are searching for is there, so we should mark that somehow.\n        // Basically the missing class needs to be completely compiled before\n        // we can again search for the current name.\n        /*catch (NoClassDefFoundError ncdfe) {\n            cachedClasses.put(name,SCRIPT);\n            return false;\n        }*/\n        if (cls == null) return false;\n        ClassNode cn = ClassHelper.make(cls);\n        cachedClasses.put(name, cn);\n        type.setRedirect(cn);\n        //NOTE: we might return false here even if we found a class,\n        //      because  we want to give a possible script a chance to\n        //      recompile. This can only be done if the loader was not\n        //      the instance defining the class.\n        return cls.getClassLoader() == loader || resolveToScript(type);\n    }\n\n\n    public Expression transform(Expression exp) {\n        if (exp == null) return null;\n        Expression ret = null;\n        if (exp instanceof VariableExpression) {\n            ret = transformVariableExpression((VariableExpression) exp);\n        } else if (exp.getClass() == PropertyExpression.class) {\n            ret = transformPropertyExpression((PropertyExpression) exp);\n        } else if (exp instanceof DeclarationExpression) {\n            ret = transformDeclarationExpression((DeclarationExpression) exp);\n        } else if (exp instanceof BinaryExpression) {\n            ret = transformBinaryExpression((BinaryExpression) exp);\n        } else if (exp instanceof MethodCallExpression) {\n            ret = transformMethodCallExpression((MethodCallExpression) exp);\n        } else if (exp instanceof ClosureExpression) {\n            ret = transformClosureExpression((ClosureExpression) exp);\n        } else if (exp instanceof ConstructorCallExpression) {\n            ret = transformConstructorCallExpression((ConstructorCallExpression) exp);\n        } else if (exp instanceof AnnotationConstantExpression) {\n            ret = transformAnnotationConstantExpression((AnnotationConstantExpression) exp);\n        } else {\n            resolveOrFail(exp.getType(), exp);\n            ret = exp.transformExpression(this);\n        }\n        if (ret!=null && ret!=exp) ret.setSourcePosition(exp);\n        return ret;\n    }\n\n    private String lookupClassName(PropertyExpression pe) {\n        boolean doInitialClassTest=true;\n        String name = \"\";\n        // this loop builds a name from right to left each name part\n        // separated by \".\"\n        for (Expression it = pe; it != null; it = ((PropertyExpression) it).getObjectExpression()) {\n            if (it instanceof VariableExpression) {\n                VariableExpression ve = (VariableExpression) it;\n                // stop at super and this\n                if (ve.isSuperExpression() || ve.isThisExpression()) {\n                    return null;\n                }\n                String varName = ve.getName();\n                if (doInitialClassTest) {\n                    // we are at the first name part. This is the right most part.\n                    // If this part is in lower case, then we do not need a class\n                    // check. other parts of the property expression will be tested\n                    // by a different method call to this method, so foo.Bar.bar\n                    // can still be resolved to the class foo.Bar and the static\n                    // field bar.\n                    if (!testVanillaNameForClass(varName)) return null;\n                    doInitialClassTest = false;\n                    name = varName;\n                } else {\n                    name = varName + \".\" + name;\n                }\n                break;\n            }\n            // anything other than PropertyExpressions or\n            // VariableExpressions will stop resolving\n            else if (it.getClass() != PropertyExpression.class) {\n                return null;\n            } else {\n                PropertyExpression current = (PropertyExpression) it;\n                String propertyPart = current.getPropertyAsString();\n                // the class property stops resolving, dynamic property names too\n                if (propertyPart == null || propertyPart.equals(\"class\")) {\n                    return null;\n                }\n                if (doInitialClassTest) {\n                    // we are at the first name part. This is the right most part.\n                    // If this part is in lower case, then we do not need a class\n                    // check. other parts of the property expression will be tested\n                    // by a different method call to this method, so foo.Bar.bar\n                    // can still be resolved to the class foo.Bar and the static\n                    // field bar.\n                    if (!testVanillaNameForClass(propertyPart)) return null;\n                    doInitialClassTest= false;\n                    name = propertyPart;\n                } else {\n                    name = propertyPart + \".\" + name;\n                }\n            }\n        }\n        if (name.length() == 0) return null;\n        return name;\n    }\n\n    // iterate from the inner most to the outer and check for classes\n    // this check will ignore a .class property, for Example Integer.class will be\n    // a PropertyExpression with the ClassExpression of Integer as objectExpression\n    // and class as property\n    private Expression correctClassClassChain(PropertyExpression pe) {\n        LinkedList<Expression> stack = new LinkedList<Expression>();\n        ClassExpression found = null;\n        for (Expression it = pe; it != null; it = ((PropertyExpression) it).getObjectExpression()) {\n            if (it instanceof ClassExpression) {\n                found = (ClassExpression) it;\n                break;\n            } else if (!(it.getClass() == PropertyExpression.class)) {\n                return pe;\n            }\n            stack.addFirst(it);\n        }\n        if (found == null) return pe;\n\n        if (stack.isEmpty()) return pe;\n        Object stackElement = stack.removeFirst();\n        if (!(stackElement.getClass() == PropertyExpression.class)) return pe;\n        PropertyExpression classPropertyExpression = (PropertyExpression) stackElement;\n        String propertyNamePart = classPropertyExpression.getPropertyAsString();\n        if (propertyNamePart == null || !propertyNamePart.equals(\"class\")) return pe;\n\n        found.setSourcePosition(classPropertyExpression);\n        if (stack.isEmpty()) return found;\n        stackElement = stack.removeFirst();\n        if (!(stackElement.getClass() == PropertyExpression.class)) return pe;\n        PropertyExpression classPropertyExpressionContainer = (PropertyExpression) stackElement;\n\n        classPropertyExpressionContainer.setObjectExpression(found);\n        return pe;\n    }\n\n    protected Expression transformPropertyExpression(PropertyExpression pe) {\n        boolean itlp = isTopLevelProperty;\n        boolean ipe = inPropertyExpression;\n\n        Expression objectExpression = pe.getObjectExpression();\n        inPropertyExpression = true;\n        isTopLevelProperty = (objectExpression.getClass() != PropertyExpression.class);\n        objectExpression = transform(objectExpression);\n        // we handle the property part as if it were not part of the property\n        inPropertyExpression = false;\n        Expression property = transform(pe.getProperty());\n        isTopLevelProperty = itlp;\n        inPropertyExpression = ipe;\n\n        boolean spreadSafe = pe.isSpreadSafe();\n        PropertyExpression old = pe;\n        pe = new PropertyExpression(objectExpression, property, pe.isSafe());\n        pe.setSpreadSafe(spreadSafe);\n        pe.setSourcePosition(old);\n\n        String className = lookupClassName(pe);\n        if (className != null) {\n            ClassNode type = ClassHelper.make(className);\n            if (resolve(type)) {\n                Expression ret =  new ClassExpression(type);\n                ret.setSourcePosition(pe);\n                return ret;\n            }\n        }\n        if (objectExpression instanceof ClassExpression && pe.getPropertyAsString() != null) {\n            // possibly an inner class\n            ClassExpression ce = (ClassExpression) objectExpression;\n            ClassNode type = ClassHelper.make(ce.getType().getName() + \"$\" + pe.getPropertyAsString());\n            if (resolve(type, false, false, false)) {\n                Expression ret = new ClassExpression(type);\n                ret.setSourcePosition(ce);\n                return ret;\n            }\n        }\n        Expression ret = pe;\n        checkThisAndSuperAsPropertyAccess(pe);\n        if (isTopLevelProperty) ret = correctClassClassChain(pe);\n        return ret;\n    }\n\n    private void checkThisAndSuperAsPropertyAccess(PropertyExpression expression) {\n        if (expression.isImplicitThis()) return;\n        String prop = expression.getPropertyAsString();\n        if (prop == null) return;\n        if (!prop.equals(\"this\") && !prop.equals(\"super\")) return;\n\n        if (expression.getObjectExpression() instanceof ClassExpression) {\n            if (!(currentClass instanceof InnerClassNode)) {\n                addError(\"The usage of 'Class.this' and 'Class.super' is only allowed in nested/inner classes.\", expression);\n                return;\n            }\n            ClassNode type = expression.getObjectExpression().getType();\n            ClassNode iterType = currentClass;\n            while (iterType != null) {\n                if (iterType.equals(type)) break;\n                iterType = iterType.getOuterClass();\n            }\n            if (iterType == null) {\n                addError(\"The class '\" + type.getName() + \"' needs to be an outer class of '\" +\n                        currentClass.getName() + \"' when using '.this' or '.super'.\", expression);\n            }\n            if ((currentClass.getModifiers() & Opcodes.ACC_STATIC) == 0) return;\n            if (!currentScope.isInStaticContext()) return;\n            addError(\"The usage of 'Class.this' and 'Class.super' within static nested class '\" +\n                    currentClass.getName() + \"' is not allowed in a static context.\", expression);\n        }\n    }\n\n    protected Expression transformVariableExpression(VariableExpression ve) {\n        visitAnnotations(ve);\n        Variable v = ve.getAccessedVariable();\n        \n        if(!(v instanceof DynamicVariable) && !checkingVariableTypeInDeclaration) {\n            /*\n             *  GROOVY-4009: when a normal variable is simply being used, there is no need to try to \n             *  resolve its type. Variable type resolve should proceed only if the variable is being declared. \n             */\n            return ve;\n        }\n        if (v instanceof DynamicVariable){\n            String name = ve.getName();\n            ClassNode t = ClassHelper.make(name);\n            // asking isResolved here allows to check if a primitive\n            // type name like \"int\" was used to make t. In such a case\n            // we have nothing left to do.\n            boolean isClass = t.isResolved();\n            if (!isClass) {\n                // It was no primitive type, so next we see if the name,\n                // which is a vanilla name, starts with a lower case letter.\n                // In that case we change it to a LowerCaseClass to let the\n                // compiler skip the resolving at several places in this class.\n                if (Character.isLowerCase(name.charAt(0))) {\n                  t = new LowerCaseClass(name);\n                }\n                isClass = resolve(t);\n                if(!isClass) isClass = resolveToInnerEnum(t);\n            }\n            if (isClass) {\n                // the name is a type so remove it from the scoping\n                // as it is only a classvariable, it is only in\n                // referencedClassVariables, but must be removed\n                // for each parentscope too\n                for (VariableScope scope = currentScope; scope != null && !scope.isRoot(); scope = scope.getParent()) {\n                    if (scope.isRoot()) break;\n                    if (scope.removeReferencedClassVariable(ve.getName()) == null) break;\n                }\n                ClassExpression ce = new ClassExpression(t);\n                ce.setSourcePosition(ve);\n                return ce;\n            }\n        }\n        resolveOrFail(ve.getType(), ve);\n        return ve;\n    }\n\n    private boolean testVanillaNameForClass(String name) {\n        if (name==null || name.length()==0) return false;\n        return !Character.isLowerCase(name.charAt(0));\n    }\n\n    protected Expression transformBinaryExpression(BinaryExpression be) {\n        Expression left = transform(be.getLeftExpression());\n        int type = be.getOperation().getType();\n        if ((type == Types.ASSIGNMENT_OPERATOR || type == Types.EQUAL) &&\n                left instanceof ClassExpression) {\n            ClassExpression ce = (ClassExpression) left;\n            String error = \"you tried to assign a value to the class '\" + ce.getType().getName() + \"'\";\n            if (ce.getType().isScript()) {\n                error += \". Do you have a script with this name?\";\n            }\n            addError(error, be.getLeftExpression());\n            return be;\n        }\n        if (left instanceof ClassExpression) {\n            if (be.getRightExpression() instanceof ListExpression) {\n                ListExpression list = (ListExpression) be.getRightExpression();\n                if (list.getExpressions().isEmpty()) {\n                    // we have C[] if the list is empty -> should be an array then!\n                    final ClassExpression ce = new ClassExpression(left.getType().makeArray());\n                    ce.setSourcePosition(be);\n                    return ce;\n                }\n                else {\n                    // may be we have C[k1:v1, k2:v2] -> should become (C)([k1:v1, k2:v2])\n                    boolean map = true;\n                    for (Expression expression : list.getExpressions()) {\n                        if(!(expression instanceof MapEntryExpression)) {\n                            map = false;\n                            break;\n                        }\n                    }\n\n                    if (map) {\n                        final MapExpression me = new MapExpression();\n                        for (Expression expression : list.getExpressions()) {\n                            me.addMapEntryExpression((MapEntryExpression) transform(expression));\n                        }\n                        me.setSourcePosition(list);\n                        final CastExpression ce = new CastExpression(left.getType(), me);\n                        ce.setSourcePosition(be);\n                        return ce;\n                    }\n                }\n            }\n\n            if (be.getRightExpression() instanceof MapEntryExpression) {\n                // may be we have C[k1:v1] -> should become (C)([k1:v1])\n                final MapExpression me = new MapExpression();\n                me.addMapEntryExpression((MapEntryExpression) transform(be.getRightExpression()));\n                me.setSourcePosition(be.getRightExpression());\n                final CastExpression ce = new CastExpression(left.getType(), me);\n                ce.setSourcePosition(be);\n                return ce;\n            }\n        }\n        Expression right = transform(be.getRightExpression());\n        be.setLeftExpression(left);\n        be.setRightExpression(right);\n        return be;\n    }\n\n    protected Expression transformClosureExpression(ClosureExpression ce) {\n        boolean oldInClosure = inClosure;\n        inClosure = true;\n        Parameter[] paras = ce.getParameters();\n        if (paras != null) {\n            for (Parameter para : paras) {\n                ClassNode t = para.getType();\n                resolveOrFail(t, ce);\n                visitAnnotations(para);\n                if (para.hasInitialExpression()) {\n                    Object initialVal = para.getInitialExpression();\n                    if (initialVal instanceof Expression) {\n                        para.setInitialExpression(transform((Expression) initialVal));\n                    }\n                }\n                visitAnnotations(para);\n            }\n        }\n        Statement code = ce.getCode();\n        if (code != null) code.visit(this);\n        inClosure = oldInClosure;\n        return ce;\n    }\n\n    protected Expression transformConstructorCallExpression(ConstructorCallExpression cce) {\n        ClassNode type = cce.getType();\n        resolveOrFail(type, cce);\n        if (Modifier.isAbstract(type.getModifiers())) {\n            addError(\"You cannot create an instance from the abstract \" + getDescription(type) + \".\", cce);\n        }\n\n        Expression ret = cce.transformExpression(this);\n        return ret;\n    }\n\n    private String getDescription(ClassNode node) {\n        return (node.isInterface() ? \"interface\" : \"class\") + \" '\" + node.getName() + \"'\";\n    }\n    \n    protected Expression transformMethodCallExpression(MethodCallExpression mce) {\n        Expression args = transform(mce.getArguments());\n        Expression method = transform(mce.getMethod());\n        Expression object = transform(mce.getObjectExpression());\n\n        resolveGenericsTypes(mce.getGenericsTypes());\n        \n        MethodCallExpression result = new MethodCallExpression(object, method, args);\n        result.setSafe(mce.isSafe());\n        result.setImplicitThis(mce.isImplicitThis());\n        result.setSpreadSafe(mce.isSpreadSafe());\n        result.setSourcePosition(mce);\n        result.setGenericsTypes(mce.getGenericsTypes());\n        result.setMethodTarget(mce.getMethodTarget());\n        return result;\n    }\n    \n    protected Expression transformDeclarationExpression(DeclarationExpression de) {\n        visitAnnotations(de);\n        Expression oldLeft = de.getLeftExpression();\n        checkingVariableTypeInDeclaration = true;\n        Expression left = transform(oldLeft);\n        checkingVariableTypeInDeclaration = false;\n        if (left instanceof ClassExpression) {\n            ClassExpression ce = (ClassExpression) left;\n            addError(\"you tried to assign a value to the class \" + ce.getType().getName(), oldLeft);\n            return de;\n        }\n        Expression right = transform(de.getRightExpression());\n        if (right == de.getRightExpression()) {\n            fixDeclaringClass(de);\n            return de;\n        }\n        DeclarationExpression newDeclExpr = new DeclarationExpression(left, de.getOperation(), right);\n        newDeclExpr.setDeclaringClass(de.getDeclaringClass());\n        fixDeclaringClass(newDeclExpr);\n        newDeclExpr.setSourcePosition(de);\n        newDeclExpr.addAnnotations(de.getAnnotations());\n        return newDeclExpr;\n    }\n\n    // TODO get normal resolving to set declaring class\n    private void fixDeclaringClass(DeclarationExpression newDeclExpr) {\n        if (newDeclExpr.getDeclaringClass() == null && currentMethod != null) {\n            newDeclExpr.setDeclaringClass(currentMethod.getDeclaringClass());\n        }\n    }\n\n    protected Expression transformAnnotationConstantExpression(AnnotationConstantExpression ace) {\n        AnnotationNode an = (AnnotationNode) ace.getValue();\n        ClassNode type = an.getClassNode();\n        resolveOrFail(type, \", unable to find class for annotation\", an);\n        for (Map.Entry<String, Expression> member : an.getMembers().entrySet()) {\n            member.setValue(transform(member.getValue()));\n        }\n        return ace;\n    }\n\n    public void visitAnnotations(AnnotatedNode node) {\n        List<AnnotationNode> annotations = node.getAnnotations();\n        if (annotations.isEmpty()) return;\n        Map<String, AnnotationNode> tmpAnnotations = new HashMap<String, AnnotationNode>();\n        ClassNode annType;\n        for (AnnotationNode an : annotations) {\n            // skip built-in properties\n            if (an.isBuiltIn()) continue;\n            annType = an.getClassNode();\n            resolveOrFail(annType, \",  unable to find class for annotation\", an);\n            for (Map.Entry<String, Expression> member : an.getMembers().entrySet()) {\n                Expression newValue = transform(member.getValue());\n                newValue = transformInlineConstants(newValue);\n                member.setValue(newValue);\n                checkAnnotationMemberValue(newValue);\n            }\n            if(annType.isResolved()) {\n                Class annTypeClass = annType.getTypeClass();\n                Retention retAnn = (Retention) annTypeClass.getAnnotation(Retention.class);\n                if (retAnn != null && retAnn.value().equals(RetentionPolicy.RUNTIME)) {\n                    AnnotationNode anyPrevAnnNode = tmpAnnotations.put(annTypeClass.getName(), an);\n                    if(anyPrevAnnNode != null) {\n                        addError(\"Cannot specify duplicate annotation on the same member : \" + annType.getName(), an);\n                    }\n                }\n            }\n        }\n    }\n\n    // resolve constant-looking expressions statically (do here as gets transformed away later)\n    private Expression transformInlineConstants(Expression exp) {\n        if (exp instanceof PropertyExpression) {\n            PropertyExpression pe = (PropertyExpression) exp;\n            if (pe.getObjectExpression() instanceof ClassExpression) {\n                ClassExpression ce = (ClassExpression) pe.getObjectExpression();\n                ClassNode type = ce.getType();\n                if (type.isEnum())\n                    return exp;\n\n                FieldNode fn = type.getField(pe.getPropertyAsString());\n                if (fn != null && !fn.isEnum() && fn.isStatic() && fn.isFinal()) {\n                    if (fn.getInitialValueExpression() instanceof ConstantExpression) {\n                        return fn.getInitialValueExpression();\n                    }\n                }\n            }\n        } else if (exp instanceof ListExpression) {\n            ListExpression le = (ListExpression) exp;\n            ListExpression result = new ListExpression();\n            for (Expression e : le.getExpressions()) {\n                result.addExpression(transformInlineConstants(e));\n            }\n            return result;\n        } else if (exp instanceof AnnotationConstantExpression) {\n            ConstantExpression ce = (ConstantExpression) exp;\n            if (ce.getValue() instanceof AnnotationNode) {\n                // replicate a little bit of AnnotationVisitor here\n                // because we can't wait until later to do this\n                AnnotationNode an = (AnnotationNode) ce.getValue();\n                for (Map.Entry<String, Expression> member : an.getMembers().entrySet()) {\n                    member.setValue(transformInlineConstants(member.getValue()));\n                }\n\n            }\n        }\n        return exp;\n    }\n\n    private void checkAnnotationMemberValue(Expression newValue) {\n        if (newValue instanceof PropertyExpression) {\n            PropertyExpression pe = (PropertyExpression) newValue;\n            if (!(pe.getObjectExpression() instanceof ClassExpression)) {\n                addError(\"unable to find class '\" + pe.getText() + \"' for annotation attribute constant\", pe.getObjectExpression());\n            }\n        } else if (newValue instanceof ListExpression) {\n            ListExpression le = (ListExpression) newValue;\n            for (Expression e : le.getExpressions()) {\n                checkAnnotationMemberValue(e);\n            }\n        }\n    }\n\n    public void visitClass(ClassNode node) {\n        ClassNode oldNode = currentClass;\n        Map<String, GenericsType> oldPNames = genericParameterNames;\n        genericParameterNames = new HashMap<String, GenericsType>(genericParameterNames);\n        currentClass = node;\n\n        resolveGenericsHeader(node.getGenericsTypes());\n\n        ModuleNode module = node.getModule();\n        if (!module.hasImportsResolved()) {\n            for (ImportNode importNode : module.getImports()) {\n                currImportNode = importNode;\n                ClassNode type = importNode.getType();\n                if (resolve(type, false, false, true)) {\n                    currImportNode = null;\n                    continue;\n                }\n                currImportNode = null;\n                addError(\"unable to resolve class \" + type.getName(), type);\n            }\n            for (ImportNode importNode : module.getStaticStarImports().values()) {\n                ClassNode type = importNode.getType();\n                if (resolve(type, false, false, true)) continue;\n                // Maybe this type belongs in the same package as the node that is doing the\n                // static import. In that case, the package may not have been explicitly specified.\n                // Try with the node's package too. If still not found, revert to original type name.\n                if (type.getPackageName() == null && node.getPackageName() != null) {\n                    String oldTypeName = type.getName();\n                    type.setName(node.getPackageName() + \".\" + oldTypeName);\n                    if (resolve(type, false, false, true)) continue;\n                    type.setName(oldTypeName);\n                }\n                addError(\"unable to resolve class \" + type.getName(), type);\n            }\n            for (ImportNode importNode : module.getStaticImports().values()) {\n                ClassNode type = importNode.getType();\n                if (resolve(type, true, true, true)) continue;\n                addError(\"unable to resolve class \" + type.getName(), type);\n            }\n            for (ImportNode importNode : module.getStaticStarImports().values()) {\n                ClassNode type = importNode.getType();\n                if (resolve(type, true, true, true)) continue;\n                addError(\"unable to resolve class \" + type.getName(), type);\n            }\n            module.setImportsResolved(true);\n        }\n\n        ClassNode sn = node.getUnresolvedSuperClass();\n        if (sn != null) resolveOrFail(sn, node, true);\n\n        for (ClassNode anInterface : node.getInterfaces()) {\n            resolveOrFail(anInterface, node, true);\n        }\n\n        checkCyclicInheritence(node, node.getUnresolvedSuperClass(), node.getInterfaces());\n        \n        super.visitClass(node);\n\n        genericParameterNames = oldPNames;\n\n        currentClass = oldNode;\n    }\n    \n    private void checkCyclicInheritence(ClassNode originalNode, ClassNode parentToCompare, ClassNode[] interfacesToCompare) {\n        if(!originalNode.isInterface()) {\n            if(parentToCompare == null) return;\n            if(originalNode == parentToCompare.redirect()) {\n                addError(\"Cyclic inheritance involving \" + parentToCompare.getName() + \" in class \" + originalNode.getName(), originalNode);\n                return;\n            }\n            if(interfacesToCompare != null && interfacesToCompare.length > 0) {\n                for(ClassNode intfToCompare : interfacesToCompare) {\n                    if(originalNode == intfToCompare.redirect()) {\n                        addError(\"Cycle detected: the type \" + originalNode.getName() + \" cannot implement itself\" , originalNode);\n                        return;\n                    }\n                }\n            }\n            if(parentToCompare == ClassHelper.OBJECT_TYPE) return;\n            checkCyclicInheritence(originalNode, parentToCompare.getUnresolvedSuperClass(), null);\n        } else {\n            if(interfacesToCompare != null && interfacesToCompare.length > 0) {\n                // check interfaces at this level first\n                for(ClassNode intfToCompare : interfacesToCompare) {\n                    if(originalNode == intfToCompare.redirect()) {\n                        addError(\"Cyclic inheritance involving \" + intfToCompare.getName() + \" in interface \" + originalNode.getName(), originalNode);\n                        return;\n                    }\n                }\n                // check next level of interfaces\n                for(ClassNode intf : interfacesToCompare) {\n                    checkCyclicInheritence(originalNode, null, intf.getInterfaces());\n                }\n            } else {\n                return;\n            }\n        }\n    }\n\n    public void visitCatchStatement(CatchStatement cs) {\n        resolveOrFail(cs.getExceptionType(), cs);\n        if (cs.getExceptionType() == ClassHelper.DYNAMIC_TYPE) {\n            cs.getVariable().setType(ClassHelper.make(Exception.class));\n        }\n        super.visitCatchStatement(cs);\n    }\n\n    public void visitForLoop(ForStatement forLoop) {\n        resolveOrFail(forLoop.getVariableType(), forLoop);\n        super.visitForLoop(forLoop);\n    }\n\n    public void visitBlockStatement(BlockStatement block) {\n        VariableScope oldScope = currentScope;\n        currentScope = block.getVariableScope();\n        super.visitBlockStatement(block);\n        currentScope = oldScope;\n    }\n\n    protected SourceUnit getSourceUnit() {\n        return source;\n    }\n\n    private void resolveGenericsTypes(GenericsType[] types) {\n        if (types == null) return;\n        currentClass.setUsingGenerics(true);\n        for (GenericsType type : types) {\n            resolveGenericsType(type);\n        }\n    }\n\n    private void resolveGenericsHeader(GenericsType[] types) {\n        if (types == null) return;\n        currentClass.setUsingGenerics(true);\n        for (GenericsType type : types) {\n            ClassNode classNode = type.getType();\n            String name = type.getName();\n            ClassNode[] bounds = type.getUpperBounds();\n            if (bounds != null) {\n                boolean nameAdded = false;\n                for (ClassNode upperBound : bounds) {\n                    if (!nameAdded && upperBound != null || !resolve(classNode)) {\n                        genericParameterNames.put(name, type);\n                        type.setPlaceholder(true);\n                        classNode.setRedirect(upperBound);\n                        nameAdded = true;\n                    }\n                    resolveOrFail(upperBound, classNode);\n                }\n            } else {\n                genericParameterNames.put(name, type);\n                classNode.setRedirect(ClassHelper.OBJECT_TYPE);\n                type.setPlaceholder(true);\n            }\n        }\n    }\n\n    private void resolveGenericsType(GenericsType genericsType) {\n        if (genericsType.isResolved()) return;\n        currentClass.setUsingGenerics(true);\n        ClassNode type = genericsType.getType();\n        // save name before redirect\n        String name = type.getName();\n        ClassNode[] bounds = genericsType.getUpperBounds();\n        if (!genericParameterNames.containsKey(name)) {\n            if (bounds != null) {\n                for (ClassNode upperBound : bounds) {\n                    resolveOrFail(upperBound, genericsType);\n                    type.setRedirect(upperBound);\n                    resolveGenericsTypes(upperBound.getGenericsTypes());\n                }\n            } else if (genericsType.isWildcard()) {\n                type.setRedirect(ClassHelper.OBJECT_TYPE);\n            } else {\n                resolveOrFail(type, genericsType);\n            }\n        } else {\n            GenericsType gt = genericParameterNames.get(name);\n            type.setRedirect(gt.getType());\n            genericsType.setPlaceholder(true);\n        }\n\n        if (genericsType.getLowerBound() != null) {\n            resolveOrFail(genericsType.getLowerBound(), genericsType);\n        }\n        resolveGenericsTypes(type.getGenericsTypes());\n        genericsType.setResolved(genericsType.getType().isResolved());\n    }\n}\n
===================================================================
--- groovy-git/src/main/org/codehaus/groovy/control/ResolveVisitor.java	(revision feffd9a127e1c1b28c0c0aa935fa41d6ebbcfcaa)
+++ groovy-git/src/main/org/codehaus/groovy/control/ResolveVisitor.java	(revision )
@@ -22,6 +22,7 @@
 import org.codehaus.groovy.ast.stmt.CatchStatement;
 import org.codehaus.groovy.ast.stmt.ForStatement;
 import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.ast.tools.GenericsUtils;
 import org.codehaus.groovy.classgen.Verifier;
 import org.codehaus.groovy.control.messages.ExceptionMessage;
 import org.codehaus.groovy.syntax.Types;
@@ -1222,10 +1223,15 @@
 
     public void visitClass(ClassNode node) {
         ClassNode oldNode = currentClass;
-        Map<String, GenericsType> oldPNames = genericParameterNames;
-        genericParameterNames = new HashMap<String, GenericsType>(genericParameterNames);
-        currentClass = node;
 
+        if (node instanceof InnerClassNode) {
+            if (Modifier.isStatic(node.getModifiers())) {
+                genericParameterNames = new HashMap<String, GenericsType>();
+            }
+        } else {
+            genericParameterNames = new HashMap<String, GenericsType>();
+        }
+        currentClass = node;
         resolveGenericsHeader(node.getGenericsTypes());
 
         ModuleNode module = node.getModule();
@@ -1277,8 +1283,6 @@
         checkCyclicInheritence(node, node.getUnresolvedSuperClass(), node.getInterfaces());
         
         super.visitClass(node);
-
-        genericParameterNames = oldPNames;
 
         currentClass = oldNode;
     }
Index: groovy-git/src/test/groovy/bugs/Groovy4457GenericTypeDeclarationLeakTest.groovy
IDEA additional info:
Subsystem: com.intellij.openapi.diff.impl.patch.CharsetEP
<+>UTF-8
Subsystem: com.intellij.openapi.diff.impl.patch.BaseRevisionTextPatchEP
<+>package groovy.bugs\n\n/**\n * @author Guillaume Laforge\n */\nclass Groovy4457GenericTypeDeclarationLeakTest extends GroovyTestCase {\n\n    void testLeak() {\n        assertScript \"\"\"\n            class A<String> {}\n\n            class B {\n                void foo(String s) {}\n            }\n\n            // use the name to check the class, since the error was that String was seen as\n            // a symbol resolved to Object, not as the class String, thus a ... == String would\n            // not have failed\n            assert B.declaredMethods.find { it.name == \"foo\" }.parameterTypes[0].name.contains(\"String\")\n        \"\"\"\n    }\n\n    void testLeakWithInnerClass() {\n        assertScript \"\"\"\n            class A<String> {\n                static class B {\n                    void foo(String s) {}\n                }\n            }\n\n            assert A.B.declaredMethods.find { it.name == \"foo\" }.parameterTypes[0].name.contains(\"String\")\n        \"\"\"\n    }\n}\n
===================================================================
--- groovy-git/src/test/groovy/bugs/Groovy4457GenericTypeDeclarationLeakTest.groovy	(revision feffd9a127e1c1b28c0c0aa935fa41d6ebbcfcaa)
+++ groovy-git/src/test/groovy/bugs/Groovy4457GenericTypeDeclarationLeakTest.groovy	(revision )
@@ -31,4 +31,17 @@
             assert A.B.declaredMethods.find { it.name == "foo" }.parameterTypes[0].name.contains("String")
         """
     }
+
+    void testNonStaticInnerClassGenerics() {
+        assertScript '''
+            class A<T> {
+                void bar(T s) {}
+                class B {
+                  void foo(T s) {}
+                }
+            }
+            assert A.class.methods.find{it.name=="bar"}.parameterTypes[0].name.contains("java.lang.Object")
+            assert A.B.class.methods.find{it.name=="foo"}.parameterTypes[0].name.contains("java.lang.Object")
+        '''
+    }
 }
