Bug 525713 - [9] Warn when consuming auto modules with unstable names

Change-Id: I372832d10a916c1604a88049bd5166ef928c2f2b
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
index 8a5a7b1..625090b 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/BatchCompilerTest.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2017 IBM Corporation and others.
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -858,6 +858,7 @@
         "							methods\n" + 
         "      missingJavadocCommentsVisibility(<visibility>)  specify visibility\n" + 
         "							modifier for missing javadoc comments warnings\n" + 
+        "      module             + module related problems.\n" + 
         "      nls                  string literal lacking non-nls tag //$NON-NLS-<n>$\n" + 
         "      noEffectAssign     + assignment without effect\n" + 
         "      null                 potential missing or redundant null check\n" + 
@@ -1107,6 +1108,7 @@
 			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unnecessaryElse\" value=\"ignore\"/>\n" + 
 			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck\" value=\"ignore\"/>\n" + 
 			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess\" value=\"ignore\"/>\n" + 
+			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName\" value=\"warning\"/>\n" + 
 			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException\" value=\"ignore\"/>\n" + 
 			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable\" value=\"enabled\"/>\n" + 
 			"		<option key=\"org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference\" value=\"enabled\"/>\n" + 
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java
index dbc38f8..4388bbd 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java
@@ -1107,6 +1107,7 @@
 		expectedProblemAttributes.put("UnsafeRawMethodInvocation", new ProblemAttributes(CategorizedProblem.CAT_UNCHECKED_RAW));
 		expectedProblemAttributes.put("UnsafeReturnTypeOverride", new ProblemAttributes(CategorizedProblem.CAT_UNCHECKED_RAW));
 		expectedProblemAttributes.put("UnsafeTypeConversion", new ProblemAttributes(CategorizedProblem.CAT_UNCHECKED_RAW));
+		expectedProblemAttributes.put("UnstableAutoModuleName", new ProblemAttributes(CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM));
 		expectedProblemAttributes.put("UnterminatedComment", new ProblemAttributes(CategorizedProblem.CAT_SYNTAX));
 		expectedProblemAttributes.put("UnterminatedString", new ProblemAttributes(CategorizedProblem.CAT_SYNTAX));
 		expectedProblemAttributes.put("UnusedConstructorDeclaredThrownException", new ProblemAttributes(CategorizedProblem.CAT_UNNECESSARY_CODE));
@@ -2002,6 +2003,7 @@
 		expectedProblemAttributes.put("UnsafeRawMethodInvocation", new ProblemAttributes(JavaCore.COMPILER_PB_UNCHECKED_TYPE_OPERATION));
 		expectedProblemAttributes.put("UnsafeReturnTypeOverride", new ProblemAttributes(JavaCore.COMPILER_PB_UNCHECKED_TYPE_OPERATION));
 		expectedProblemAttributes.put("UnsafeTypeConversion", new ProblemAttributes(JavaCore.COMPILER_PB_UNCHECKED_TYPE_OPERATION));
+		expectedProblemAttributes.put("UnstableAutoModuleName", new ProblemAttributes(JavaCore.COMPILER_PB_UNSTABLE_AUTO_MODULE_NAME));
 		expectedProblemAttributes.put("UnterminatedComment", SKIP);
 		expectedProblemAttributes.put("UnterminatedString", SKIP);
 		expectedProblemAttributes.put("UnusedConstructorDeclaredThrownException", new ProblemAttributes(JavaCore.COMPILER_PB_UNUSED_DECLARED_THROWN_EXCEPTION));
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java
index 3a171c0..9a5a624 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/ModuleCompilationTests.java
@@ -1985,6 +1985,7 @@
 			.append(Util.getJavaClassLibsAsString()).append("\" ")
 			.append("-p \"")
 			.append(LIB_DIR).append("\" ")
+			.append(" -warn:-module ")
 			.append(" --module-source-path " + "\"" + directory + "\"");
 		runConformModuleTest(files, 
 				buffer,
@@ -3432,12 +3433,19 @@
 			.append(" -classpath \"")
 			.append(Util.getJavaClassLibsAsString())
 			.append("\" ")
+			.append(" -info:+module ")
 			.append(" --module-path " + "\"" + jarPath + "\"");
 
 		runConformModuleTest(files, 
 			buffer,
 			"",
-			"",
+			"----------\n" + 
+			"1. INFO in ---OUTPUT_DIR_PLACEHOLDER---/src/mod.one/module-info.java (at line 2)\n" + 
+			"	requires lib.x;\n" + 
+			"	         ^^^^^\n" + 
+			"Name of automatic module \'lib.x\' is unstable, it is derived from the module\'s file name.\n" + 
+			"----------\n" + 
+			"1 problem (1 info)\n",
 			false,
 			OUTPUT_DIR + File.separator + out);
 	}
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModuleBuilderTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModuleBuilderTests.java
index 7b63883..f90cefc 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModuleBuilderTests.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ModuleBuilderTests.java
@@ -3871,6 +3871,7 @@
 					new IClasspathAttribute[] {modAttr},
 					false/*not exported*/);
 			IJavaProject p2 = setupModuleProject("com.greetings", src, new IClasspathEntry[] { dep });
+			p2.setOption(JavaCore.COMPILER_PB_UNSTABLE_AUTO_MODULE_NAME, JavaCore.IGNORE);
 
 			getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, null);
 			IMarker[] markers = p2.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE);
@@ -4096,6 +4097,7 @@
 				new IClasspathAttribute[] {modAttr},
 				false/*not exported*/);
 			IJavaProject p3 = setupModuleProject("test_automodules", src, new IClasspathEntry[] {dep, dep2});
+			p3.setOption(JavaCore.COMPILER_PB_UNSTABLE_AUTO_MODULE_NAME, JavaCore.IGNORE);
 			getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, null);
 			IMarker[] markers = p3.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE);
 			assertMarkers("Unexpected markers", "", markers);
@@ -4167,6 +4169,7 @@
 				new IClasspathAttribute[] {modAttr},
 				false/*not exported*/);
 			IJavaProject p3 = setupModuleProject("test_automodules", src, new IClasspathEntry[] {dep, dep2});
+			p3.setOption(JavaCore.COMPILER_PB_UNSTABLE_AUTO_MODULE_NAME, JavaCore.IGNORE);
 			getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, null);
 			IMarker[] markers = p3.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE);
 			sortMarkers(markers);
@@ -4872,7 +4875,9 @@
 			IJavaProject p3 = setupModuleProject("test", src, new IClasspathEntry[] {dep, dep2});
 			getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, null);
 			IMarker[] markers = p3.getProject().findMarkers(null, true, IResource.DEPTH_INFINITE);
-			assertMarkers("Unexpected markers", "", markers);
+			assertMarkers("Unexpected markers", 
+					"Name of automatic module \'com.greetings\' is unstable, it is derived from the module\'s file name.",
+					markers);
 		} finally {
 			this.deleteProject("test");
 			this.deleteProject("com.greetings");
@@ -5079,8 +5084,12 @@
 			
 			this.problemRequestor.initialize(srcMod.toCharArray());
 			getWorkingCopy("/mod.one/module-info.java", srcMod, true);
-			assertProblems("module-info should have no problems",
+			assertProblems("module-info should have one warning",
 					"----------\n" + 
+					"1. WARNING in /mod.one/module-info.java (at line 2)\n" + 
+					"	requires lib.x;\n" + 
+					"	         ^^^^^\n" + 
+					"Name of automatic module \'lib.x\' is unstable, it is derived from the module\'s file name.\n" + 
 					"----------\n",
 					this.problemRequestor);
 
@@ -5307,7 +5316,7 @@
 				deleteProject(javaProject2);
 		}
 	}
-	// like testAutoModule3 without name derived from project, not manifest
+	// like testAutoModule3 without name derived from project, not manifest - warning suppressed
 	public void testAutoModule5() throws Exception {
 		if (!isJRE9) return;
 		IJavaProject javaProject = null, auto = null;
@@ -5323,6 +5332,7 @@
 			addClasspathEntry(javaProject, JavaCore.newProjectEntry(auto.getPath(), null, false, attributes, false));
 
 			String srcMod =
+				"@SuppressWarnings(\"module\")\n" +
 				"module mod.one { \n" +
 				"	requires auto;\n" +
 				"}";
@@ -5360,6 +5370,47 @@
 				deleteProject(auto);
 		}
 	}
+	// like testAutoModule5, warning configured as ERROR
+	public void testAutoModule6() throws Exception {
+		if (!isJRE9) return;
+		IJavaProject javaProject = null, auto = null;
+		try {
+			auto = createJava9Project("auto", new String[] {"src"});
+			createFolder("auto/src/p/a");
+			createFile("auto/src/p/a/X.java",
+				"package p.a;\n" +
+				"public class X {}\n;");
+
+			javaProject = createJava9Project("mod.one", new String[] {"src"});
+			IClasspathAttribute[] attributes = { JavaCore.newClasspathAttribute(IClasspathAttribute.MODULE, "true") };
+			addClasspathEntry(javaProject, JavaCore.newProjectEntry(auto.getPath(), null, false, attributes, false));
+			javaProject.setOption(JavaCore.COMPILER_PB_UNSTABLE_AUTO_MODULE_NAME, JavaCore.ERROR);
+
+			String srcMod =
+				"module mod.one { \n" +
+				"	requires auto;\n" +
+				"}";
+			createFile("/mod.one/src/module-info.java", 
+				srcMod);
+			auto.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null);
+
+			this.problemRequestor.initialize(srcMod.toCharArray());
+			getWorkingCopy("/mod.one/module-info.java", srcMod, true);
+			assertProblems("module-info should have only one error",
+					"----------\n" + 
+					"1. ERROR in /mod.one/module-info.java (at line 2)\n" + 
+					"	requires auto;\n" + 
+					"	         ^^^^\n" + 
+					"Name of automatic module \'auto\' is unstable, it is derived from the module\'s file name.\n" + 
+					"----------\n",
+					this.problemRequestor);
+		} finally {
+			if (javaProject != null)
+				deleteProject(javaProject);
+			if (auto != null)
+				deleteProject(auto);
+		}
+	}
 
 	// patch can see unexported type from host (and package accessible method), but not vice versa
 	public void testPatch1() throws CoreException, IOException {
@@ -6138,8 +6189,12 @@
 			
 			this.problemRequestor.initialize(srcMod.toCharArray());
 			getWorkingCopy("/mod1/src/module-info.java", srcMod, true);
-			assertProblems("module-info should have no problems",
+			assertProblems("module-info should have exactly one warning",
 					"----------\n" + 
+					"1. WARNING in /mod1/src/module-info.java (at line 3)\n" + 
+					"	requires automod;\n" + 
+					"	         ^^^^^^^\n" + 
+					"Name of automatic module \'automod\' is unstable, it is derived from the module\'s file name.\n" + 
 					"----------\n",
 					this.problemRequestor);
 
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
index d5ead5d..8c385c1 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java
@@ -4146,6 +4146,9 @@
 				} else {
 					throw new IllegalArgumentException(this.bind("configure.missingJavadocCommentsVisibility", token)); //$NON-NLS-1$
 				}
+			} else if (token.equals("module")) { //$NON-NLS-1$
+				setSeverity(CompilerOptions.OPTION_ReportUnstableAutoModuleName, severity, isEnabling);
+				return;
 			}
 			break;
 		case 'n' :
diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
index 1ee5d42..7923d00 100644
--- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
+++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/messages.properties
@@ -410,6 +410,7 @@
 \							methods\n\
 \      missingJavadocCommentsVisibility(<visibility>)  specify visibility\n\
 \							modifier for missing javadoc comments warnings\n\
+\      module             + module related problems.\n\
 \      nls                  string literal lacking non-nls tag //$NON-NLS-<n>$\n\
 \      noEffectAssign     + assignment without effect\n\
 \      null                 potential missing or redundant null check\n\
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
index 541060e..b99d196 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/core/compiler/IProblem.java
@@ -1994,6 +1994,8 @@
 	int MissingRequiresTransitiveForTypeInAPI = ModuleRelated + 1459;
 	/** @since  3.14 */
 	int UnnamedPackageInNamedModule = ModuleRelated + 1460;
+	/** @since  3.14 */
+	int UnstableAutoModuleName = ModuleRelated + 1461;
 
 	/** @since 3.13 */
 	int RedundantNullDefaultAnnotationLocal = Internal + 1062;
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
index 4891ada..80957aa 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/Annotation.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2017 IBM Corporation and others.
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -921,6 +921,10 @@
 					case Binding.MODULE :
 						SourceModuleBinding module = (SourceModuleBinding) this.recipient;
 						module.tagBits |= tagBits;
+						if ((tagBits & TagBits.AnnotationSuppressWarnings) != 0) {
+							ModuleDeclaration moduleDeclaration =  module.scope.referenceContext.moduleDeclaration;
+							recordSuppressWarnings(scope, 0, moduleDeclaration.declarationSourceEnd, compilerOptions.suppressWarnings);
+						}
 						module.defaultNullness |= defaultNullness;
 						break;
 					case Binding.PACKAGE :
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/RequiresStatement.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/RequiresStatement.java
index 918ff43..dbb4187 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/RequiresStatement.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/ast/RequiresStatement.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2016 IBM Corporation and others.
+ * Copyright (c) 2016, 2018 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -46,9 +46,12 @@
 		if (this.resolvedBinding != null)
 			return this.resolvedBinding;
 		this.resolvedBinding = this.module.resolve(scope);
-		if (this.resolvedBinding == null) {
-			if (scope != null)
+		if (scope != null) {
+			if (this.resolvedBinding == null) {
 				scope.problemReporter().invalidModule(this.module);
+			} else if (this.resolvedBinding.hasUnstableAutoName()) {
+				scope.problemReporter().autoModuleWithUnstableName(this.module);
+			}
 		}
 		return this.resolvedBinding;
 	}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/AutomaticModuleNaming.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/AutomaticModuleNaming.java
index bb1c9d1..4c659a3 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/AutomaticModuleNaming.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/AutomaticModuleNaming.java
@@ -62,6 +62,22 @@
 	}
 
 	/**
+	 * Determine the automatic module name of a given jar or project as defined by an Automatic-Module-Name
+	 * header in its manifest.
+	 * @param manifest representation of the META-INF/MANIFEST.MF entry within the given source (jar or project), or <code>null</code>
+	 * @return the derived module name or <code>null</code>
+	 */
+	public static char[] determineAutomaticModuleNameFromManifest(Manifest manifest) {
+		if (manifest != null) {
+			String automaticModuleName = manifest.getMainAttributes().getValue(AUTOMATIC_MODULE_NAME);
+			if (automaticModuleName != null) {
+				return automaticModuleName.toCharArray();
+			}
+		}
+		return null;
+	}
+
+	/**
 	 * Determine the automatic module name if no "Automatic-Module-Name" was found in the Manifest, as specified in
 	 * {@link <a href=
 	 * "http://download.java.net/java/jdk9/docs/api/java/lang/module/ModuleFinder.html#of-java.nio.file.Path...-">ModuleFinder.of</a>}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/IModule.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/IModule.java
index b8915a2..4ea429e 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/IModule.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/IModule.java
@@ -79,14 +79,19 @@
 	public default boolean isAutomatic() {
 		return false;
 	}
+	public default boolean isAutoNameFromManifest() {
+		return false;
+	}
 	public abstract boolean isOpen();
 
 
-	public static IModule createAutomatic(char[] moduleName) {
+	public static IModule createAutomatic(char[] moduleName, boolean fromManifest) {
 		final class AutoModule implements IModule {
 			char[] name;
-			public AutoModule(char[] name) {
+			boolean nameFromManifest;
+			public AutoModule(char[] name, boolean nameFromManifest) {
 				this.name = name;
+				this.nameFromManifest = nameFromManifest;
 			}
 			@Override
 			public char[] name() {
@@ -123,14 +128,24 @@
 				return true;
 			}
 			@Override
+			public boolean isAutoNameFromManifest() {
+				return this.nameFromManifest;
+			}
+			@Override
 			public boolean isOpen() {
 				return false;
 			}
 		}
-		return new AutoModule(moduleName);
+		return new AutoModule(moduleName, fromManifest);
 	}
 
 	public static IModule createAutomatic(String fileName, boolean isFile, Manifest manifest) {
-		return createAutomatic(AutomaticModuleNaming.determineAutomaticModuleName(fileName, isFile, manifest));
+		boolean fromManifest = true;
+		char[] inferredName = AutomaticModuleNaming.determineAutomaticModuleNameFromManifest(manifest);
+		if (inferredName == null) {
+			fromManifest = false;
+			inferredName = AutomaticModuleNaming.determineAutomaticModuleNameFromFileName(fileName, true, isFile);
+		}
+		return createAutomatic(inferredName, fromManifest);
 	}
 }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
index 453337a..5c129f2 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/CompilerOptions.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2017 IBM Corporation and others.
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -196,6 +196,7 @@
 	public static final String OPTION_ReportUnlikelyEqualsArgumentType = "org.eclipse.jdt.core.compiler.problem.unlikelyEqualsArgumentType"; //$NON-NLS-1$
 
 	public static final String OPTION_ReportAPILeak = "org.eclipse.jdt.core.compiler.problem.APILeak"; //$NON-NLS-1$
+	public static final String OPTION_ReportUnstableAutoModuleName = "org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName";   //$NON-NLS-1$
 
 	/**
 	 * Possible values for configurable options
@@ -323,6 +324,7 @@
 	public static final int UnlikelyEqualsArgumentType = IrritantSet.GROUP2 | ASTNode.Bit23;
 	public static final int UsingTerminallyDeprecatedAPI = IrritantSet.GROUP2 | ASTNode.Bit24;
 	public static final int APILeak = IrritantSet.GROUP2 | ASTNode.Bit25;
+	public static final int UnstableAutoModuleName = IrritantSet.GROUP2 | ASTNode.Bit26;
 
 
 	// Severity level for handlers
@@ -521,6 +523,7 @@
 		"hiding", //$NON-NLS-1$
 		"incomplete-switch", //$NON-NLS-1$
 		"javadoc", //$NON-NLS-1$
+		"module", //$NON-NLS-1$
 		"nls", //$NON-NLS-1$
 		"null", //$NON-NLS-1$
 		"rawtypes", //$NON-NLS-1$
@@ -739,6 +742,8 @@
 				return OPTION_ReportUnlikelyEqualsArgumentType;
 			case APILeak:
 				return OPTION_ReportAPILeak;
+			case UnstableAutoModuleName:
+				return OPTION_ReportUnstableAutoModuleName;
 		}
 		return null;
 	}
@@ -1056,6 +1061,8 @@
 				return "unlikely-arg-type"; //$NON-NLS-1$
 			case APILeak:
 				return "exports"; //$NON-NLS-1$
+			case UnstableAutoModuleName:
+				return "module"; //$NON-NLS-1$
 		}
 		return null;
 	}
@@ -1104,6 +1111,10 @@
 				if ("javadoc".equals(warningToken)) //$NON-NLS-1$
 					return IrritantSet.JAVADOC;
 				break;
+			case 'm' :
+				if ("module".equals(warningToken)) //$NON-NLS-1$
+					return IrritantSet.MODULE;
+				break;
 			case 'n' :
 				if ("nls".equals(warningToken)) //$NON-NLS-1$
 					return IrritantSet.NLS;
@@ -1294,6 +1305,7 @@
 		optionsMap.put(OPTION_ReportUnlikelyCollectionMethodArgumentTypeStrict, this.reportUnlikelyCollectionMethodArgumentTypeStrict ? ENABLED : DISABLED);
 		optionsMap.put(OPTION_ReportUnlikelyEqualsArgumentType, getSeverityString(UnlikelyEqualsArgumentType));
 		optionsMap.put(OPTION_ReportAPILeak, getSeverityString(APILeak));
+		optionsMap.put(OPTION_ReportUnstableAutoModuleName, getSeverityString(UnstableAutoModuleName));
 		return optionsMap;
 	}
 
@@ -1812,6 +1824,7 @@
 			this.analyseResourceLeaks = true;
 		}
 		if ((optionValue = optionsMap.get(OPTION_ReportAPILeak)) != null) updateSeverity(APILeak, optionValue);
+		if ((optionValue = optionsMap.get(OPTION_ReportUnstableAutoModuleName)) != null) updateSeverity(UnstableAutoModuleName, optionValue);
 		if ((optionValue = optionsMap.get(OPTION_AnnotationBasedNullAnalysis)) != null) {
 			this.isAnnotationBasedNullAnalysisEnabled = ENABLED.equals(optionValue);
 		}
@@ -2138,6 +2151,7 @@
 		buf.append("\n\t- unlikely argument type for collection methods, strict check against expected type: ").append(this.reportUnlikelyCollectionMethodArgumentTypeStrict ? ENABLED : DISABLED); //$NON-NLS-1$
 		buf.append("\n\t- unlikely argument types for equals(): ").append(getSeverityString(UnlikelyEqualsArgumentType)); //$NON-NLS-1$
 		buf.append("\n\t- API leak: ").append(getSeverityString(APILeak)); //$NON-NLS-1$
+		buf.append("\n\t- unstable auto module name: ").append(getSeverityString(UnstableAutoModuleName)); //$NON-NLS-1$
 		return buf.toString();
 	}
 	
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java
index aa7d95a..e77b9be 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/impl/IrritantSet.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2017 IBM Corporation and others.
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -71,6 +71,7 @@
 	public static final IrritantSet RESOURCE = new IrritantSet(CompilerOptions.UnclosedCloseable);
 	public static final IrritantSet UNLIKELY_ARGUMENT_TYPE = new IrritantSet(CompilerOptions.UnlikelyCollectionMethodArgumentType);
 	public static final IrritantSet API_LEAK = new IrritantSet(CompilerOptions.APILeak);
+	public static final IrritantSet MODULE = new IrritantSet(CompilerOptions.UnstableAutoModuleName);
 
 	public static final IrritantSet JAVADOC = new IrritantSet(CompilerOptions.InvalidJavadoc);
 	public static final IrritantSet COMPILER_DEFAULT_ERRORS = new IrritantSet(0); // no optional error by default	
@@ -129,7 +130,8 @@
 				|CompilerOptions.NonNullTypeVariableFromLegacyInvocation
 				|CompilerOptions.UnlikelyCollectionMethodArgumentType
 				|CompilerOptions.UsingTerminallyDeprecatedAPI
-				|CompilerOptions.APILeak);
+				|CompilerOptions.APILeak
+				|CompilerOptions.UnstableAutoModuleName);
 		// default errors IF AnnotationBasedNullAnalysis is enabled:
 		COMPILER_DEFAULT_ERRORS.set(
 				CompilerOptions.NullSpecViolation
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryModuleBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryModuleBinding.java
index 7c259d8..88872c5 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryModuleBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/BinaryModuleBinding.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2017 GK Software AG, and others.
+ * Copyright (c) 2017, 2018 GK Software SE, and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -28,15 +28,22 @@
 	
 	private static class AutomaticModuleBinding extends ModuleBinding {
 
+		boolean autoNameFromManifest;
+
 		public AutomaticModuleBinding(IModule module, LookupEnvironment existingEnvironment) {
 			super(module.name(), existingEnvironment);
 			existingEnvironment.root.knownModules.put(this.moduleName, this);
 			this.isAuto = true;
+			this.autoNameFromManifest = module.isAutoNameFromManifest();
 			this.requires = Binding.NO_MODULES;
 			this.requiresTransitive = Binding.NO_MODULES;
 			this.exportedPackages = Binding.NO_PACKAGES;
 		}
 		@Override
+		public boolean hasUnstableAutoName() {
+			return !this.autoNameFromManifest;
+		}
+		@Override
 		public ModuleBinding[] getRequiresTransitive() {
 			if (this.requiresTransitive == NO_MODULES) {
 				char[][] autoModules = ((IModuleAwareNameEnvironment)this.environment.nameEnvironment).getAllAutomaticModules();
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ModuleBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ModuleBinding.java
index 0250ff5..091ad47 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ModuleBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ModuleBinding.java
@@ -829,6 +829,9 @@
 		// TODO(SHMOD) implement deprecation for modules
 		return false;
 	}
+	public boolean hasUnstableAutoName() {
+		return false;
+	}
 	public boolean isTransitivelyRequired(ModuleBinding otherModule) {
 		if (this.transitiveRequires == null) {
 			Set<ModuleBinding> transitiveDeps = new HashSet<>();
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
index e697035..59e7d56 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java
@@ -652,6 +652,8 @@
 		case IProblem.NotExportedTypeInAPI:
 		case IProblem.MissingRequiresTransitiveForTypeInAPI:
 			return CompilerOptions.APILeak;
+		case IProblem.UnstableAutoModuleName:
+			return CompilerOptions.UnstableAutoModuleName;
 }
 	return 0;
 }
@@ -714,6 +716,7 @@
 			case CompilerOptions.UnlikelyCollectionMethodArgumentType :
 			case CompilerOptions.UnlikelyEqualsArgumentType:
 			case CompilerOptions.APILeak:
+			case CompilerOptions.UnstableAutoModuleName:
 				return CategorizedProblem.CAT_POTENTIAL_PROGRAMMING_PROBLEM;
 			
 			case CompilerOptions.OverriddenPackageDefaultMethod :
@@ -10830,4 +10833,13 @@
 			0,
 			0);
 }
+
+public void autoModuleWithUnstableName(ModuleReference moduleReference) {
+	String[] args = { new String(moduleReference.moduleName) };
+	handle(IProblem.UnstableAutoModuleName,
+			args,
+			args,
+			moduleReference.sourceStart,
+			moduleReference.sourceEnd);
+}
 }
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
index b6f56e6..db8b7c1 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/problem/messages.properties
@@ -935,6 +935,7 @@
 1458 = The type {0} is not exported from this module
 1459 = The type {0} from module {1} may not be accessible to clients due to missing ''requires transitive''
 1460 = Must declare a named package because this compilation unit is associated to the named module ''{0}''
+1461 = Name of automatic module ''{0}'' is unstable, it is derived from the module's file name.
 
 ### ELABORATIONS
 ## Access restrictions
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java
index 6c8bf76..25819ee 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/JavaCore.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2017 IBM Corporation and others.
+ * Copyright (c) 2000, 2018 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
@@ -1618,6 +1618,27 @@
 	public static final String COMPILER_PB_API_LEAKS = PLUGIN_ID + ".compiler.problem.APILeak"; //$NON-NLS-1$
 	
 	/**
+	 * Compiler option ID: Reporting when a module requires an auto module with an unstable name.
+	 * <p>
+	 * The name of an auto module name is considered unstable when it is derived from a file name rather than
+	 * being declared in the module's MANIFEST.MF.
+	 * <p>
+	 * When enabled, the compiler will issue an error or warning when a module references an auto module
+	 * with an unstable name in its 'requires' clause.
+	 * <dl>
+	 * <dt>Option id:</dt><dd><code>"org.eclipse.jdt.core.compiler.problem.unstableAutoModuleName"</code></dd>
+	 * <dt>Possible values:</dt>
+	 * <dd><code>{ "error", "warning", "info", "ignore" }</code></dd>
+	 * <dt>Default:</dt><dd><code>"warning"</code></dd>
+	 * </dl>
+	 * 
+	 * @since 3.14
+	 * @category CompilerOptionID
+	 */
+	public static final String COMPILER_PB_UNSTABLE_AUTO_MODULE_NAME = PLUGIN_ID + ".compiler.problem.unstableAutoModuleName"; //$NON-NLS-1$
+
+	
+	/**
 	 * Compiler option ID: Annotation-based Null Analysis.
 	 * <p>This option controls whether the compiler will use null annotations for
 	 *    improved analysis of (potential) null references.</p>
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/AbstractModule.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/AbstractModule.java
index 3ff609b..d3a381a 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/AbstractModule.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/AbstractModule.java
@@ -28,8 +28,11 @@
 	 */
 	static class AutoModule extends NamedMember implements AbstractModule {
 	
-		public AutoModule(JavaElement parent, String name) {
+		private boolean nameFromManifest;
+
+		public AutoModule(JavaElement parent, String name, boolean nameFromManifest) {
 			super(parent, name);
+			this.nameFromManifest = nameFromManifest;
 		}
 		@Override
 		public IJavaElement[] getChildren() throws JavaModelException {
@@ -39,6 +42,9 @@
 		public int getFlags() throws JavaModelException {
 			return 0;
 		}
+		public boolean isAutoNameFromManifest() {
+			return this.nameFromManifest;
+		}
 		@Override
 		public char getHandleMementoDelimiter() {
 			return JavaElement.JEM_MODULE;
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
index ab9df20..c947bdb 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/JavaProject.java
@@ -3699,8 +3699,13 @@
 	}
 
 	public IModuleDescription getAutomaticModuleDescription() throws JavaModelException {
-		char[] moduleName = AutomaticModuleNaming.determineAutomaticModuleName(getElementName(), false, getManifest());
-		return new AbstractModule.AutoModule(this, String.valueOf(moduleName));
+		boolean nameFromManifest = true;
+		char[] moduleName = AutomaticModuleNaming.determineAutomaticModuleNameFromManifest(getManifest());
+		if (moduleName == null) {
+			nameFromManifest = false;
+			moduleName = AutomaticModuleNaming.determineAutomaticModuleNameFromFileName(getElementName(), true, false);
+		}
+		return new AbstractModule.AutoModule(this, String.valueOf(moduleName), nameFromManifest);
 	}
 
 	public void setModuleDescription(IModuleDescription module) throws JavaModelException {
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NameLookup.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NameLookup.java
index f544891..869c672 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NameLookup.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NameLookup.java
@@ -43,6 +43,7 @@
 import org.eclipse.jdt.internal.compiler.parser.ScannerHelper;
 import org.eclipse.jdt.internal.compiler.util.HashtableOfObjectToInt;
 import org.eclipse.jdt.internal.compiler.util.SuffixConstants;
+import org.eclipse.jdt.internal.core.AbstractModule.AutoModule;
 import org.eclipse.jdt.internal.core.util.HashtableOfArrayToObject;
 import org.eclipse.jdt.internal.core.util.Messages;
 import org.eclipse.jdt.internal.core.util.Util;
@@ -859,8 +860,9 @@
 						return ((ModularClassFile) parent).getBinaryModuleInfo();
 				} else if (moduleDesc instanceof SourceModule) {
 					return (IModule)((SourceModule) moduleDesc).getElementInfo();
-				} else {
-					return IModule.createAutomatic(moduleDesc.getElementName().toCharArray());
+				} else if (moduleDesc instanceof AutoModule) {
+					boolean nameFromManifest = ((AutoModule) moduleDesc).isAutoNameFromManifest();
+					return IModule.createAutomatic(moduleDesc.getElementName().toCharArray(), nameFromManifest);
 				}
 			} catch (JavaModelException e) {
 				if (!e.isDoesNotExist())
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/PackageFragmentRoot.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/PackageFragmentRoot.java
index 93b4ca0..df40d5f 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/PackageFragmentRoot.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/PackageFragmentRoot.java
@@ -928,8 +928,13 @@
 			elementName = javaProject.getElementName();
 			break;
 	}
-	char[] moduleName = AutomaticModuleNaming.determineAutomaticModuleName(elementName, isArchive(), manifest);
-	return new AbstractModule.AutoModule(this, String.valueOf(moduleName));
+	boolean nameFromManifest = true;
+	char[] moduleName = AutomaticModuleNaming.determineAutomaticModuleNameFromManifest(manifest);
+	if (moduleName == null) {
+		nameFromManifest = false;
+		moduleName = AutomaticModuleNaming.determineAutomaticModuleNameFromFileName(elementName, true, isArchive());
+	}
+	return new AbstractModule.AutoModule(this, String.valueOf(moduleName), nameFromManifest);
 }
 
 /** @see org.eclipse.jdt.internal.compiler.env.IModulePathEntry#hasCompilationUnit(String, String) */