# HG changeset patch # User jlahoda # Date 1465814547 -7200 # Mon Jun 13 12:42:27 2016 +0200 # Node ID ea65beb873d325c4bb66ae64055ab3b7845bb7cc # Parent b94f9c167fbaea25f823edb7bf4141037bd9ae21 [mq]: 8153362 diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Lint.java @@ -230,7 +230,12 @@ /** * Warn about potentially unsafe vararg methods */ - VARARGS("varargs"); + VARARGS("varargs"), + + /** + * Warn about unexported types being used in public APIs. + */ + UNEXPORTED_IN_API("unexportedinapi"); LintCategory(String option) { this(option, false); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Attr.java @@ -4393,6 +4393,7 @@ chk.checkDeprecatedAnnotation(env.tree.pos(), c); chk.checkClassOverrideEqualsAndHashIfNeeded(env.tree.pos(), c); chk.checkFunctionalInterface((JCClassDecl) env.tree, c); + chk.checkUnexportedInApi(env, (JCClassDecl) env.tree); } finally { env.info.returnResult = prevReturnRes; log.useSource(prev); diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Check.java @@ -31,6 +31,8 @@ import com.sun.tools.javac.code.*; import com.sun.tools.javac.code.Attribute.Compound; +import com.sun.tools.javac.code.Directive.ExportsDirective; +import com.sun.tools.javac.code.Directive.RequiresDirective; import com.sun.tools.javac.comp.Annotate.AnnotationTypeMetadata; import com.sun.tools.javac.jvm.*; import com.sun.tools.javac.resources.CompilerProperties.Errors; @@ -3656,4 +3658,161 @@ } } + public void checkUnexportedInApi(Env env, JCClassDecl check) { + JCCompilationUnit toplevel = env.toplevel; + + if ( toplevel.modle == syms.unnamedModule + || toplevel.modle == syms.noModule + || (check.sym.flags() & COMPOUND) != 0) { + return ; + } + + ExportsDirective currentExport = findExport(toplevel.packge); + + if ( currentExport == null //not exported + || currentExport.modules != null) //don't check classes in qualified export + return ; + + new TreeScanner() { + Lint lint = env.info.lint; + boolean inSuperType; + + @Override public void visitBlock(JCBlock tree) { + } + @Override public void visitMethodDef(JCMethodDecl tree) { + if (!isAPISymbol(tree.sym)) + return; + lint = lint.augment(tree.sym); + if (lint.isEnabled(LintCategory.UNEXPORTED_IN_API)) { + super.visitMethodDef(tree); + } + } + @Override + public void visitVarDef(JCVariableDecl tree) { + if (!isAPISymbol(tree.sym) && tree.sym.owner.kind != MTH) + return; + lint = lint.augment(tree.sym); + if (lint.isEnabled(LintCategory.UNEXPORTED_IN_API)) { + scan(tree.mods); + scan(tree.vartype); + } + } + @Override + public void visitClassDef(JCClassDecl tree) { + if (tree != check) + return ; + + if (!isAPISymbol(tree.sym)) + return ; + + lint = lint.augment(tree.sym); + if (lint.isEnabled(LintCategory.UNEXPORTED_IN_API)) { + scan(tree.mods); + scan(tree.typarams); + try { + inSuperType = true; + scan(tree.extending); + scan(tree.implementing); + } finally { + inSuperType = false; + } + scan(tree.defs); + } + } + @Override + public void visitTypeApply(JCTypeApply tree) { + scan(tree.clazz); + boolean oldInSuperType = inSuperType; + try { + inSuperType = false; + scan(tree.arguments); + } finally { + inSuperType = oldInSuperType; + } + } + @Override + public void visitIdent(JCIdent tree) { + Symbol sym = TreeInfo.symbol(tree); + if (sym.kind == TYP && !sym.type.hasTag(TYPEVAR)) { + checkVisible(tree.pos(), sym, toplevel.packge, inSuperType); + } + } + + @Override + public void visitSelect(JCFieldAccess tree) { + Symbol sym = TreeInfo.symbol(tree); + Symbol sitesym = TreeInfo.symbol(tree.selected); + if (sym.kind == TYP && sitesym.kind == PCK) { + checkVisible(tree.pos(), sym, toplevel.packge, inSuperType); + } else { + super.visitSelect(tree); + } + } + + @Override + public void visitAnnotation(JCAnnotation tree) { + if (tree.attribute.type.tsym.getAnnotation(java.lang.annotation.Documented.class) != null) + super.visitAnnotation(tree); + } + + }.scan(check); + } + //where: + private ExportsDirective findExport(PackageSymbol pack) { + for (ExportsDirective d : pack.modle.exports) { + if (d.packge == pack) + return d; + } + + return null; + } + private boolean isAPISymbol(Symbol sym) { + while (sym.kind != PCK) { + if ((sym.flags() & Flags.PUBLIC) == 0 && (sym.flags() & Flags.PROTECTED) == 0) { + return false; + } + sym = sym.owner; + } + return true; + } + private void checkVisible(DiagnosticPosition pos, Symbol what, PackageSymbol inPackage, boolean inSuperType) { + if (!isAPISymbol(what) && !inSuperType) { //package private/private element + log.warning(pos, "inaccessible.in.api"); + return ; + } + + PackageSymbol whatPackage = what.packge(); + ExportsDirective whatExport = findExport(whatPackage); + ExportsDirective inExport = findExport(inPackage); + + if (whatExport == null) { //package not exported: + log.warning(pos, "unexported.in.api"); + return ; + } + + if (whatExport.modules != null) { + if (inExport.modules == null || !whatExport.modules.containsAll(inExport.modules)) { + log.warning(pos, "unexported.in.api.qualified"); + } + } + + if (whatPackage.modle != inPackage.modle && whatPackage.modle != syms.java_base) { + //check that relativeTo.modle requires public what.modle, somehow: + List todo = List.of(inPackage.modle); + + while (todo.nonEmpty()) { + ModuleSymbol current = todo.head; + todo = todo.tail; + if (current == whatPackage.modle) + return ; //OK + for (RequiresDirective req : current.requires) { + if (req.isPublic()) { + todo = todo.prepend(req.module); + } + } + } + + log.warning(pos, "unexported.in.api.not.required.public"); + } + } } diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties @@ -2843,6 +2843,15 @@ compiler.warn.service.provided.but.not.exported.or.used=\ service interface provided but not exported or used +compiler.warn.inaccessible.in.api=\ + inaccessible type referenced in exported API +compiler.warn.unexported.in.api=\ + unexported type referenced in exported API +compiler.warn.unexported.in.api.not.required.public=\ + type from a module referenced in exported API that is not 'requires public' from this module +compiler.warn.unexported.in.api.qualified=\ + type that is invisible for some modules which read this package referenced in exported API + ### # errors related to options diff --git a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties --- a/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties +++ b/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/javac.properties @@ -213,6 +213,9 @@ javac.opt.Xlint.desc.processing=\ Warn about issues regarding annotation processing. +javac.opt.Xlint.desc.unexportedinapi=\ + Warn about use of types not visible to clients in exported API + javac.opt.Xlint.desc.rawtypes=\ Warn about use of raw types. diff --git a/test/tools/javac/diags/examples/InaccessibleInApi/InaccessibleInApi.java b/test/tools/javac/diags/examples/InaccessibleInApi/InaccessibleInApi.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/InaccessibleInApi/InaccessibleInApi.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.warn.inaccessible.in.api +// options: -Xlint:unexportedinapi diff --git a/test/tools/javac/diags/examples/InaccessibleInApi/modulesourcepath/m1/api/Api.java b/test/tools/javac/diags/examples/InaccessibleInApi/modulesourcepath/m1/api/Api.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/InaccessibleInApi/modulesourcepath/m1/api/Api.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package api; + +public class Api { + public Impl getImpl() { + return null; + } +} + +class Impl {} diff --git a/test/tools/javac/diags/examples/InaccessibleInApi/modulesourcepath/m1/module-info.java b/test/tools/javac/diags/examples/InaccessibleInApi/modulesourcepath/m1/module-info.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/InaccessibleInApi/modulesourcepath/m1/module-info.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +module m1 { + exports api; +} diff --git a/test/tools/javac/diags/examples/UnexportedInApi/UnexportedInApi.java b/test/tools/javac/diags/examples/UnexportedInApi/UnexportedInApi.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/UnexportedInApi/UnexportedInApi.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.warn.unexported.in.api +// options: -Xlint:unexportedinapi diff --git a/test/tools/javac/diags/examples/UnexportedInApi/modulesourcepath/m1/api/Api.java b/test/tools/javac/diags/examples/UnexportedInApi/modulesourcepath/m1/api/Api.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/UnexportedInApi/modulesourcepath/m1/api/Api.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package api; + +public class Api { + public impl.Impl getImpl() { + return null; + } +} diff --git a/test/tools/javac/diags/examples/UnexportedInApi/modulesourcepath/m1/impl/Impl.java b/test/tools/javac/diags/examples/UnexportedInApi/modulesourcepath/m1/impl/Impl.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/UnexportedInApi/modulesourcepath/m1/impl/Impl.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package impl; + +public class Impl { +} diff --git a/test/tools/javac/diags/examples/UnexportedInApi/modulesourcepath/m1/module-info.java b/test/tools/javac/diags/examples/UnexportedInApi/modulesourcepath/m1/module-info.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/UnexportedInApi/modulesourcepath/m1/module-info.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +module m1 { + exports api; +} diff --git a/test/tools/javac/diags/examples/UnexportedInApiNotRequiredPublic/UnexportedInApiNotRequiredPublic.java b/test/tools/javac/diags/examples/UnexportedInApiNotRequiredPublic/UnexportedInApiNotRequiredPublic.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/UnexportedInApiNotRequiredPublic/UnexportedInApiNotRequiredPublic.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.warn.unexported.in.api.not.required.public +// options: -Xlint:unexportedinapi diff --git a/test/tools/javac/diags/examples/UnexportedInApiNotRequiredPublic/modulesourcepath/m1/api1/Api1.java b/test/tools/javac/diags/examples/UnexportedInApiNotRequiredPublic/modulesourcepath/m1/api1/Api1.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/UnexportedInApiNotRequiredPublic/modulesourcepath/m1/api1/Api1.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package api1; + +public class Api1 { +} diff --git a/test/tools/javac/diags/examples/UnexportedInApiNotRequiredPublic/modulesourcepath/m1/module-info.java b/test/tools/javac/diags/examples/UnexportedInApiNotRequiredPublic/modulesourcepath/m1/module-info.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/UnexportedInApiNotRequiredPublic/modulesourcepath/m1/module-info.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +module m1 { + exports api1; +} diff --git a/test/tools/javac/diags/examples/UnexportedInApiNotRequiredPublic/modulesourcepath/m2/api2/Api2.java b/test/tools/javac/diags/examples/UnexportedInApiNotRequiredPublic/modulesourcepath/m2/api2/Api2.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/UnexportedInApiNotRequiredPublic/modulesourcepath/m2/api2/Api2.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package api2; + +public class Api2 { + public api1.Api1 getApi1() { + return null; + } +} diff --git a/test/tools/javac/diags/examples/UnexportedInApiNotRequiredPublic/modulesourcepath/m2/module-info.java b/test/tools/javac/diags/examples/UnexportedInApiNotRequiredPublic/modulesourcepath/m2/module-info.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/UnexportedInApiNotRequiredPublic/modulesourcepath/m2/module-info.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +module m2 { + requires m1; + exports api2; +} diff --git a/test/tools/javac/diags/examples/UnexportedInApiQualified/UnexportedInApiQualified.java b/test/tools/javac/diags/examples/UnexportedInApiQualified/UnexportedInApiQualified.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/UnexportedInApiQualified/UnexportedInApiQualified.java @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +// key: compiler.warn.unexported.in.api.qualified +// options: -Xlint:unexportedinapi diff --git a/test/tools/javac/diags/examples/UnexportedInApiQualified/modulesourcepath/m1/api/Api.java b/test/tools/javac/diags/examples/UnexportedInApiQualified/modulesourcepath/m1/api/Api.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/UnexportedInApiQualified/modulesourcepath/m1/api/Api.java @@ -0,0 +1,30 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package api; + +public class Api { + public qapi.QApi get() { + return null; + } +} diff --git a/test/tools/javac/diags/examples/UnexportedInApiQualified/modulesourcepath/m1/module-info.java b/test/tools/javac/diags/examples/UnexportedInApiQualified/modulesourcepath/m1/module-info.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/UnexportedInApiQualified/modulesourcepath/m1/module-info.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +module m1 { + exports api; + exports qapi to m2; +} diff --git a/test/tools/javac/diags/examples/UnexportedInApiQualified/modulesourcepath/m1/qapi/QApi.java b/test/tools/javac/diags/examples/UnexportedInApiQualified/modulesourcepath/m1/qapi/QApi.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/UnexportedInApiQualified/modulesourcepath/m1/qapi/QApi.java @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package qapi; + +public class QApi { +} diff --git a/test/tools/javac/diags/examples/UnexportedInApiQualified/modulesourcepath/m2/module-info.java b/test/tools/javac/diags/examples/UnexportedInApiQualified/modulesourcepath/m2/module-info.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/diags/examples/UnexportedInApiQualified/modulesourcepath/m2/module-info.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +module m2 { + requires m1; +} diff --git a/test/tools/javac/modules/ExportsUnexported.java b/test/tools/javac/modules/ExportsUnexported.java new file mode 100644 --- /dev/null +++ b/test/tools/javac/modules/ExportsUnexported.java @@ -0,0 +1,309 @@ +/* + * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * @test + * @summary tests for module declarations + * @library /tools/lib + * @modules jdk.compiler/com.sun.tools.javac.api + * jdk.compiler/com.sun.tools.javac.main + * @build toolbox.ToolBox toolbox.JavacTask ModuleTestBase + * @run main ExportsUnexported + */ + +import java.nio.file.Path; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import toolbox.JavacTask; +import toolbox.Task; + +public class ExportsUnexported extends ModuleTestBase { + + public static void main(String... args) throws Exception { + ExportsUnexported t = new ExportsUnexported(); + t.runTests(); + } + + @Test + public void testLocations(Path base) throws Exception { + String warningsTest = "package api;\n" + + "import impl.impl.*;\n" + + "@impl.impl^.DocAnn\n" + + "public abstract class Api extends impl.impl^.Cls implements impl.impl^.Intf, impl.impl^.NonDocAnn, impl.impl^.DocAnn {\n" + + " public static impl.impl^.Cls m(impl.impl^.Intf i, impl.impl^.Cls c) throws impl.impl^.Exc { return null; }\n" + + " public static impl.impl^.Cls f;\n" + + "}"; + String noWarningsTest = "package api;\n" + + "import impl.impl.*;\n" + + "@impl.impl.NonDocAnn\n" + + "public abstract class Api {\n" + + " private static abstract class I extends impl.impl.Cls implements impl.impl.Intf, impl.impl.NonDocAnn, impl.impl.DocAnn {\n" + + " public static abstract class II extends impl.impl.Cls implements impl.impl.Intf, impl.impl.NonDocAnn, impl.impl.DocAnn { }\n" + + " public static impl.impl.Cls m(impl.impl.Intf i, impl.impl.Cls c) throws impl.impl.Exc { return null; }\n" + + " public static impl.impl.Cls f;\n" + + " }\n" + + " private static impl.impl.Cls m(impl.impl.Intf i, impl.impl.Cls c) throws impl.impl.Exc { return null; }\n" + + " private static impl.impl.Cls f1;\n" + + " public static void m() { new impl.impl.Cls(); }\n" + + " public static Object f2 = new impl.impl.Cls();\n" + + "}"; + for (String genericTest : new String[] {warningsTest, noWarningsTest}) { + for (String test : new String[] {genericTest, genericTest.replaceAll("impl\\.impl\\^.([A-Za-z])", "^$1").replaceAll("impl\\.impl\\.([A-Za-z])", "$1")}) { + System.err.println("testing: " + test); + + Path src = base.resolve("src"); + Path src_m1 = src.resolve("m1"); + StringBuilder testCode = new StringBuilder(); + List expectedLog = new ArrayList<>(); + int line = 1; + int col = 1; + + for (char c : test.toCharArray()) { + if (c == '^') { + expectedLog.add("Api.java:" + line + ":" + col + ": compiler.warn.unexported.in.api"); + continue; + } + + if (c == '\n') { + line++; + col = 0; + } + + testCode.append(c); + col++; + } + + if (!expectedLog.isEmpty()) { + expectedLog.add("" + expectedLog.size() + " warning" + (expectedLog.size() == 1 ? "" : "s")); + expectedLog.add("- compiler.err.warnings.and.werror"); + expectedLog.add("1 error"); + } + + Collections.sort(expectedLog); + + tb.writeJavaFiles(src_m1, + "module m1 { exports api; }", + testCode.toString(), + "package impl.impl; public class Cls { }", + "package impl.impl; public class Exc extends Exception { }", + "package impl.impl; public interface Intf { }", + "package impl.impl; @java.lang.annotation.Documented public @interface DocAnn { }", + "package impl.impl; public @interface NonDocAnn { }"); + Path classes = base.resolve("classes"); + tb.createDirectories(classes); + + List log = new JavacTask(tb) + .options("-XDrawDiagnostics", + "-Werror", + "-modulesourcepath", src.toString(), + "-Xlint:unexportedinapi") + .outdir(classes) + .files(findJavaFiles(src)) + .run(expectedLog.isEmpty() ? Task.Expect.SUCCESS : Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + log = new ArrayList<>(log); + Collections.sort(log); + + if (expectedLog.isEmpty() ? !log.equals(Arrays.asList("")) : !log.equals(expectedLog)) { + throw new Exception("expected output not found in: " + log + "; " + expectedLog); + } + } + } + } + + @Test + public void testAccessibleToSpecificOrAll(Path base) throws Exception { + Path src = base.resolve("src"); + Path src_lib1 = src.resolve("lib1"); + tb.writeJavaFiles(src_lib1, + "module lib1 { exports lib1; }", + "package lib1; public class Lib1 {}"); + Path src_lib2 = src.resolve("lib2"); + tb.writeJavaFiles(src_lib2, + "module lib2 { exports lib2; }", + "package lib2; public class Lib2 {}"); + Path src_api = src.resolve("api"); + tb.writeJavaFiles(src_api, + "module api {\n" + + " exports api;\n" + + " exports qapi1 to qual1;\n" + + " exports qapi2 to qual1, qual2;\n" + + " requires public lib1;\n" + + " requires lib2;\n" + + "}\n", + "package api;\n" + + "public class Api {\n" + + " public lib1.Lib1 lib1;\n" + + " public lib2.Lib2 lib2;\n" + + " public qapi1.QApi1 qapi1;\n" + + " public impl.Impl impl;\n" + + "}", + "package qapi1;\n" + + "public class QApi1 {\n" + + " public qapi2.QApi2 qapi2;\n" + + "}", + "package qapi2;\n" + + "public class QApi2 {\n" + + " public qapi1.QApi1 qapi1;\n" + + "}", + "package impl;\n" + + "public class Impl {\n" + + "}"); + Path src_qual1 = src.resolve("qual1"); + tb.writeJavaFiles(src_qual1, "module qual1 { }"); + Path src_qual2 = src.resolve("qual2"); + tb.writeJavaFiles(src_qual2, "module qual2 { }"); + Path classes = base.resolve("classes"); + tb.createDirectories(classes); + + List log = new JavacTask(tb) + .options("-XDrawDiagnostics", + "-Werror", + "-modulesourcepath", src.toString(), + "-Xlint:unexportedinapi") + .outdir(classes) + .files(findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = Arrays.asList( + "Api.java:4:16: compiler.warn.unexported.in.api.not.required.public", + "Api.java:5:17: compiler.warn.unexported.in.api.qualified", + "Api.java:6:16: compiler.warn.unexported.in.api", + "- compiler.err.warnings.and.werror", + "1 error", + "3 warnings" + ); + + if (!log.equals(expected)) + throw new Exception("expected output not found"); + } + + @Test + public void testNestedClasses(Path base) throws Exception { + Path src = base.resolve("src"); + Path src_api = src.resolve("api"); + tb.writeJavaFiles(src_api, + "module api {\n" + + " exports api;\n" + + "}\n", + "package api;\n" + + "import impl.Impl.Nested;\n" + + "public class Api {\n" + + " public impl.Impl impl1;\n" + + " public impl.Impl.Nested impl2;\n" + + " public Nested impl3;\n" + + "}", + "package impl;\n" + + "public class Impl {\n" + + " public static class Nested {\n" + + " }\n" + + "}"); + Path classes = base.resolve("classes"); + tb.createDirectories(classes); + + List log = new JavacTask(tb) + .options("-XDrawDiagnostics", + "-Werror", + "-modulesourcepath", src.toString(), + "-Xlint:unexportedinapi") + .outdir(classes) + .files(findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = Arrays.asList( + "Api.java:4:16: compiler.warn.unexported.in.api", + "Api.java:5:16: compiler.warn.unexported.in.api", + "Api.java:6:12: compiler.warn.unexported.in.api", + "- compiler.err.warnings.and.werror", + "1 error", + "3 warnings" + ); + + if (!log.equals(expected)) + throw new Exception("expected output not found"); + } + + @Test + public void testProtectedAndInaccessible(Path base) throws Exception { + Path src = base.resolve("src"); + Path src_api = src.resolve("api"); + tb.writeJavaFiles(src_api, + "module api {\n" + + " exports api;\n" + + "}\n", + "package api;\n" + + "public class Api extends PackagePrivateClass implements PackagePrivateInterface {\n" + + " protected PackagePrivateClass f1;\n" + + " protected PackagePrivateInterface f2;\n" + + " protected Inner f3;\n" + + " protected PrivateInner f4;\n" + + " protected impl.Impl f5;\n" + + " public static class InnerClass extends PrivateInner {}\n" + + " protected static class Inner {}\n" + + " private static class PrivateInner {}\n" + + "}\n" + + "class PackagePrivateClass {}\n" + + "interface PackagePrivateInterface {}", + "package impl;\n" + + "public class Impl {\n" + + "}"); + Path classes = base.resolve("classes"); + tb.createDirectories(classes); + + List log = new JavacTask(tb) + .options("-XDrawDiagnostics", + "-Werror", + "-modulesourcepath", src.toString(), + "-Xlint:unexportedinapi") + .outdir(classes) + .files(findJavaFiles(src)) + .run(Task.Expect.FAIL) + .writeAll() + .getOutputLines(Task.OutputKind.DIRECT); + + List expected = Arrays.asList( + "Api.java:2:46: compiler.warn.inaccessible.in.api", + "Api.java:2:106: compiler.warn.inaccessible.in.api", + "Api.java:3:15: compiler.warn.inaccessible.in.api", + "Api.java:4:15: compiler.warn.inaccessible.in.api", + "Api.java:6:15: compiler.warn.inaccessible.in.api", + "Api.java:7:19: compiler.warn.unexported.in.api", + "- compiler.err.warnings.and.werror", + "1 error", + "6 warnings" + ); + + if (!log.equals(expected)) + throw new Exception("expected output not found"); + } + +}