6863465: javac doesn't detect circular subclass dependencies via qualified names

Sat, 18 Sep 2010 14:24:09 -0700

author
mcimadamore
date
Sat, 18 Sep 2010 14:24:09 -0700
changeset 690
0c1ef2af7a8e
parent 689
77cc34d5e548
child 691
da7ca56d092c

6863465: javac doesn't detect circular subclass dependencies via qualified names
Summary: class inheritance circularity check should look at trees, not just symbols
Reviewed-by: jjg

src/share/classes/com/sun/tools/javac/comp/Check.java file | annotate | diff | comparison | revisions
src/share/classes/com/sun/tools/javac/comp/MemberEnter.java file | annotate | diff | comparison | revisions
test/tools/javac/6863465/T6863465a.java file | annotate | diff | comparison | revisions
test/tools/javac/6863465/T6863465a.out file | annotate | diff | comparison | revisions
test/tools/javac/6863465/T6863465b.java file | annotate | diff | comparison | revisions
test/tools/javac/6863465/T6863465b.out file | annotate | diff | comparison | revisions
test/tools/javac/6863465/T6863465c.java file | annotate | diff | comparison | revisions
test/tools/javac/6863465/T6863465c.out file | annotate | diff | comparison | revisions
test/tools/javac/6863465/T6863465d.java file | annotate | diff | comparison | revisions
test/tools/javac/6863465/T6863465d.out file | annotate | diff | comparison | revisions
test/tools/javac/6863465/TestCircularClassfile.java file | annotate | diff | comparison | revisions
test/tools/javac/CyclicInheritance.out file | annotate | diff | comparison | revisions
test/tools/javac/NameCollision.out file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/classes/com/sun/tools/javac/comp/Check.java	Sat Sep 18 09:56:23 2010 -0700
     1.2 +++ b/src/share/classes/com/sun/tools/javac/comp/Check.java	Sat Sep 18 14:24:09 2010 -0700
     1.3 @@ -60,6 +60,7 @@
     1.4      private final Names names;
     1.5      private final Log log;
     1.6      private final Symtab syms;
     1.7 +    private final Enter enter;
     1.8      private final Infer infer;
     1.9      private final Types types;
    1.10      private final JCDiagnostic.Factory diags;
    1.11 @@ -86,6 +87,7 @@
    1.12          names = Names.instance(context);
    1.13          log = Log.instance(context);
    1.14          syms = Symtab.instance(context);
    1.15 +        enter = Enter.instance(context);
    1.16          infer = Infer.instance(context);
    1.17          this.types = Types.instance(context);
    1.18          diags = JCDiagnostic.Factory.instance(context);
    1.19 @@ -1727,6 +1729,113 @@
    1.20              return undef;
    1.21          }
    1.22  
    1.23 +    void checkNonCyclicDecl(JCClassDecl tree) {
    1.24 +        CycleChecker cc = new CycleChecker();
    1.25 +        cc.scan(tree);
    1.26 +        if (!cc.errorFound && !cc.partialCheck) {
    1.27 +            tree.sym.flags_field |= ACYCLIC;
    1.28 +        }
    1.29 +    }
    1.30 +
    1.31 +    class CycleChecker extends TreeScanner {
    1.32 +
    1.33 +        List<Symbol> seenClasses = List.nil();
    1.34 +        boolean errorFound = false;
    1.35 +        boolean partialCheck = false;
    1.36 +
    1.37 +        private void checkSymbol(DiagnosticPosition pos, Symbol sym) {
    1.38 +            if (sym != null && sym.kind == TYP) {
    1.39 +                Env<AttrContext> classEnv = enter.getEnv((TypeSymbol)sym);
    1.40 +                if (classEnv != null) {
    1.41 +                    DiagnosticSource prevSource = log.currentSource();
    1.42 +                    try {
    1.43 +                        log.useSource(classEnv.toplevel.sourcefile);
    1.44 +                        scan(classEnv.tree);
    1.45 +                    }
    1.46 +                    finally {
    1.47 +                        log.useSource(prevSource.getFile());
    1.48 +                    }
    1.49 +                } else if (sym.kind == TYP) {
    1.50 +                    checkClass(pos, sym, List.<JCTree>nil());
    1.51 +                }
    1.52 +            } else {
    1.53 +                //not completed yet
    1.54 +                partialCheck = true;
    1.55 +            }
    1.56 +        }
    1.57 +
    1.58 +        @Override
    1.59 +        public void visitSelect(JCFieldAccess tree) {
    1.60 +            super.visitSelect(tree);
    1.61 +            checkSymbol(tree.pos(), tree.sym);
    1.62 +        }
    1.63 +
    1.64 +        @Override
    1.65 +        public void visitIdent(JCIdent tree) {
    1.66 +            checkSymbol(tree.pos(), tree.sym);
    1.67 +        }
    1.68 +
    1.69 +        @Override
    1.70 +        public void visitTypeApply(JCTypeApply tree) {
    1.71 +            scan(tree.clazz);
    1.72 +        }
    1.73 +
    1.74 +        @Override
    1.75 +        public void visitTypeArray(JCArrayTypeTree tree) {
    1.76 +            scan(tree.elemtype);
    1.77 +        }
    1.78 +
    1.79 +        @Override
    1.80 +        public void visitClassDef(JCClassDecl tree) {
    1.81 +            List<JCTree> supertypes = List.nil();
    1.82 +            if (tree.getExtendsClause() != null) {
    1.83 +                supertypes = supertypes.prepend(tree.getExtendsClause());
    1.84 +            }
    1.85 +            if (tree.getImplementsClause() != null) {
    1.86 +                for (JCTree intf : tree.getImplementsClause()) {
    1.87 +                    supertypes = supertypes.prepend(intf);
    1.88 +                }
    1.89 +            }
    1.90 +            checkClass(tree.pos(), tree.sym, supertypes);
    1.91 +        }
    1.92 +
    1.93 +        void checkClass(DiagnosticPosition pos, Symbol c, List<JCTree> supertypes) {
    1.94 +            if ((c.flags_field & ACYCLIC) != 0)
    1.95 +                return;
    1.96 +            if (seenClasses.contains(c)) {
    1.97 +                errorFound = true;
    1.98 +                noteCyclic(pos, (ClassSymbol)c);
    1.99 +            } else if (!c.type.isErroneous()) {
   1.100 +                try {
   1.101 +                    seenClasses = seenClasses.prepend(c);
   1.102 +                    if (c.type.tag == CLASS) {
   1.103 +                        if (supertypes.nonEmpty()) {
   1.104 +                            scan(supertypes);
   1.105 +                        }
   1.106 +                        else {
   1.107 +                            ClassType ct = (ClassType)c.type;
   1.108 +                            if (ct.supertype_field == null ||
   1.109 +                                    ct.interfaces_field == null) {
   1.110 +                                //not completed yet
   1.111 +                                partialCheck = true;
   1.112 +                                return;
   1.113 +                            }
   1.114 +                            checkSymbol(pos, ct.supertype_field.tsym);
   1.115 +                            for (Type intf : ct.interfaces_field) {
   1.116 +                                checkSymbol(pos, intf.tsym);
   1.117 +                            }
   1.118 +                        }
   1.119 +                        if (c.owner.kind == TYP) {
   1.120 +                            checkSymbol(pos, c.owner);
   1.121 +                        }
   1.122 +                    }
   1.123 +                } finally {
   1.124 +                    seenClasses = seenClasses.tail;
   1.125 +                }
   1.126 +            }
   1.127 +        }
   1.128 +    }
   1.129 +
   1.130      /** Check for cyclic references. Issue an error if the
   1.131       *  symbol of the type referred to has a LOCKED flag set.
   1.132       *
     2.1 --- a/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java	Sat Sep 18 09:56:23 2010 -0700
     2.2 +++ b/src/share/classes/com/sun/tools/javac/comp/MemberEnter.java	Sat Sep 18 14:24:09 2010 -0700
     2.3 @@ -927,7 +927,7 @@
     2.4                  tp.accept(new TypeAnnotate(baseEnv));
     2.5              tree.accept(new TypeAnnotate(env));
     2.6  
     2.7 -            chk.checkNonCyclic(tree.pos(), c.type);
     2.8 +            chk.checkNonCyclicDecl(tree);
     2.9  
    2.10              attr.attribTypeVariables(tree.typarams, baseEnv);
    2.11  
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/test/tools/javac/6863465/T6863465a.java	Sat Sep 18 14:24:09 2010 -0700
     3.3 @@ -0,0 +1,14 @@
     3.4 +/**
     3.5 + * @test /nodynamiccopyright/
     3.6 + * @bug     6863465
     3.7 + * @summary javac doesn't detect circular subclass dependencies via qualified names
     3.8 + * @author  Maurizio Cimadamore
     3.9 + * @compile/fail/ref=T6863465a.out -XDrawDiagnostics T6863465a.java
    3.10 + */
    3.11 +
    3.12 +class T6863465a {
    3.13 +    static class a { static interface b {} }
    3.14 +    static class c extends a implements z.y {}
    3.15 +    static class x { static interface y {} }
    3.16 +    static class z extends x implements c.b {}
    3.17 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/test/tools/javac/6863465/T6863465a.out	Sat Sep 18 14:24:09 2010 -0700
     4.3 @@ -0,0 +1,2 @@
     4.4 +T6863465a.java:11:12: compiler.err.cyclic.inheritance: T6863465a.c
     4.5 +1 error
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/test/tools/javac/6863465/T6863465b.java	Sat Sep 18 14:24:09 2010 -0700
     5.3 @@ -0,0 +1,14 @@
     5.4 +/**
     5.5 + * @test /nodynamiccopyright/
     5.6 + * @bug     6863465
     5.7 + * @summary javac doesn't detect circular subclass dependencies via qualified names
     5.8 + * @author  Maurizio Cimadamore
     5.9 + * @compile/fail/ref=T6863465b.out -XDrawDiagnostics T6863465b.java
    5.10 + */
    5.11 +
    5.12 +class T6863465b {
    5.13 +    static class a { static interface b { static interface d {} } }
    5.14 +    static class c extends a implements z.y, z.d {}
    5.15 +    static class x { static interface y {} }
    5.16 +    static class z extends x implements c.b {}
    5.17 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/test/tools/javac/6863465/T6863465b.out	Sat Sep 18 14:24:09 2010 -0700
     6.3 @@ -0,0 +1,2 @@
     6.4 +T6863465b.java:11:12: compiler.err.cyclic.inheritance: T6863465b.c
     6.5 +1 error
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/test/tools/javac/6863465/T6863465c.java	Sat Sep 18 14:24:09 2010 -0700
     7.3 @@ -0,0 +1,14 @@
     7.4 +/**
     7.5 + * @test /nodynamiccopyright/
     7.6 + * @bug     6863465
     7.7 + * @summary javac doesn't detect circular subclass dependencies via qualified names
     7.8 + * @author  Maurizio Cimadamore
     7.9 + * @compile/fail/ref=T6863465c.out -XDrawDiagnostics T6863465c.java
    7.10 + */
    7.11 +
    7.12 +class T6863465c {
    7.13 +    static class x { static interface y {} }
    7.14 +    static class z extends x implements c.b {}
    7.15 +    static class a { static interface b { static interface d {} } }
    7.16 +    static class c extends a implements z.y, z.d {}
    7.17 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/test/tools/javac/6863465/T6863465c.out	Sat Sep 18 14:24:09 2010 -0700
     8.3 @@ -0,0 +1,3 @@
     8.4 +T6863465c.java:13:47: compiler.err.cant.resolve.location: kindname.class, d, , , kindname.class, T6863465c.z
     8.5 +T6863465c.java:11:12: compiler.err.cyclic.inheritance: T6863465c.z
     8.6 +2 errors
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/test/tools/javac/6863465/T6863465d.java	Sat Sep 18 14:24:09 2010 -0700
     9.3 @@ -0,0 +1,14 @@
     9.4 +/**
     9.5 + * @test /nodynamiccopyright/
     9.6 + * @bug     6863465
     9.7 + * @summary javac doesn't detect circular subclass dependencies via qualified names
     9.8 + * @author  Maurizio Cimadamore
     9.9 + * @compile/fail/ref=T6863465d.out -XDrawDiagnostics T6863465d.java
    9.10 + */
    9.11 +
    9.12 +class T6863465d {
    9.13 +    static class a { static interface b { static interface d {} } }
    9.14 +    static class c extends a implements z.y, z.d {}
    9.15 +    static class x { static interface y { static interface w {} } }
    9.16 +    static class z extends x implements c.b, c.w {}
    9.17 +}
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/test/tools/javac/6863465/T6863465d.out	Sat Sep 18 14:24:09 2010 -0700
    10.3 @@ -0,0 +1,3 @@
    10.4 +T6863465d.java:13:47: compiler.err.cant.resolve.location: kindname.class, w, , , kindname.class, T6863465d.c
    10.5 +T6863465d.java:11:12: compiler.err.cyclic.inheritance: T6863465d.c
    10.6 +2 errors
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/test/tools/javac/6863465/TestCircularClassfile.java	Sat Sep 18 14:24:09 2010 -0700
    11.3 @@ -0,0 +1,123 @@
    11.4 +/*
    11.5 + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
    11.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
    11.7 + *
    11.8 + * This code is free software; you can redistribute it and/or modify it
    11.9 + * under the terms of the GNU General Public License version 2 only, as
   11.10 + * published by the Free Software Foundation.
   11.11 + *
   11.12 + * This code is distributed in the hope that it will be useful, but WITHOUT
   11.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
   11.14 + * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
   11.15 + * version 2 for more details (a copy is included in the LICENSE file that
   11.16 + * accompanied this code).
   11.17 + *
   11.18 + * You should have received a copy of the GNU General Public License version
   11.19 + * 2 along with this work; if not, write to the Free Software Foundation,
   11.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
   11.21 + *
   11.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
   11.23 + * or visit www.oracle.com if you need additional information or have any
   11.24 + * questions.
   11.25 + */
   11.26 +
   11.27 +/*
   11.28 + * @test
   11.29 + * @bug 6863465
   11.30 + * @summary  javac doesn't detect circular subclass dependencies via qualified names
   11.31 + * @run main TestCircularClassfile
   11.32 + */
   11.33 +
   11.34 +import java.io.*;
   11.35 +import java.net.URI;
   11.36 +import java.util.Arrays;
   11.37 +import javax.tools.Diagnostic;
   11.38 +import javax.tools.DiagnosticListener;
   11.39 +import javax.tools.JavaCompiler;
   11.40 +import javax.tools.JavaFileObject;
   11.41 +import javax.tools.SimpleJavaFileObject;
   11.42 +import javax.tools.ToolProvider;
   11.43 +
   11.44 +import com.sun.source.util.JavacTask;
   11.45 +import java.util.EnumSet;
   11.46 +
   11.47 +public class TestCircularClassfile {
   11.48 +
   11.49 +    enum ClassName {
   11.50 +        A("A"),
   11.51 +        B("B"),
   11.52 +        C("C"),
   11.53 +        OBJECT("Object");
   11.54 +
   11.55 +        String name;
   11.56 +
   11.57 +        ClassName(String name) {
   11.58 +            this.name = name;
   11.59 +        }
   11.60 +    }
   11.61 +
   11.62 +    static class JavaSource extends SimpleJavaFileObject {
   11.63 +
   11.64 +        final static String sourceStub = "class #C extends #S {}";
   11.65 +
   11.66 +        String source;
   11.67 +
   11.68 +        public JavaSource(ClassName clazz, ClassName sup) {
   11.69 +            super(URI.create("myfo:/Test.java"), JavaFileObject.Kind.SOURCE);
   11.70 +            source = sourceStub.replace("#C", clazz.name).replace("#S", sup.name);
   11.71 +        }
   11.72 +
   11.73 +        @Override
   11.74 +        public CharSequence getCharContent(boolean ignoreEncodingErrors) {
   11.75 +            return source;
   11.76 +        }
   11.77 +    }
   11.78 +
   11.79 +    public static void main(String... args) throws Exception {
   11.80 +        int count = 0;
   11.81 +        for (ClassName clazz : EnumSet.of(ClassName.A, ClassName.B, ClassName.C)) {
   11.82 +            for (ClassName sup : EnumSet.of(ClassName.A, ClassName.B, ClassName.C)) {
   11.83 +                if (sup.ordinal() < clazz.ordinal()) continue;
   11.84 +                check("sub_"+count++, clazz, sup);
   11.85 +            }
   11.86 +        }
   11.87 +    }
   11.88 +
   11.89 +    static JavaSource[] initialSources = new JavaSource[] {
   11.90 +            new JavaSource(ClassName.A, ClassName.OBJECT),
   11.91 +            new JavaSource(ClassName.B, ClassName.A),
   11.92 +            new JavaSource(ClassName.C, ClassName.B)
   11.93 +        };
   11.94 +
   11.95 +    static String workDir = System.getProperty("user.dir");
   11.96 +
   11.97 +    static void check(String destPath, ClassName clazz, ClassName sup) throws Exception {
   11.98 +        File destDir = new File(workDir, destPath); destDir.mkdir();
   11.99 +        final JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
  11.100 +        JavacTask ct = (JavacTask)tool.getTask(null, null, null,
  11.101 +                Arrays.asList("-d", destPath), null, Arrays.asList(initialSources));
  11.102 +        ct.generate();
  11.103 +        File fileToRemove = new File(destPath, clazz.name + ".class");
  11.104 +        fileToRemove.delete();
  11.105 +        JavaSource newSource = new JavaSource(clazz, sup);
  11.106 +        DiagnosticChecker checker = new DiagnosticChecker();
  11.107 +        ct = (JavacTask)tool.getTask(null, null, checker,
  11.108 +                Arrays.asList("-cp", destPath), null, Arrays.asList(newSource));
  11.109 +        ct.analyze();
  11.110 +        if (!checker.errorFound) {
  11.111 +            throw new AssertionError(newSource.source);
  11.112 +        }
  11.113 +    }
  11.114 +
  11.115 +    static class DiagnosticChecker implements DiagnosticListener<JavaFileObject> {
  11.116 +
  11.117 +        boolean errorFound = false;
  11.118 +
  11.119 +        public void report(Diagnostic<? extends JavaFileObject> diagnostic) {
  11.120 +            if (diagnostic.getKind() == Diagnostic.Kind.ERROR &&
  11.121 +                    diagnostic.getCode().equals("compiler.err.cyclic.inheritance")) {
  11.122 +                errorFound = true;
  11.123 +            }
  11.124 +        }
  11.125 +    }
  11.126 +}
    12.1 --- a/test/tools/javac/CyclicInheritance.out	Sat Sep 18 09:56:23 2010 -0700
    12.2 +++ b/test/tools/javac/CyclicInheritance.out	Sat Sep 18 14:24:09 2010 -0700
    12.3 @@ -4,6 +4,6 @@
    12.4  CyclicInheritance.java:22:1: compiler.err.cyclic.inheritance: I11
    12.5  CyclicInheritance.java:27:1: compiler.err.cyclic.inheritance: C211
    12.6  CyclicInheritance.java:31:1: compiler.err.cyclic.inheritance: C212
    12.7 -CyclicInheritance.java:36:27: compiler.err.report.access: C221.I, private, C221
    12.8 -CyclicInheritance.java:40:24: compiler.err.report.access: C222.C, private, C222
    12.9 +CyclicInheritance.java:36:1: compiler.err.cyclic.inheritance: C221
   12.10 +CyclicInheritance.java:40:1: compiler.err.cyclic.inheritance: C222
   12.11  8 errors
    13.1 --- a/test/tools/javac/NameCollision.out	Sat Sep 18 09:56:23 2010 -0700
    13.2 +++ b/test/tools/javac/NameCollision.out	Sat Sep 18 14:24:09 2010 -0700
    13.3 @@ -1,2 +1,3 @@
    13.4  NameCollision.java:13:31: compiler.err.intf.expected.here
    13.5 -1 error
    13.6 +NameCollision.java:13:5: compiler.err.cyclic.inheritance: NameCollision.Runnable
    13.7 +2 errors

mercurial