8176327: javac produces wrong module-info
authorjjg
Mon, 24 Apr 2017 14:59:43 -0700
changeset 44822 2f24758e7ae0
parent 44821 c436e60e7af8
child 44823 8fd0a4569191
child 44876 f8559fb40dd6
child 44877 1ab95a256567
child 45041 f17345838471
8176327: javac produces wrong module-info 8178518: Add method JavaFileManager.contains Reviewed-by: jlahoda
langtools/src/java.compiler/share/classes/javax/tools/ForwardingJavaFileManager.java
langtools/src/java.compiler/share/classes/javax/tools/JavaFileManager.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/doclint/DocLint.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/api/ClientCodeWrapper.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/api/WrappingJavaFileManager.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java
langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties
langtools/src/jdk.jshell/share/classes/jdk/jshell/MemoryFileManager.java
langtools/test/tools/doclint/ProvidesTest.java
langtools/test/tools/doclint/ProvidesTest.out
langtools/test/tools/doclint/UsesTest.java
langtools/test/tools/doclint/UsesTest.out
langtools/test/tools/javac/api/TestClientCodeWrapper.java
langtools/test/tools/javac/diags/examples/FileShouldBeOnSourcePathOrPatchPath/FileShouldBeOnSourcePathOrModulePath.java
langtools/test/tools/javac/diags/examples/FileShouldBeOnSourcePathOrPatchPath/sourcepath/module-info.java
langtools/test/tools/javac/file/ModuleAndPackageLocations.java
langtools/test/tools/javac/modules/ContainsTest.java
langtools/test/tools/javac/modules/SourcePathTest.java
langtools/test/tools/javac/modules/T8158224/T8158224.java
langtools/test/tools/javac/modules/T8158224/T8158224.out
--- a/langtools/src/java.compiler/share/classes/javax/tools/ForwardingJavaFileManager.java	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/src/java.compiler/share/classes/javax/tools/ForwardingJavaFileManager.java	Mon Apr 24 14:59:43 2017 -0700
@@ -204,4 +204,11 @@
     public Iterable<Set<Location>> listLocationsForModules(Location location) throws IOException {
         return fileManager.listLocationsForModules(location);
     }
+
+    /**
+     * @since 9
+     */
+    public boolean contains(Location location, FileObject fo) throws IOException {
+        return fileManager.contains(location, fo);
+    }
 }
--- a/langtools/src/java.compiler/share/classes/javax/tools/JavaFileManager.java	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/src/java.compiler/share/classes/javax/tools/JavaFileManager.java	Mon Apr 24 14:59:43 2017 -0700
@@ -566,4 +566,38 @@
         throw new UnsupportedOperationException();
     }
 
+    /**
+     * Determines whether or not a given file object is "contained in" a specified location.
+     *
+     * <p>For a package-oriented location, a file object is contained in the location if there exist
+     * values for <i>packageName</i> and <i>relativeName</i> such that either of the following
+     * calls would return the {@link #isSameFile same} file object:
+     * <pre>
+     *     getFileForInput(location, <i>packageName</i>, <i>relativeName</i>)
+     *     getFileForOutput(location, <i>packageName</i>, <i>relativeName</i>, null)
+     * </pre>
+     *
+     * <p>For a module-oriented location, a file object is contained in the location if there exists
+     * a module that may be obtained by the call:
+     * <pre>
+     *     getLocationForModule(location, <i>moduleName</i>)
+     * </pre>
+     * such that the file object is contained in the (package-oriented) location for that module.
+     *
+     * @implSpec This implementation throws {@code UnsupportedOperationException}.
+     *
+     * @param location the location
+     * @param fo the file object
+     * @return whether or not the file is contained in the location
+     *
+     * @throws IOException if there is a problem determining the result
+     * @throws UnsupportedOperationException if the method is not supported
+     *
+     * @since 9
+     */
+
+    default boolean contains(Location location, FileObject fo) throws IOException {
+        throw new UnsupportedOperationException();
+    }
+
 }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/doclint/DocLint.java	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/doclint/DocLint.java	Mon Apr 24 14:59:43 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2017, 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
@@ -32,7 +32,6 @@
 import java.util.LinkedList;
 import java.util.List;
 import java.util.Queue;
-import java.util.regex.Pattern;
 
 import javax.lang.model.element.Name;
 import javax.tools.StandardLocation;
@@ -148,9 +147,15 @@
 
         JavacFileManager fm = new JavacFileManager(new Context(), false, null);
         fm.setSymbolFileEnabled(false);
-        fm.setLocation(StandardLocation.PLATFORM_CLASS_PATH, javacBootClassPath);
-        fm.setLocation(StandardLocation.CLASS_PATH, javacClassPath);
-        fm.setLocation(StandardLocation.SOURCE_PATH, javacSourcePath);
+        if (javacBootClassPath != null) {
+            fm.setLocation(StandardLocation.PLATFORM_CLASS_PATH, javacBootClassPath);
+        }
+        if (javacClassPath != null) {
+            fm.setLocation(StandardLocation.CLASS_PATH, javacClassPath);
+        }
+        if (javacSourcePath != null) {
+            fm.setLocation(StandardLocation.SOURCE_PATH, javacSourcePath);
+        }
 
         JavacTask task = tool.getTask(out, fm, null, javacOpts, null,
                 fm.getJavaFileObjectsFromFiles(javacFiles));
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/api/ClientCodeWrapper.java	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/api/ClientCodeWrapper.java	Mon Apr 24 14:59:43 2017 -0700
@@ -326,6 +326,17 @@
         }
 
         @Override @DefinedBy(Api.COMPILER)
+        public boolean contains(Location location, FileObject file) throws IOException {
+            try {
+                return clientJavaFileManager.contains(location, unwrap(file));
+            } catch (ClientCodeException e) {
+                throw e;
+            } catch (RuntimeException | Error e) {
+                throw new ClientCodeException(e);
+            }
+        }
+
+        @Override @DefinedBy(Api.COMPILER)
         public void flush() throws IOException {
             try {
                 clientJavaFileManager.flush();
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/api/WrappingJavaFileManager.java	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/api/WrappingJavaFileManager.java	Mon Apr 24 14:59:43 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2006, 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2006, 2017, 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
@@ -35,6 +35,7 @@
 import javax.tools.*;
 import javax.tools.JavaFileObject.Kind;
 
+import com.sun.tools.javac.util.ClientCodeException;
 import com.sun.tools.javac.util.DefinedBy;
 import com.sun.tools.javac.util.DefinedBy.Api;
 
@@ -214,4 +215,9 @@
                                            unwrap(sibling)));
     }
 
+    @Override @DefinedBy(Api.COMPILER)
+    public boolean contains(Location location, FileObject file) throws IOException {
+        return super.contains(location, unwrap(file));
+    }
+
 }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/comp/Modules.java	Mon Apr 24 14:59:43 2017 -0700
@@ -532,12 +532,52 @@
                 module = defaultModule;
             }
 
-            for (JCCompilationUnit tree: trees) {
+            for (JCCompilationUnit tree : trees) {
+                if (defaultModule != syms.unnamedModule
+                        && defaultModule.sourceLocation == StandardLocation.SOURCE_PATH
+                        && fileManager.hasLocation(StandardLocation.SOURCE_PATH)) {
+                    checkSourceLocation(tree, module);
+                }
                 tree.modle = module;
             }
         }
     }
 
+    private void checkSourceLocation(JCCompilationUnit tree, ModuleSymbol msym) {
+        // skip check if legacy module override still in use
+        if (legacyModuleOverride != null) {
+            return;
+        }
+
+        try {
+            JavaFileObject fo = tree.sourcefile;
+            if (fileManager.contains(msym.sourceLocation, fo)) {
+                return;
+            }
+            if (msym.patchLocation != null && fileManager.contains(msym.patchLocation, fo)) {
+                return;
+            }
+            if (fileManager.hasLocation(StandardLocation.SOURCE_OUTPUT)) {
+                if (fileManager.contains(StandardLocation.SOURCE_OUTPUT, fo)) {
+                    return;
+                }
+            } else {
+                if (fileManager.contains(StandardLocation.CLASS_OUTPUT, fo)) {
+                    return;
+                }
+            }
+        } catch (IOException e) {
+            throw new Error(e);
+        }
+
+        JavaFileObject prev = log.useSource(tree.sourcefile);
+        try {
+            log.error(tree.pos(), "file.sb.on.source.or.patch.path.for.module");
+        } finally {
+            log.useSource(prev);
+        }
+    }
+
     private String singleModuleOverride(List<JCCompilationUnit> trees) {
         if (!fileManager.hasLocation(StandardLocation.PATCH_MODULE_PATH)) {
             return legacyModuleOverride;
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/JavacFileManager.java	Mon Apr 24 14:59:43 2017 -0700
@@ -946,6 +946,14 @@
         return locations.getLocation(location);
     }
 
+    @Override @DefinedBy(Api.COMPILER)
+    public boolean contains(Location location, FileObject fo) throws IOException {
+        nullCheck(location);
+        nullCheck(fo);
+        Path p = asPath(fo);
+        return locations.contains(location, p);
+    }
+
     private Path getClassOutDir() {
         return locations.getOutputLocation(CLASS_OUTPUT);
     }
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/file/Locations.java	Mon Apr 24 14:59:43 2017 -0700
@@ -85,6 +85,7 @@
 import com.sun.tools.javac.util.ListBuffer;
 import com.sun.tools.javac.util.Log;
 import com.sun.tools.javac.jvm.ModuleNameReader;
+import com.sun.tools.javac.util.Assert;
 import com.sun.tools.javac.util.Pair;
 import com.sun.tools.javac.util.StringUtils;
 
@@ -226,6 +227,41 @@
         fsEnv = Collections.singletonMap("multi-release", multiReleaseValue);
     }
 
+    private boolean contains(Collection<Path> searchPath, Path file) throws IOException {
+
+        if (searchPath == null) {
+            return false;
+        }
+
+        Path enclosingJar = null;
+        if (file.getFileSystem().provider() == fsInfo.getJarFSProvider()) {
+            URI uri = file.toUri();
+            if (uri.getScheme().equals("jar")) {
+                String ssp = uri.getSchemeSpecificPart();
+                int sep = ssp.lastIndexOf("!");
+                if (ssp.startsWith("file:") && sep > 0) {
+                    enclosingJar = Paths.get(URI.create(ssp.substring(0, sep)));
+                }
+            }
+        }
+
+        Path nf = normalize(file);
+        for (Path p : searchPath) {
+            Path np = normalize(p);
+            if (np.getFileSystem() == nf.getFileSystem()
+                    && Files.isDirectory(np)
+                    && nf.startsWith(np)) {
+                return true;
+            }
+            if (enclosingJar != null
+                    && Files.isSameFile(enclosingJar, np)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
     /**
      * Utility class to help evaluate a path option. Duplicate entries are ignored, jar class paths
      * can be expanded.
@@ -456,6 +492,11 @@
         Iterable<Set<Location>> listLocationsForModules() throws IOException {
             return null;
         }
+
+        /**
+         * @see JavaFileManager#contains
+         */
+        abstract boolean contains(Path file) throws IOException;
     }
 
     /**
@@ -611,6 +652,15 @@
 
             return Collections.singleton(moduleTable.locations());
         }
+
+        @Override
+        boolean contains(Path file) throws IOException {
+            if (moduleTable != null) {
+                return moduleTable.contains(file);
+            } else {
+                return (outputDir) != null && normalize(file).startsWith(normalize(outputDir));
+            }
+        }
     }
 
     /**
@@ -660,6 +710,11 @@
         protected SearchPath createPath() {
             return new SearchPath();
         }
+
+        @Override
+        boolean contains(Path file) throws IOException {
+            return Locations.this.contains(searchPath, file);
+        }
     }
 
     /**
@@ -879,13 +934,18 @@
         private void lazy() {
             if (searchPath == null) {
                 try {
-                searchPath = Collections.unmodifiableCollection(computePath());
+                    searchPath = Collections.unmodifiableCollection(computePath());
                 } catch (IOException e) {
                     // TODO: need better handling here, e.g. javac Abort?
                     throw new UncheckedIOException(e);
                 }
             }
         }
+
+        @Override
+        boolean contains(Path file) throws IOException {
+            return Locations.this.contains(searchPath, file);
+        }
     }
 
     /**
@@ -896,7 +956,7 @@
      * The Location can be specified to accept overriding classes from the
      * {@code --patch-module <module>=<path> } parameter.
      */
-    private static class ModuleLocationHandler extends LocationHandler implements Location {
+    private class ModuleLocationHandler extends LocationHandler implements Location {
         private final LocationHandler parent;
         private final String name;
         private final String moduleName;
@@ -949,6 +1009,11 @@
         }
 
         @Override
+        boolean contains(Path file) throws IOException {
+            return Locations.this.contains(searchPath, file);
+        }
+
+        @Override
         public String toString() {
             return name;
         }
@@ -957,7 +1022,7 @@
     /**
      * A table of module location handlers, indexed by name and path.
      */
-    private static class ModuleTable {
+    private class ModuleTable {
         private final Map<String, ModuleLocationHandler> nameMap = new LinkedHashMap<>();
         private final Map<Path, ModuleLocationHandler> pathMap = new LinkedHashMap<>();
 
@@ -1008,6 +1073,10 @@
             return nameMap.isEmpty();
         }
 
+        boolean contains(Path file) throws IOException {
+            return Locations.this.contains(pathMap.keySet(), file);
+        }
+
         Set<Location> locations() {
             return Collections.unmodifiableSet(nameMap.values().stream().collect(Collectors.toSet()));
         }
@@ -1040,6 +1109,12 @@
         }
 
         @Override
+        public Location getLocationForModule(Path file) {
+            initModuleLocations();
+            return moduleTable.get(file);
+        }
+
+        @Override
         Iterable<Set<Location>> listLocationsForModules() {
             if (searchPath == null)
                 return Collections.emptyList();
@@ -1048,6 +1123,14 @@
         }
 
         @Override
+        boolean contains(Path file) throws IOException {
+            if (moduleTable == null) {
+                initModuleLocations();
+            }
+            return moduleTable.contains(file);
+        }
+
+        @Override
         void setPaths(Iterable<? extends Path> paths) {
             if (paths != null) {
                 for (Path p: paths) {
@@ -1592,6 +1675,11 @@
             return Collections.singleton(moduleTable.locations());
         }
 
+        @Override
+        boolean contains(Path file) throws IOException {
+            return (moduleTable == null) ? false : moduleTable.contains(file);
+        }
+
     }
 
     private class SystemModulesLocationHandler extends BasicLocationHandler {
@@ -1698,6 +1786,12 @@
             return Collections.singleton(moduleTable.locations());
         }
 
+        @Override
+        boolean contains(Path file) throws IOException {
+            initSystemModules();
+            return moduleTable.contains(file);
+        }
+
         private void initSystemModules() throws IOException {
             if (moduleTable != null)
                 return;
@@ -1828,6 +1922,11 @@
         Iterable<Set<Location>> listLocationsForModules() throws IOException {
             return Collections.singleton(moduleTable.locations());
         }
+
+        @Override
+        boolean contains(Path file) throws IOException {
+            return moduleTable.contains(file);
+        }
     }
 
     Map<Location, LocationHandler> handlersForLocation;
@@ -1931,6 +2030,13 @@
         return (h == null ? null : h.listLocationsForModules());
     }
 
+    boolean contains(Location location, Path file) throws IOException {
+        LocationHandler h = getHandler(location);
+        if (h == null)
+            throw new IllegalArgumentException("unknown location");
+        return h.contains(file);
+    }
+
     protected LocationHandler getHandler(Location location) {
         Objects.requireNonNull(location);
         return (location instanceof LocationHandler)
--- a/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/src/jdk.compiler/share/classes/com/sun/tools/javac/resources/compiler.properties	Mon Apr 24 14:59:43 2017 -0700
@@ -947,7 +947,7 @@
     package annotations should be in file package-info.java
 
 compiler.err.no.pkg.in.module-info.java=\
-    package clauses should not be in file module-info.java
+    package declarations not allowed in file module-info.java
 
 # 0: symbol
 compiler.err.pkg.clashes.with.class.of.same.name=\
@@ -1304,6 +1304,9 @@
 compiler.err.locn.invalid.arg.for.xpatch=\
     invalid argument for --patch-module option: {0}
 
+compiler.err.file.sb.on.source.or.patch.path.for.module=\
+    file should be on source path, or on patch path for module
+
 #####
 
 # Fatal Errors
--- a/langtools/src/jdk.jshell/share/classes/jdk/jshell/MemoryFileManager.java	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/src/jdk.jshell/share/classes/jdk/jshell/MemoryFileManager.java	Mon Apr 24 14:59:43 2017 -0700
@@ -559,6 +559,11 @@
         return stdFileManager.listLocationsForModules(location);
     }
 
+    @Override
+    public boolean contains(Location location, FileObject file) throws IOException {
+        return stdFileManager.contains(location, file);
+    }
+
     /**
      * Flushes any resources opened for output by this file manager
      * directly or indirectly.  Flushing a closed file manager has no
--- a/langtools/test/tools/doclint/ProvidesTest.java	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/test/tools/doclint/ProvidesTest.java	Mon Apr 24 14:59:43 2017 -0700
@@ -10,20 +10,20 @@
 /**
  * Invalid use of provides in class documentation.
  *
- * @provides UsesTest
+ * @provides NotFound
  */
 public class ProvidesTest {
     /**
      * Invalid use of provides in field documentation
      *
-     * @provides UsesTest Test description.
+     * @provides NotFound Test description.
      */
     public int invalid_param;
 
     /**
      * Invalid use of provides in method documentation
      *
-     * @provides UsesTest Test description.
+     * @provides NotFound Test description.
      */
     public class InvalidParam { }
 }
--- a/langtools/test/tools/doclint/ProvidesTest.out	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/test/tools/doclint/ProvidesTest.out	Mon Apr 24 14:59:43 2017 -0700
@@ -1,28 +1,28 @@
 ProvidesTest.java:13: error: invalid use of @provides
- * @provides UsesTest
+ * @provides NotFound
    ^
 ProvidesTest.java:13: error: service-type not found
- * @provides UsesTest
+ * @provides NotFound
    ^
 ProvidesTest.java:13: error: reference not found
- * @provides UsesTest
+ * @provides NotFound
              ^
 ProvidesTest.java:19: error: invalid use of @provides
-     * @provides UsesTest Test description.
+     * @provides NotFound Test description.
        ^
 ProvidesTest.java:19: error: service-type not found
-     * @provides UsesTest Test description.
+     * @provides NotFound Test description.
        ^
 ProvidesTest.java:19: error: reference not found
-     * @provides UsesTest Test description.
+     * @provides NotFound Test description.
                  ^
 ProvidesTest.java:26: error: invalid use of @provides
-     * @provides UsesTest Test description.
+     * @provides NotFound Test description.
        ^
 ProvidesTest.java:26: error: service-type not found
-     * @provides UsesTest Test description.
+     * @provides NotFound Test description.
        ^
 ProvidesTest.java:26: error: reference not found
-     * @provides UsesTest Test description.
+     * @provides NotFound Test description.
                  ^
 9 errors
--- a/langtools/test/tools/doclint/UsesTest.java	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/test/tools/doclint/UsesTest.java	Mon Apr 24 14:59:43 2017 -0700
@@ -10,20 +10,20 @@
 /**
  * Invalid use of uses in class documentation.
  *
- * @uses ProvidesTest
+ * @uses NotFound
  */
 public class UsesTest {
     /**
      * Invalid use of uses in field documentation
      *
-     * @uses ProvidesTest Test description.
+     * @uses NotFound Test description.
      */
     public int invalid_param;
 
     /**
      * Invalid use of uses in method documentation
      *
-     * @uses ProvidesTest Test description.
+     * @uses NotFound Test description.
      */
     public class InvalidParam { }
 }
--- a/langtools/test/tools/doclint/UsesTest.out	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/test/tools/doclint/UsesTest.out	Mon Apr 24 14:59:43 2017 -0700
@@ -1,28 +1,28 @@
 UsesTest.java:13: error: invalid use of @uses
- * @uses ProvidesTest
+ * @uses NotFound
    ^
 UsesTest.java:13: error: service-type not found
- * @uses ProvidesTest
+ * @uses NotFound
    ^
 UsesTest.java:13: error: reference not found
- * @uses ProvidesTest
+ * @uses NotFound
          ^
 UsesTest.java:19: error: invalid use of @uses
-     * @uses ProvidesTest Test description.
+     * @uses NotFound Test description.
        ^
 UsesTest.java:19: error: service-type not found
-     * @uses ProvidesTest Test description.
+     * @uses NotFound Test description.
        ^
 UsesTest.java:19: error: reference not found
-     * @uses ProvidesTest Test description.
+     * @uses NotFound Test description.
              ^
 UsesTest.java:26: error: invalid use of @uses
-     * @uses ProvidesTest Test description.
+     * @uses NotFound Test description.
        ^
 UsesTest.java:26: error: service-type not found
-     * @uses ProvidesTest Test description.
+     * @uses NotFound Test description.
        ^
 UsesTest.java:26: error: reference not found
-     * @uses ProvidesTest Test description.
+     * @uses NotFound Test description.
              ^
 9 errors
--- a/langtools/test/tools/javac/api/TestClientCodeWrapper.java	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/test/tools/javac/api/TestClientCodeWrapper.java	Mon Apr 24 14:59:43 2017 -0700
@@ -62,7 +62,7 @@
             defaultFileManager = fm;
 
             for (Method m: getMethodsExcept(JavaFileManager.class,
-                        "close", "getJavaFileForInput", "getLocationForModule", "getServiceLoader")) {
+                        "close", "getJavaFileForInput", "getLocationForModule", "getServiceLoader", "contains")) {
                 test(m);
             }
 
@@ -424,6 +424,12 @@
             return super.listLocationsForModules(location);
         }
 
+        @Override
+        public boolean contains(Location location, FileObject fo) throws IOException {
+            throwUserExceptionIfNeeded(fileManagerMethod, "contains");
+            return super.contains(location, fo);
+        }
+
         public FileObject wrap(FileObject fo) {
             if (fileObjectMethod == null || fo == null)
                 return fo;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/FileShouldBeOnSourcePathOrPatchPath/FileShouldBeOnSourcePathOrModulePath.java	Mon Apr 24 14:59:43 2017 -0700
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2017, 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.err.file.sb.on.source.or.patch.path.for.module
+
+public class FileShouldBeOnSourcePathOrModulePath {
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/diags/examples/FileShouldBeOnSourcePathOrPatchPath/sourcepath/module-info.java	Mon Apr 24 14:59:43 2017 -0700
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2017, 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 m { }
+
--- a/langtools/test/tools/javac/file/ModuleAndPackageLocations.java	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/test/tools/javac/file/ModuleAndPackageLocations.java	Mon Apr 24 14:59:43 2017 -0700
@@ -117,7 +117,6 @@
             assertRefused(() -> fm.getJavaFileForOutput(StandardLocation.MODULE_SOURCE_PATH, "", Kind.SOURCE, null));
             assertRefused(() -> fm.getLocationForModule(StandardLocation.SOURCE_PATH, "test"));
             JavaFileObject out = fm.getJavaFileForInput(StandardLocation.CLASS_OUTPUT, "test.Test", Kind.CLASS);
-            assertRefused(() -> fm.getLocationForModule(StandardLocation.SOURCE_PATH, out));
             assertRefused(() -> fm.inferBinaryName(StandardLocation.MODULE_PATH, out));
             assertRefused(() -> fm.inferModuleName(StandardLocation.MODULE_SOURCE_PATH));
             assertRefused(() -> fm.list(StandardLocation.MODULE_SOURCE_PATH, "test", EnumSet.allOf(Kind.class), false));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/modules/ContainsTest.java	Mon Apr 24 14:59:43 2017 -0700
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2017, 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
+ * @bug 8178518
+ * @summary Add method JavaFileManager.contains
+ * @library /tools/lib
+ * @modules
+ *      jdk.compiler/com.sun.tools.javac.api
+ *      jdk.compiler/com.sun.tools.javac.main
+ * @build toolbox.ToolBox toolbox.JarTask toolbox.JavacTask ModuleTestBase
+ * @run main ContainsTest
+ */
+
+import java.io.IOException;
+import java.nio.file.FileSystem;
+import java.nio.file.FileSystems;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.EnumSet;
+import java.util.List;
+
+import javax.tools.FileObject;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileManager.Location;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.StandardLocation;
+import javax.tools.ToolProvider;
+
+import toolbox.JarTask;
+import toolbox.JavacTask;
+
+public class ContainsTest extends ModuleTestBase {
+    public static void main(String... args) throws Exception {
+        ContainsTest t = new ContainsTest();
+        t.runTests();
+    }
+
+    JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
+
+    @Test
+    public void testSimplePath(Path base) throws IOException {
+        // Test that we can look up in directories in the default file system.
+        Path src = base.resolve("src");
+        tb.writeJavaFiles(src, "package p; class C { }");
+        Path c = src.resolve("p/C.java");
+        Path x = base.resolve("src2/p/C.java");
+        try (StandardJavaFileManager fm = javaCompiler.getStandardFileManager(null, null, null)) {
+            fm.setLocationFromPaths(StandardLocation.SOURCE_PATH, List.of(src));
+            checkContains(fm, StandardLocation.SOURCE_PATH, c, true);
+            checkContains(fm, StandardLocation.SOURCE_PATH, x, false);
+        }
+    }
+
+    @Test
+    public void testJarPath(Path base) throws IOException {
+        // Test that we can look up in jar files on a search path.
+        // In this case, the path we look up must come from open file system
+        // as used by the file manager.
+        Path src = base.resolve("src");
+        tb.writeJavaFiles(src, "package p; class C { }");
+        Path classes = Files.createDirectories(base.resolve("classes"));
+        new JavacTask(tb)
+            .options("-sourcepath", src.toString())
+            .outdir(classes)
+            .files(findJavaFiles(src))
+            .run()
+            .writeAll();
+
+        Path jar = base.resolve("classes.jar");
+        new JarTask(tb).run("cf", jar.toString(), "-C", classes.toString(), "p");
+
+        Path c = src.resolve("p/C.java");
+        Path x = base.resolve("src2/p/C.java");
+
+        try (StandardJavaFileManager fm = javaCompiler.getStandardFileManager(null, null, null)) {
+            fm.setLocationFromPaths(StandardLocation.CLASS_PATH, List.of(src, jar));
+
+            checkContains(fm, StandardLocation.CLASS_PATH, c, true);
+            checkContains(fm, StandardLocation.CLASS_PATH, x, false);
+
+            JavaFileObject fo = fm.list(StandardLocation.CLASS_PATH, "p",
+                    EnumSet.of(JavaFileObject.Kind.CLASS), false).iterator().next();
+
+            checkContains(fm, StandardLocation.CLASS_PATH, fo, true);
+        }
+    }
+
+    @Test
+    public void testJarFSPath(Path base) throws IOException {
+        // Test that we can look up in non-default file systems on the search path,
+        // such as an open jar file system.
+        Path src = base.resolve("src");
+        tb.writeJavaFiles(src, "package p; class C { }");
+        Path classes = Files.createDirectories(base.resolve("classes"));
+        new JavacTask(tb)
+            .options("-sourcepath", src.toString())
+            .outdir(classes)
+            .files(findJavaFiles(src))
+            .run()
+            .writeAll();
+
+        Path jar = base.resolve("classes.jar");
+        new JarTask(tb).run("cf", jar.toString(), "-C", classes.toString(), "p");
+
+        Path c = src.resolve("p/C.java");
+        Path x = base.resolve("src2/p/C.java");
+
+        try (FileSystem jarFS = FileSystems.newFileSystem(jar, null);
+                StandardJavaFileManager fm = javaCompiler.getStandardFileManager(null, null, null)) {
+            Path jarRoot = jarFS.getRootDirectories().iterator().next();
+            fm.setLocationFromPaths(StandardLocation.CLASS_PATH, List.of(src, jarRoot));
+
+            checkContains(fm, StandardLocation.CLASS_PATH, c, true);
+            checkContains(fm, StandardLocation.CLASS_PATH, x, false);
+
+            JavaFileObject fo = fm.list(StandardLocation.CLASS_PATH, "p",
+                    EnumSet.of(JavaFileObject.Kind.CLASS), false).iterator().next();
+
+            checkContains(fm, StandardLocation.CLASS_PATH, fo, true);
+            checkContains(fm, StandardLocation.CLASS_PATH, jarRoot.resolve("p/C.class"), true);
+        }
+    }
+
+    void checkContains(StandardJavaFileManager fm, Location l, Path p, boolean expect) throws IOException {
+        JavaFileObject fo = fm.getJavaFileObjects(p).iterator().next();
+        checkContains(fm, l, fo, expect);
+    }
+
+    void checkContains(StandardJavaFileManager fm, Location l, FileObject fo, boolean expect) throws IOException {
+        boolean found = fm.contains(l, fo);
+        if (found) {
+            if (expect) {
+                out.println("file found, as expected: " + l + " " + fo.getName());
+            } else {
+                error("file not found: " + l + " " + fo.getName());
+            }
+        } else {
+            if (expect) {
+                error("file found unexpectedly: " + l + " " + fo.getName());
+            } else {
+                out.println("file not found, as expected: " + l + " " + fo.getName());
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/langtools/test/tools/javac/modules/SourcePathTest.java	Mon Apr 24 14:59:43 2017 -0700
@@ -0,0 +1,310 @@
+/*
+ * Copyright (c) 2017, 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
+ * @bug 8176327
+ * @summary javac produces wrong module-info
+ * @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 SourcePathTest
+ */
+
+import java.io.IOException;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+
+import toolbox.JavacTask;
+import toolbox.Task;
+
+public class SourcePathTest extends ModuleTestBase {
+    public static void main(String... args) throws Exception {
+        SourcePathTest t = new SourcePathTest();
+        t.runTests();
+    }
+
+    @Test
+    public void test_unnamedModuleOnSourcePath_fileNotOnPath(Path base) throws IOException {
+        Path src = base.resolve("src");
+        tb.writeJavaFiles(src, "package p; public class A { }");
+        Path otherSrc = base.resolve("otherSrc");
+        tb.writeJavaFiles(otherSrc, "package p2; public class B { }");
+
+        Path classes = base.resolve("classes");
+        Files.createDirectories(classes);
+
+        new JavacTask(tb)
+            .options("-sourcepath", src.toString())
+            .outdir(classes)
+            .files(findJavaFiles(otherSrc))
+            .run()
+            .writeAll();
+
+        showFiles(classes);
+    }
+
+    @Test
+    public void test_unnamedModuleOnSourcePath_fileOnPath(Path base) throws IOException {
+        Path src = base.resolve("src");
+        tb.writeJavaFiles(src, "package p; public class A { }");
+
+        Path classes = base.resolve("classes");
+        Files.createDirectories(classes);
+
+        new JavacTask(tb)
+            .options("-sourcepath", src.toString())
+            .outdir(classes)
+            .files(findJavaFiles(src))
+            .run()
+            .writeAll();
+
+        showFiles(classes);
+    }
+
+    @Test
+    public void test_namedModuleOnSourcePath_fileNotOnPath_1(Path base) throws Exception {
+        Path src = base.resolve("src");
+        tb.writeFile(src.resolve("module-info.java"), "module m { exports p; }");
+        tb.writeJavaFiles(src, "package p; public class A { }");
+        Path otherSrc = base.resolve("otherSrc");
+        tb.writeJavaFiles(otherSrc, "package p2; public class B { }");
+
+        Path classes = base.resolve("classes");
+        Files.createDirectories(classes);
+
+        String log = new JavacTask(tb)
+            .options("-XDrawDiagnostics=true", "-sourcepath", src.toString())
+            .outdir(classes)
+            .files(findJavaFiles(otherSrc))
+            .run(Task.Expect.FAIL)
+            .writeAll()
+            .getOutput(Task.OutputKind.DIRECT);
+
+        showFiles(classes);
+        checkOutputContains(log,
+                "B.java:1:1: compiler.err.file.sb.on.source.or.patch.path.for.module");
+    }
+
+    @Test
+    public void test_namedModuleOnSourcePath_fileNotOnPath_2(Path base) throws Exception {
+        // This is the original test case:
+        // the source path contains one module, but the file to be compiled appears to be
+        // in another module.
+        Path src = base.resolve("src");
+        Path src_mA = src.resolve("mA");
+        tb.writeJavaFiles(src_mA,
+                "module mA { exports p; }",
+                "package p; public class A { }");
+        Path src_mB = src.resolve("mB");
+        tb.writeJavaFiles(src_mB,
+                "module mA { exports p2; }",
+                "package p2; public class B { }");
+
+        Path classes = base.resolve("classes");
+        Files.createDirectories(classes);
+
+        String log = new JavacTask(tb)
+            .options("-XDrawDiagnostics=true", "-sourcepath", src_mA.toString())
+            .outdir(classes)
+            .files(findJavaFiles(src_mB.resolve("p2")))
+            .run(Task.Expect.FAIL)
+            .writeAll()
+            .getOutput(Task.OutputKind.DIRECT);
+
+        showFiles(classes);
+        checkOutputContains(log,
+                "B.java:1:1: compiler.err.file.sb.on.source.or.patch.path.for.module");
+    }
+
+    @Test
+    public void test_namedModuleOnSourcePath_fileOnPath(Path base) throws Exception {
+        Path src = base.resolve("src");
+        tb.writeFile(src.resolve("module-info.java"), "module m { exports p; }");
+        tb.writeJavaFiles(src, "package p; public class A { }");
+
+        Path classes = base.resolve("classes");
+        Files.createDirectories(classes);
+
+        String log = new JavacTask(tb)
+            .options("-XDrawDiagnostics=true", "-sourcepath", src.toString())
+            .outdir(classes)
+            .files(findJavaFiles(src.resolve("p")))
+            .run()
+            .writeAll()
+            .getOutput(Task.OutputKind.DIRECT);
+
+        showFiles(classes);
+    }
+
+    @Test
+    public void test_namedModuleOnSourcePath_fileOnPatchPath(Path base) throws Exception {
+        // similar to test_namedModuleOnSourcePath_fileNotOnPath_1
+        // except that other src directory is not put on the patch path
+        Path src = base.resolve("src");
+        tb.writeFile(src.resolve("module-info.java"), "module m { exports p; }");
+        tb.writeJavaFiles(src, "package p; public class A { }");
+        Path otherSrc = base.resolve("otherSrc");
+        tb.writeJavaFiles(otherSrc, "package p2; public class B { }");
+
+        Path classes = base.resolve("classes");
+        Files.createDirectories(classes);
+
+        String log = new JavacTask(tb)
+            .options("-XDrawDiagnostics=true",
+                    "-sourcepath", src.toString(),
+                    "--patch-module", "m=" + otherSrc)
+            .outdir(classes)
+            .files(findJavaFiles(otherSrc))
+            .run()
+            .writeAll()
+            .getOutput(Task.OutputKind.DIRECT);
+
+        showFiles(classes);
+    }
+
+    /*
+     * The following tests are not for the source path, but they exercise similar test
+     * cases for the module source path.
+     */
+
+    @Test
+    public void test_moduleSourcePath_fileNotOnPath(Path base) throws Exception {
+        Path src = base.resolve("src");
+        Path src_mA = src.resolve("mA");
+        tb.writeJavaFiles(src_mA,
+                "module mA { exports p; }",
+                "package p; public class A { }");
+        Path src_mB = src.resolve("mB");
+        tb.writeJavaFiles(src_mB,
+                "module mB { exports p2; }",
+                "package p2; public class B { }");
+        Path otherSrc = base.resolve("otherSrc");
+        tb.writeJavaFiles(otherSrc, "package p3; public class C { }");
+
+        Path classes = base.resolve("classes");
+        Files.createDirectories(classes);
+
+        String log = new JavacTask(tb)
+            .options("-XDrawDiagnostics=true",
+                    "--module-source-path", src.toString())
+            .outdir(classes)
+            .files(findJavaFiles(otherSrc))
+            .run(Task.Expect.FAIL)
+            .writeAll()
+            .getOutput(Task.OutputKind.DIRECT);
+
+        showFiles(classes);
+        checkOutputContains(log,
+                "C.java:1:1: compiler.err.not.in.module.on.module.source.path");
+    }
+
+    @Test
+    public void test_moduleSourcePath_fileOnPath(Path base) throws Exception {
+        Path src = base.resolve("src");
+        Path src_mA = src.resolve("mA");
+        tb.writeJavaFiles(src_mA,
+                "module mA { exports p; }",
+                "package p; public class A { }");
+        Path src_mB = src.resolve("mB");
+        tb.writeJavaFiles(src_mB,
+                "module mB { exports p2; }",
+                "package p2; public class B { }");
+
+        Path classes = base.resolve("classes");
+        Files.createDirectories(classes);
+
+        String log = new JavacTask(tb)
+            .options("-XDrawDiagnostics=true",
+                    "--module-source-path", src.toString())
+            .outdir(classes)
+            .files(findJavaFiles(src_mB.resolve("p2")))
+            .run()
+            .writeAll()
+            .getOutput(Task.OutputKind.DIRECT);
+
+        showFiles(classes);
+
+    }
+
+    @Test
+    public void test_moduleSourcePath_fileOnPatchPath(Path base) throws Exception {
+        // similar to test_moduleSourcePath_fileNotOnPath
+        // except that other src directory is not put on the patch path
+        Path src = base.resolve("src");
+        Path src_mA = src.resolve("mA");
+        tb.writeJavaFiles(src_mA,
+                "module mA { exports p; }",
+                "package p; public class A { }");
+        Path src_mB = src.resolve("mB");
+        tb.writeJavaFiles(src_mB,
+                "module mB { exports p2; }",
+                "package p2; public class B { }");
+        Path otherSrc = base.resolve("otherSrc");
+        tb.writeJavaFiles(otherSrc, "package p3; public class C { }");
+
+        Path classes = base.resolve("classes");
+        Files.createDirectories(classes);
+
+        String log = new JavacTask(tb)
+            .options("-XDrawDiagnostics=true",
+                    "--module-source-path", src.toString(),
+                    "--patch-module", "mA=" + otherSrc)
+            .outdir(classes)
+            .files(findJavaFiles(otherSrc))
+            .run()
+            .writeAll()
+            .getOutput(Task.OutputKind.DIRECT);
+
+        showFiles(classes);
+    }
+
+    /**
+     * This method is primarily used to give visual confirmation that a test case
+     * generated files when the compilation succeeds and so generates no other output,
+     * such as error messages.
+     */
+    List<Path> showFiles(Path dir) throws IOException {
+        List<Path> files = new ArrayList<>();
+        Files.walkFileTree(dir, new SimpleFileVisitor<Path>() {
+            @Override
+            public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
+                    throws IOException {
+                if (Files.isRegularFile(file)) {
+                    out.println("Found " + file);
+                    files.add(file);
+                }
+                return FileVisitResult.CONTINUE;
+            }
+        });
+        return files;
+    }
+}
+
--- a/langtools/test/tools/javac/modules/T8158224/T8158224.java	Mon Apr 24 18:58:50 2017 +0200
+++ b/langtools/test/tools/javac/modules/T8158224/T8158224.java	Mon Apr 24 14:59:43 2017 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2016, 2017, 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
@@ -25,8 +25,41 @@
  * @test
  * @bug 8158224
  * @summary NullPointerException in com.sun.tools.javac.comp.Modules.checkCyclicDependencies when module missing
+ * @library /tools/lib
+ * @modules
+ *      jdk.compiler/com.sun.tools.javac.api
+ *      jdk.compiler/com.sun.tools.javac.main
+ * @build toolbox.ToolBox toolbox.JavacTask
  * @build Processor
- * @compile/fail/ref=T8158224.out -XDrawDiagnostics -processor Processor mods/foo/module-info.java
+ * @run main T8158224
  */
 
-// No code here, this file is just to host test description.
+// previously:
+// @compile/fail/ref=T8158224.out -XDrawDiagnostics -processor Processor mods/foo/module-info.java
+
+import java.util.List;
+import toolbox.JavacTask;
+import toolbox.Task;
+import toolbox.ToolBox;
+
+public class T8158224 {
+    public static void main(String... args) throws Exception {
+        ToolBox tb = new ToolBox();
+
+        List<String> log = new JavacTask(tb)
+                .options("-XDrawDiagnostics",
+                        "-processor", "Processor",
+                        "-sourcepath", tb.testSrc + "/mods/foo",
+                        "-classpath", tb.testClasses)
+                .files(tb.testSrc + "/mods/foo/module-info.java")
+                .run(Task.Expect.FAIL)
+                .writeAll()
+                .getOutputLines(Task.OutputKind.DIRECT);
+
+        if (!log.equals(List.of(
+                "module-info.java:4:14: compiler.err.module.not.found: nonexistent",
+                "1 error"))) {
+            throw new Exception("Expected output not found");
+        }
+    }
+}
--- a/langtools/test/tools/javac/modules/T8158224/T8158224.out	Mon Apr 24 18:58:50 2017 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-module-info.java:4:14: compiler.err.module.not.found: nonexistent
-1 error