Bug 534979 - [11] Add warning in annotation processing on redefining
symbols

Change-Id: I3054799f2c9f185a5e84b6ae53b7197568a1cf27
Signed-off-by: jay <jarthana@in.ibm.com>
diff --git a/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/filer/IdeFilerImpl.java b/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/filer/IdeFilerImpl.java
index 309fd86..e45abeb 100644
--- a/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/filer/IdeFilerImpl.java
+++ b/org.eclipse.jdt.apt.pluggable.core/src/org/eclipse/jdt/internal/apt/pluggable/core/filer/IdeFilerImpl.java
@@ -22,9 +22,9 @@
 import javax.annotation.processing.FilerException;
 import javax.lang.model.element.Element;
 import javax.tools.FileObject;
+import javax.tools.JavaFileManager.Location;
 import javax.tools.JavaFileObject;
 import javax.tools.StandardLocation;
-import javax.tools.JavaFileManager.Location;
 
 import org.eclipse.core.resources.IFile;
 import org.eclipse.core.resources.IResource;
@@ -34,6 +34,8 @@
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.jdt.apt.core.internal.AptCompilationParticipant;
 import org.eclipse.jdt.apt.core.internal.generatedfile.GeneratedSourceFolderManager;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IType;
 import org.eclipse.jdt.core.JavaModelException;
 import org.eclipse.jdt.internal.apt.pluggable.core.Apt6Plugin;
 import org.eclipse.jdt.internal.apt.pluggable.core.dispatch.IdeAnnotationProcessorManager;
@@ -128,7 +130,16 @@
 		if (AptCompilationParticipant.getInstance().getJava6GeneratedFiles().contains(file)) {
 			throw new FilerException("Source file already created: " + file.getFullPath()); //$NON-NLS-1$
 		}
-		
+		IJavaProject javaProject = _env.getJavaProject();
+		IType type = null;
+		try {
+			name = name.toString().replace('/', '.');
+			type = javaProject.findType(name.toString());
+		} catch (JavaModelException e) {
+		}
+		if (type != null) {
+			throw new FilerException("Source file already exists : " + name); //$NON-NLS-1$
+		}
 		Set<IFile> parentFiles = Collections.emptySet();
 		if (originatingElements != null && originatingElements.length > 0) {
 			parentFiles = new HashSet<IFile>(originatingElements.length);
diff --git a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/FilerTests.java b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/FilerTests.java
index 3834cef..b354f74 100644
--- a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/FilerTests.java
+++ b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/FilerTests.java
@@ -355,5 +355,23 @@
 		assertTrue("Processor did not run", ProcessorTestStatus.processorRan());
 		assertEquals("Processor reported errors", ProcessorTestStatus.NO_ERRORS, ProcessorTestStatus.getErrors());
 	}
+	public void testBug534979() throws Throwable {
+		ProcessorTestStatus.reset();
+		IJavaProject jproj = createJavaProject(_projectName);
+		disableJava5Factories(jproj);
+		IProject proj = jproj.getProject();
+		IPath projPath = proj.getFullPath();
 
+		env.addClass(projPath.append("src"), "p", "Trigger",
+				"package p;\n" +
+				"import org.eclipse.jdt.apt.pluggable.tests.annotations.FilerTestTrigger;\n" +
+				"@FilerTestTrigger(test = \"testBug534979\", arg0 = \"t\", arg1 = \"Test\")" +
+				"public class Trigger {\n" +
+				"}"
+		); 
+
+		AptConfig.setEnabled(jproj, true);
+		fullBuild();
+		assertEquals("Processor reported errors", "FilerException invoking test method testBug534979 - see console for details", ProcessorTestStatus.getErrors());
+	}
 }
diff --git a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/processors/filertester/FilerTesterProc.java b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/processors/filertester/FilerTesterProc.java
index 9736c94..d0164ad 100644
--- a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/processors/filertester/FilerTesterProc.java
+++ b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/processors/filertester/FilerTesterProc.java
@@ -230,7 +230,20 @@
 		FileObject foGenSrc = _filer.createSourceFile("g.G", e);
 		checkGenUri(foGenSrc, "G", javaStr, "generated source file");
 	}
-	
+
+	public void testBug534979(Element e, String pkg, String relName) throws Exception {
+		JavaFileObject jfo = _filer.createSourceFile(e.getEnclosingElement().getSimpleName() + "/" + e.getSimpleName());
+		PrintWriter pw = null;
+		try {
+			pw = new PrintWriter(jfo.openWriter());
+			pw.println("package " + pkg + ";\npublic class " + e.getSimpleName() + "{ }");
+		}
+		finally {
+			if (pw != null)
+				pw.close();
+		}
+	}
+
 	private void checkGenUri(FileObject fo, String name, String content, String category) throws Exception {
 		PrintWriter pw = null;
 		try {
diff --git a/org.eclipse.jdt.compiler.apt.tests/lib/apttestprocessors8.jar b/org.eclipse.jdt.compiler.apt.tests/lib/apttestprocessors8.jar
index bf811a9..6a62c6f 100644
--- a/org.eclipse.jdt.compiler.apt.tests/lib/apttestprocessors8.jar
+++ b/org.eclipse.jdt.compiler.apt.tests/lib/apttestprocessors8.jar
Binary files differ
diff --git a/org.eclipse.jdt.compiler.apt.tests/processors8/META-INF/services/javax.annotation.processing.Processor b/org.eclipse.jdt.compiler.apt.tests/processors8/META-INF/services/javax.annotation.processing.Processor
index 839d9ea..8b8bccf 100644
--- a/org.eclipse.jdt.compiler.apt.tests/processors8/META-INF/services/javax.annotation.processing.Processor
+++ b/org.eclipse.jdt.compiler.apt.tests/processors8/META-INF/services/javax.annotation.processing.Processor
@@ -1,2 +1,3 @@
 org.eclipse.jdt.compiler.apt.tests.processors.elements.Java8ElementProcessor

-org.eclipse.jdt.compiler.apt.tests.processors.elements.Java9ElementProcessor
\ No newline at end of file
+org.eclipse.jdt.compiler.apt.tests.processors.elements.Java9ElementProcessor

+org.eclipse.jdt.compiler.apt.tests.processors.elements.Java11ElementProcessor
\ No newline at end of file
diff --git a/org.eclipse.jdt.compiler.apt.tests/processors8/org/eclipse/jdt/compiler/apt/tests/processors/elements/Java11ElementProcessor.java b/org.eclipse.jdt.compiler.apt.tests/processors8/org/eclipse/jdt/compiler/apt/tests/processors/elements/Java11ElementProcessor.java
new file mode 100644
index 0000000..706027e
--- /dev/null
+++ b/org.eclipse.jdt.compiler.apt.tests/processors8/org/eclipse/jdt/compiler/apt/tests/processors/elements/Java11ElementProcessor.java
@@ -0,0 +1,242 @@
+/*******************************************************************************
+ * Copyright (c) 2019 IBM Corporation.
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jdt.compiler.apt.tests.processors.elements;
+
+import java.io.IOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import javax.annotation.processing.Filer;
+import javax.annotation.processing.Messager;
+import javax.annotation.processing.ProcessingEnvironment;
+import javax.annotation.processing.RoundEnvironment;
+import javax.annotation.processing.SupportedAnnotationTypes;
+import javax.lang.model.element.Modifier;
+import javax.lang.model.element.TypeElement;
+import javax.tools.JavaFileObject;
+
+import org.eclipse.jdt.compiler.apt.tests.processors.base.BaseProcessor;
+
+/**
+ * A processor that explores the java 9 specific elements and validates the lambda and 
+ * type annotated elements. To enable this processor, add 
+ * -Aorg.eclipse.jdt.compiler.apt.tests.processors.elements.Java11ElementProcessor to the command line.
+ * @since 3.14
+ */
+@SupportedAnnotationTypes("*")
+public class Java11ElementProcessor extends BaseProcessor {
+	boolean reportSuccessAlready = true;
+	RoundEnvironment roundEnv = null;
+	Messager _messager = null;
+	Filer _filer = null;
+	@Override
+	public synchronized void init(ProcessingEnvironment processingEnv) {
+		super.init(processingEnv);
+		_elementUtils = processingEnv.getElementUtils();
+		_messager = processingEnv.getMessager();
+		_filer = processingEnv.getFiler();
+	}
+	// Always return false from this processor, because it supports "*".
+	// The return value does not signify success or failure!
+	@Override
+	public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
+		if (roundEnv.processingOver()) {
+			return false;
+		}
+		
+		this.roundEnv = roundEnv;
+		Map<String, String> options = processingEnv.getOptions();
+		if (!options.containsKey(this.getClass().getName())) {
+			// Disable this processor unless we are intentionally performing the test.
+			return false;
+		} else {
+			try {
+				if (!invokeTestMethods(options)) {
+					testAll();
+				}
+				if (this.reportSuccessAlready) {
+					super.reportSuccess();
+				}
+			} catch (AssertionFailedError e) {
+				super.reportError(getExceptionStackTrace(e));
+			} catch (Throwable e) {
+				e.printStackTrace();
+			}
+		}
+		return false;
+	}
+
+	private boolean invokeTestMethods(Map<String, String> options) throws Throwable {
+		Method testMethod = null;
+		Set<String> keys = options.keySet();
+		boolean testsFound = false;
+		for (String option : keys) {
+			if (option.startsWith("test")) {
+				try {
+					testMethod = this.getClass().getDeclaredMethod(option, new Class[0]);
+					if (testMethod != null) {
+						testsFound = true;
+						testMethod.invoke(this,  new Object[0]);
+					}
+				} catch (InvocationTargetException e) {
+					throw e.getCause();
+				} catch (Exception e) {
+					super.reportError(getExceptionStackTrace(e));
+				}
+			}
+		}
+		return testsFound;
+	}
+
+	public void testAll() throws AssertionFailedError {
+	}
+
+	public void testFiler1() throws IOException {
+		String typeName = "abc.internal.TypeInAModule";
+		TypeElement typeElement = _elementUtils.getTypeElement(typeName);
+		assertNotNull("type element should not be null", typeElement);
+		Object obj = null;
+		try {
+			obj = _filer.createSourceFile("mod.a/" + typeName);
+			obj = typeName;
+		} catch (IOException e) {
+		}
+		assertNull("Source should not be created", obj);
+	}
+	public void testFiler2() throws IOException {
+		String typeName = "abc.internal.TypeInAModule";
+		TypeElement typeElement = _elementUtils.getTypeElement(typeName);
+		assertNotNull("type element should not be null", typeElement);
+		Object obj = null;
+		try {
+			obj = _filer.createSourceFile(typeName);
+			obj = typeName;
+		} catch (IOException e) {
+		}
+		assertNull("Source should not be created", obj);
+	}
+	public void testFiler3() throws IOException {
+		String typeName = "mod.a/abc.internal.AnotherTypeInAModule";
+		JavaFileObject obj = null;
+		try {
+			obj = _filer.createSourceFile(typeName);
+		} catch (IOException e) {
+		}
+		assertNotNull("Source should have been created", obj);
+		String name = obj.getName();
+		if (!name.contains("mod.a")) {
+			reportError("source should be created inside the module");
+		}
+		
+	}
+	@Override
+	public void reportError(String msg) {
+		throw new AssertionFailedError(msg);
+	}
+	private String getExceptionStackTrace(Throwable t) {
+		StringBuffer buf = new StringBuffer(t.getMessage());
+		StackTraceElement[] traces = t.getStackTrace();
+		for (int i = 0; i < traces.length; i++) {
+			StackTraceElement trace = traces[i];
+			buf.append("\n\tat " + trace);
+			if (i == 12)
+				break; // Don't dump all stacks
+		}
+		return buf.toString();
+	}
+	public void assertModifiers(Set<Modifier> modifiers, String[] expected) {
+		assertEquals("Incorrect no of modifiers", modifiers.size(), expected.length);
+		Set<String> actual = new HashSet<String>(expected.length);
+		for (Modifier modifier : modifiers) {
+			actual.add(modifier.toString());
+		}
+		for(int i = 0, length = expected.length; i < length; i++) {
+			boolean result = actual.remove(expected[i]);
+			if (!result) reportError("Modifier not present :" + expected[i]);
+		}
+		if (!actual.isEmpty()) {
+			reportError("Unexpected modifiers present:" + actual.toString());
+		}
+	}
+	public void assertTrue(String msg, boolean value) {
+		if (!value) reportError(msg);
+	}
+	public void assertFalse(String msg, boolean value) {
+		if (value) reportError(msg);
+	}
+	public void assertSame(String msg, Object obj1, Object obj2) {
+		if (obj1 != obj2) {
+			reportError(msg + ", should be " + obj1.toString() + " but " + obj2.toString());
+		}
+	}
+	public void assertNotSame(String msg, Object obj1, Object obj2) {
+		if (obj1 == obj2) {
+			reportError(msg + ", " + obj1.toString() + " should not be same as " + obj2.toString());
+		}
+	}
+	public void assertNotNull(String msg, Object obj) {
+		if (obj == null) {
+			reportError(msg);
+		}
+	}
+	public void assertNull(String msg, Object obj) {
+		if (obj != null) {
+			reportError(msg);
+		}
+	}
+    public void assertEquals(String message, Object expected, Object actual) {
+        if (equalsRegardingNull(expected, actual)) {
+            return;
+        } else {
+        	reportError(message + ", expected " + expected.toString() + " but was " + actual.toString());
+        }
+    }
+
+    public void assertEquals(String message, Object expected, Object alternateExpected, Object actual) {
+        if (equalsRegardingNull(expected, actual) || equalsRegardingNull(alternateExpected, actual)) {
+            return;
+        } else {
+        	reportError(message + ", expected " + expected.toString() + " but was " + actual.toString());
+        }
+    }
+    
+    static boolean equalsRegardingNull(Object expected, Object actual) {
+        if (expected == null) {
+            return actual == null;
+        }
+        return expected.equals(actual);
+    }
+    
+	public void assertEquals(String msg, int expected, int actual) {
+		if (expected != actual) {
+			StringBuffer buf = new StringBuffer();
+			buf.append(msg);
+			buf.append(", expected " + expected + " but was " + actual);
+			reportError(buf.toString());
+		}
+	}
+	public void assertEquals(Object expected, Object actual) {
+		if (expected != actual) {
+			
+		}
+	}
+	private class AssertionFailedError extends Error {
+		private static final long serialVersionUID = 1L;
+
+		public AssertionFailedError(String msg) {
+			super(msg);
+		}
+	}
+}
diff --git a/org.eclipse.jdt.compiler.apt.tests/processors8/org/eclipse/jdt/compiler/apt/tests/processors/elements/Java9ElementProcessor.java b/org.eclipse.jdt.compiler.apt.tests/processors8/org/eclipse/jdt/compiler/apt/tests/processors/elements/Java9ElementProcessor.java
index 8310874..dbf6e82 100644
--- a/org.eclipse.jdt.compiler.apt.tests/processors8/org/eclipse/jdt/compiler/apt/tests/processors/elements/Java9ElementProcessor.java
+++ b/org.eclipse.jdt.compiler.apt.tests/processors8/org/eclipse/jdt/compiler/apt/tests/processors/elements/Java9ElementProcessor.java
@@ -25,6 +25,7 @@
 import javax.annotation.processing.RoundEnvironment;
 import javax.annotation.processing.SupportedAnnotationTypes;
 import javax.lang.model.AnnotatedConstruct;
+import javax.lang.model.SourceVersion;
 import javax.lang.model.element.AnnotationMirror;
 import javax.lang.model.element.AnnotationValue;
 import javax.lang.model.element.Element;
@@ -58,11 +59,26 @@
 	boolean reportSuccessAlready = true;
 	RoundEnvironment roundEnv = null;
 	Messager _messager = null;
+	boolean isJre11;
+	boolean isJre10;
 	@Override
 	public synchronized void init(ProcessingEnvironment processingEnv) {
 		super.init(processingEnv);
 		_typeUtils = processingEnv.getTypeUtils();
 		_messager = processingEnv.getMessager();
+		try {
+			SourceVersion.valueOf("RELEASE_11");
+			this.isJre10 = true;
+			this.isJre11 = true;
+		} catch(IllegalArgumentException iae) {
+		}
+		if (!this.isJre11) {
+			try {
+				SourceVersion.valueOf("RELEASE_10");
+				this.isJre10 = true;
+			} catch(IllegalArgumentException iae) {
+			}
+		}
 	}
 	// Always return false from this processor, because it supports "*".
 	// The return value does not signify success or failure!
@@ -391,7 +407,7 @@
 		assertNotNull("java.base module null", base);
 		List<? extends Directive> directives = base.getDirectives();
 		List<Directive> filterDirective = filterDirective(directives, DirectiveKind.EXPORTS);
-		assertEquals("incorrect no of exports", 108 , filterDirective.size());
+		assertEquals("incorrect no of exports", this.isJre11 ? 108 : (this.isJre10 ? 102 : 108) , filterDirective.size());
 		ExportsDirective pack = null;
 		for (Directive directive : filterDirective) {
 			ModuleElement.ExportsDirective exports = (ExportsDirective) directive;
@@ -506,7 +522,7 @@
 		assertNotNull("java.sql module null", base);
 		List<? extends Directive> directives = base.getDirectives();
 		List<Directive> filterDirective = filterDirective(directives, DirectiveKind.REQUIRES);
-		assertEquals("Incorrect no of requires", 3, filterDirective.size());
+		assertEquals("Incorrect no of requires", this.isJre11 ? 4 : 3, filterDirective.size());
 		RequiresDirective req = null;
 		for (Directive directive : filterDirective) {
 			if (((RequiresDirective) directive).getDependency().getQualifiedName().toString().equals("java.logging")) {
diff --git a/org.eclipse.jdt.compiler.apt.tests/src/org/eclipse/jdt/compiler/apt/tests/BatchTestUtils.java b/org.eclipse.jdt.compiler.apt.tests/src/org/eclipse/jdt/compiler/apt/tests/BatchTestUtils.java
index 7903d88..a59ae63 100644
--- a/org.eclipse.jdt.compiler.apt.tests/src/org/eclipse/jdt/compiler/apt/tests/BatchTestUtils.java
+++ b/org.eclipse.jdt.compiler.apt.tests/src/org/eclipse/jdt/compiler/apt/tests/BatchTestUtils.java
@@ -120,6 +120,10 @@
 
 	public static void compileInModuleMode(JavaCompiler compiler, List<String> options, String processor,
 			File targetFolder, DiagnosticListener<? super JavaFileObject> listener, boolean multiModule) {
+		compileInModuleMode(compiler, options, processor, targetFolder, listener, multiModule, true);
+	}
+	public static void compileInModuleMode(JavaCompiler compiler, List<String> options, String processor,
+			File targetFolder, DiagnosticListener<? super JavaFileObject> listener, boolean multiModule, boolean processBinariesAgain) {
 		StandardJavaFileManager manager = compiler.getStandardFileManager(null, Locale.getDefault(), Charset.defaultCharset());
 		Iterable<? extends File> location = manager.getLocation(StandardLocation.CLASS_PATH);
 		// create new list containing inputfile
@@ -148,6 +152,9 @@
 			System.err.println("Compilation failed: " + errorOutput);
 	 		junit.framework.TestCase.assertTrue("Compilation failed : " + errorOutput, false);
 		}
+		if (!processBinariesAgain) {
+			return;
+		}
 		List<String> classes = new ArrayList<>();
 		try {
 			manager = compiler.getStandardFileManager(null, Locale.getDefault(), Charset.defaultCharset());
diff --git a/org.eclipse.jdt.compiler.apt.tests/src/org/eclipse/jdt/compiler/apt/tests/Java11ElementsTests.java b/org.eclipse.jdt.compiler.apt.tests/src/org/eclipse/jdt/compiler/apt/tests/Java11ElementsTests.java
new file mode 100644
index 0000000..0565b82
--- /dev/null
+++ b/org.eclipse.jdt.compiler.apt.tests/src/org/eclipse/jdt/compiler/apt/tests/Java11ElementsTests.java
@@ -0,0 +1,132 @@
+/*******************************************************************************
+ * Copyright (c) 2018 IBM Corporation.
+ * 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
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *******************************************************************************/
+
+package org.eclipse.jdt.compiler.apt.tests;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.lang.model.SourceVersion;
+import javax.tools.JavaCompiler;
+import javax.tools.ToolProvider;
+
+import org.eclipse.jdt.internal.compiler.tool.EclipseCompiler;
+
+import junit.framework.TestCase;
+
+public class Java11ElementsTests extends TestCase {
+	private static final String MODULE_PROC = "org.eclipse.jdt.compiler.apt.tests.processors.elements.Java11ElementProcessor";
+
+	public void testFiler1() throws IOException {
+		JavaCompiler compiler = BatchTestUtils.getEclipseCompiler();
+		internalTest2(compiler, MODULE_PROC, "testFiler1", null);
+	}
+	public void testFiler1Javac() throws IOException {
+		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+		internalTest2(compiler, MODULE_PROC, "testFiler1", null);
+	}
+	public void testFiler2() throws IOException {
+		JavaCompiler compiler = BatchTestUtils.getEclipseCompiler();
+		internalTest2(compiler, MODULE_PROC, "testFiler2", null);
+	}
+	public void testFiler2Javac() throws IOException {
+		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+		internalTest2(compiler, MODULE_PROC, "testFiler2", null);
+	}
+	public void testFiler3() throws IOException {
+		JavaCompiler compiler = BatchTestUtils.getEclipseCompiler();
+		internalTest2(compiler, MODULE_PROC, "testFiler3", null);
+	}
+	public void testFiler3Javac() throws IOException {
+		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+		internalTest2(compiler, MODULE_PROC, "testFiler3", null);
+	}
+	protected void internalTestWithBinary(JavaCompiler compiler, String processor, String compliance, String testMethod, String testClass, String resourceArea) throws IOException {
+		if (!canRunJava11()) {
+			return;
+		}
+		System.clearProperty(processor);
+		File targetFolder = TestUtils.concatPath(BatchTestUtils.getSrcFolderName(), "targets", resourceArea);
+		if (testClass == null || testClass.equals("")) {
+			BatchTestUtils.copyResources("targets/" + resourceArea, targetFolder);
+		} else {
+			BatchTestUtils.copyResource("targets/" + resourceArea + "/" + testClass, targetFolder);
+		}
+		
+
+		List<String> options = new ArrayList<String>();
+		options.add("-A" + processor);
+		options.add("-A" + testMethod);
+		options.add("-processor");
+		options.add(processor);
+		// Javac 1.8 doesn't (yet?) support the -1.8 option
+		if (compiler instanceof EclipseCompiler) {
+			options.add("-" + compliance);
+		} else {
+			options.add("-source");
+			options.add(compliance);
+		}
+		BatchTestUtils.compileTreeAndProcessBinaries(compiler, options, processor, targetFolder, null);
+
+		// If it succeeded, the processor will have set this property to "succeeded";
+		// if not, it will set it to an error value.
+		assertEquals("succeeded", System.getProperty(processor));
+	}
+	/*
+	 * Tests are run in multi-module mode
+	 */
+	private void internalTest2(JavaCompiler compiler, String processor, String testMethod, String testClass) throws IOException {
+		if (!canRunJava11()) {
+			return;
+		}
+		System.clearProperty(MODULE_PROC);
+		File srcRoot = TestUtils.concatPath(BatchTestUtils.getSrcFolderName());
+		BatchTestUtils.copyResources("mod_locations/modules", srcRoot);
+
+		List<String> options = new ArrayList<String>();
+		options.add("-processor");
+		options.add(MODULE_PROC);
+		options.add("-A" + MODULE_PROC);
+		options.add("-A" + testMethod);
+		if (compiler instanceof EclipseCompiler) {
+			options.add("-9");
+		}
+		BatchTestUtils.compileInModuleMode(compiler, options, MODULE_PROC, srcRoot, null, true, false);
+		assertEquals("succeeded", System.getProperty(MODULE_PROC));
+	}
+	public boolean canRunJava11() {
+		try {
+			SourceVersion.valueOf("RELEASE_11");
+		} catch(IllegalArgumentException iae) {
+			return false;
+		}
+		return true;
+	}
+	/* (non-Javadoc)
+	 * @see junit.framework.TestCase#setUp()
+	 */
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+		BatchTestUtils.init();
+	}
+
+	/* (non-Javadoc)
+	 * @see junit.framework.TestCase#tearDown()
+	 */
+	@Override
+	protected void tearDown() throws Exception {
+		super.tearDown();
+	}
+
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.compiler.apt/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchFilerImpl.java b/org.eclipse.jdt.compiler.apt/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchFilerImpl.java
index 31a29ea..d5d522d 100644
--- a/org.eclipse.jdt.compiler.apt/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchFilerImpl.java
+++ b/org.eclipse.jdt.compiler.apt/src/org/eclipse/jdt/internal/compiler/apt/dispatch/BatchFilerImpl.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2006, 2015 BEA Systems, Inc. 
+ * Copyright (c) 2006, 2018 BEA Systems, Inc. 
  * 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
@@ -21,6 +21,7 @@
 import javax.annotation.processing.Filer;
 import javax.annotation.processing.FilerException;
 import javax.lang.model.element.Element;
+import javax.lang.model.element.TypeElement;
 import javax.tools.FileObject;
 import javax.tools.JavaFileManager;
 import javax.tools.JavaFileObject;
@@ -133,8 +134,19 @@
 	@Override
 	public JavaFileObject createSourceFile(CharSequence name,
 			Element... originatingElements) throws IOException {
-		JavaFileObject jfo = _fileManager.getJavaFileForOutput(
-				StandardLocation.SOURCE_OUTPUT, name.toString(), JavaFileObject.Kind.SOURCE, null);
+		String moduleAndPkgString = name.toString();
+		int slash = moduleAndPkgString.indexOf('/');
+		String mod = null;
+		if (slash != -1) {
+			name = moduleAndPkgString.substring(slash + 1, name.length());
+			mod = moduleAndPkgString.substring(0, slash);
+		}
+		TypeElement typeElement = _env._elementUtils.getTypeElement(name);
+		if (typeElement != null) {
+			throw new FilerException("Source file already exists : " + moduleAndPkgString); //$NON-NLS-1$
+		}
+		Location location = mod == null ? StandardLocation.SOURCE_OUTPUT : _fileManager.getLocationForModule(StandardLocation.SOURCE_OUTPUT, mod);
+		JavaFileObject jfo = _fileManager.getJavaFileForOutput(location, name.toString(), JavaFileObject.Kind.SOURCE, null);
 		URI uri = jfo.toUri();
 		if (_createdFiles.contains(uri)) {
 			throw new FilerException("Source file already created : " + name); //$NON-NLS-1$
diff --git a/org.eclipse.jdt.compiler.apt/src/org/eclipse/jdt/internal/compiler/apt/util/EclipseFileManager.java b/org.eclipse.jdt.compiler.apt/src/org/eclipse/jdt/internal/compiler/apt/util/EclipseFileManager.java
index f6a71b1..b12f1d7 100644
--- a/org.eclipse.jdt.compiler.apt/src/org/eclipse/jdt/internal/compiler/apt/util/EclipseFileManager.java
+++ b/org.eclipse.jdt.compiler.apt/src/org/eclipse/jdt/internal/compiler/apt/util/EclipseFileManager.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2006, 2017 IBM Corporation and others.
+ * Copyright (c) 2006, 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
@@ -1390,7 +1390,17 @@
 	@Override
 	public Location getLocationForModule(Location location, String moduleName) throws IOException {
 		validateModuleLocation(location, moduleName);
-		return this.locationHandler.getLocation(location, moduleName);
+		Location result = this.locationHandler.getLocation(location, moduleName);
+		if (result == null && location == StandardLocation.CLASS_OUTPUT) {
+			LocationWrapper wrapper = this.locationHandler.getLocation(StandardLocation.MODULE_SOURCE_PATH, moduleName);
+			deriveOutputLocationForModules(moduleName, wrapper.paths);
+			result = getLocationForModule(location, moduleName);
+		} else if (result == null && location == StandardLocation.SOURCE_OUTPUT) {
+			LocationWrapper wrapper = this.locationHandler.getLocation(StandardLocation.MODULE_SOURCE_PATH, moduleName);
+			deriveSourceOutputLocationForModules(moduleName, wrapper.paths);
+			result = getLocationForModule(location, moduleName);
+		}
+		return result;
 	}
 
 	@Override
@@ -1448,7 +1458,50 @@
 		}
 		return null;
 	}
-
+	private void deriveOutputLocationForModules(String moduleName, Collection<? extends Path> paths) {
+		LocationWrapper wrapper = this.locationHandler.getLocation(StandardLocation.CLASS_OUTPUT, moduleName);
+		if (wrapper == null) {
+			// First get from our internally known location for legacy/unnamed location
+			wrapper = this.locationHandler.getLocation(StandardLocation.CLASS_OUTPUT, ""); //$NON-NLS-1$
+			if (wrapper == null) {
+				wrapper = this.locationHandler.getLocation(StandardLocation.CLASS_OUTPUT);
+			}
+			if (wrapper != null) {
+				Iterator<? extends Path> iterator = wrapper.paths.iterator();
+				if (iterator.hasNext()) {
+					try {
+					// Per module output location is always a singleton list
+					Path path = iterator.next().resolve(moduleName);
+					this.locationHandler.setLocation(StandardLocation.CLASS_OUTPUT, moduleName, Collections.singletonList(path));
+					} catch(Exception e) {
+						e.printStackTrace();
+					}
+				}
+			}
+		}
+	}
+	private void deriveSourceOutputLocationForModules(String moduleName, Collection<? extends Path> paths) {
+		LocationWrapper wrapper = this.locationHandler.getLocation(StandardLocation.SOURCE_OUTPUT, moduleName);
+		if (wrapper == null) {
+			// First get from our internally known location for legacy/unnamed location
+			wrapper = this.locationHandler.getLocation(StandardLocation.SOURCE_OUTPUT, ""); //$NON-NLS-1$
+			if (wrapper == null) {
+				wrapper = this.locationHandler.getLocation(StandardLocation.SOURCE_OUTPUT);
+			}
+			if (wrapper != null) {
+				Iterator<? extends Path> iterator = wrapper.paths.iterator();
+				if (iterator.hasNext()) {
+					try {
+					// Per module output location is always a singleton list
+					Path path = iterator.next().resolve(moduleName);
+					this.locationHandler.setLocation(StandardLocation.SOURCE_OUTPUT, moduleName, Collections.singletonList(path));
+					} catch(Exception e) {
+						e.printStackTrace();
+					}
+				}
+			}
+		}
+	}
 	@Override
 	public void setLocationForModule(Location location, String moduleName, Collection<? extends Path> paths) throws IOException {
 		validateModuleLocation(location, moduleName);
diff --git a/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/tool/EclipseFileManager.java b/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/tool/EclipseFileManager.java
index 62472da..a5c8c2d 100644
--- a/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/tool/EclipseFileManager.java
+++ b/org.eclipse.jdt.compiler.tool/src/org/eclipse/jdt/internal/compiler/tool/EclipseFileManager.java
@@ -1421,6 +1421,10 @@
 			LocationWrapper wrapper = this.locationHandler.getLocation(StandardLocation.MODULE_SOURCE_PATH, moduleName);
 			deriveOutputLocationForModules(moduleName, wrapper.paths);
 			result = getLocationForModule(location, moduleName);
+		} else if (result == null && location == StandardLocation.SOURCE_OUTPUT) {
+			LocationWrapper wrapper = this.locationHandler.getLocation(StandardLocation.MODULE_SOURCE_PATH, moduleName);
+			deriveSourceOutputLocationForModules(moduleName, wrapper.paths);
+			result = getLocationForModule(location, moduleName);
 		}
 		return result;
 	}
@@ -1502,6 +1506,28 @@
 			}
 		}
 	}
+	private void deriveSourceOutputLocationForModules(String moduleName, Collection<? extends Path> paths) {
+		LocationWrapper wrapper = this.locationHandler.getLocation(StandardLocation.SOURCE_OUTPUT, moduleName);
+		if (wrapper == null) {
+			// First get from our internally known location for legacy/unnamed location
+			wrapper = this.locationHandler.getLocation(StandardLocation.SOURCE_OUTPUT, ""); //$NON-NLS-1$
+			if (wrapper == null) {
+				wrapper = this.locationHandler.getLocation(StandardLocation.SOURCE_OUTPUT);
+			}
+			if (wrapper != null) {
+				Iterator<? extends Path> iterator = wrapper.paths.iterator();
+				if (iterator.hasNext()) {
+					try {
+					// Per module output location is always a singleton list
+					Path path = iterator.next().resolve(moduleName);
+					this.locationHandler.setLocation(StandardLocation.SOURCE_OUTPUT, moduleName, Collections.singletonList(path));
+					} catch(Exception e) {
+						e.printStackTrace();
+					}
+				}
+			}
+		}
+	}
 	@Override
 	public void setLocationForModule(Location location, String moduleName, Collection<? extends Path> paths) throws IOException {
 		validateModuleLocation(location, moduleName);