diff options
author | Jesper Moller | 2014-04-25 08:31:49 +0000 |
---|---|---|
committer | Jayaprakash Arthanareeswaran | 2014-04-25 09:06:08 +0000 |
commit | b6e21c793f3617f9c60f843e02a0537d5bd09d93 (patch) | |
tree | f48825a8adc2d353d8cb07c081de15c223c260d3 /org.eclipse.jdt.apt.pluggable.tests | |
parent | 7cbfac0f262651a92552aa1b245acf5ef1db3bb0 (diff) | |
download | eclipse.jdt.core-b6e21c793f3617f9c60f843e02a0537d5bd09d93.tar.gz eclipse.jdt.core-b6e21c793f3617f9c60f843e02a0537d5bd09d93.tar.xz eclipse.jdt.core-b6e21c793f3617f9c60f843e02a0537d5bd09d93.zip |
Bug 416027 - [1.8] Enable reflected parameter names during annotation
processing
Signed-off-by: Jesper Moller <jesper@selskabet.org>
Diffstat (limited to 'org.eclipse.jdt.apt.pluggable.tests')
6 files changed, 392 insertions, 3 deletions
diff --git a/org.eclipse.jdt.apt.pluggable.tests/lib/annotations.jar b/org.eclipse.jdt.apt.pluggable.tests/lib/annotations.jar Binary files differindex f9febfbdd9..d53dcb7e9e 100644 --- a/org.eclipse.jdt.apt.pluggable.tests/lib/annotations.jar +++ b/org.eclipse.jdt.apt.pluggable.tests/lib/annotations.jar diff --git a/org.eclipse.jdt.apt.pluggable.tests/plugin.xml b/org.eclipse.jdt.apt.pluggable.tests/plugin.xml index 806cb32707..cc988bdfab 100644 --- a/org.eclipse.jdt.apt.pluggable.tests/plugin.xml +++ b/org.eclipse.jdt.apt.pluggable.tests/plugin.xml @@ -37,6 +37,9 @@ <java6processor class="org.eclipse.jdt.apt.pluggable.tests.processors.modeltester.ModelTesterProc"> </java6processor> + <java6processor + class="org.eclipse.jdt.apt.pluggable.tests.processors.modeltester.ModelTester8Proc"> + </java6processor> </java6processors> </extension> </plugin> diff --git a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/ModelTests.java b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/ModelTests.java index 98c20e695c..6fbe44a0dc 100644 --- a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/ModelTests.java +++ b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/ModelTests.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2008 Walter Harley and others + * Copyright (c) 2008, 2014 Walter Harley 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 @@ -11,14 +11,21 @@ package org.eclipse.jdt.apt.pluggable.tests; +import javax.lang.model.SourceVersion; + import junit.framework.Test; import junit.framework.TestSuite; +import org.eclipse.core.resources.IFile; import org.eclipse.core.resources.IProject; +import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.jdt.apt.core.util.AptConfig; +import org.eclipse.jdt.apt.pluggable.tests.processors.modeltester.ModelTester8Proc; import org.eclipse.jdt.apt.pluggable.tests.processors.modeltester.ModelTesterProc; import org.eclipse.jdt.core.IJavaProject; +import org.eclipse.jdt.internal.compiler.impl.CompilerOptions; /** * Basic tests for the typesystem model interfaces in the IDE. @@ -81,4 +88,74 @@ public class ModelTests extends TestBase assertTrue("Processor did not run", ProcessorTestStatus.processorRan()); assertEquals("Processor reported errors", ProcessorTestStatus.NO_ERRORS, ProcessorTestStatus.getErrors()); } + + /** + * Call ModelTester8Proc.testMethodParameters(), which checks the type of a method + */ + public void testMethodParameters() throws Throwable { + if (!canRunJava8()) { + return; + } + ProcessorTestStatus.reset(); + IJavaProject jproj = createJava8Project(_projectName); + jproj.setOption(CompilerOptions.OPTION_LocalVariableAttribute, CompilerOptions.DO_NOT_GENERATE); + jproj.setOption(CompilerOptions.OPTION_MethodParametersAttribute, CompilerOptions.GENERATE); + disableJava5Factories(jproj); + IProject proj = jproj.getProject(); + IPath projPath = proj.getFullPath(); + + env.addClass(projPath.append("src"), + ModelTester8Proc.TEST_METHOD_PARAMETERS_TYPE1_PKG, + ModelTester8Proc.TEST_METHOD_PARAMETERS_TYPE1_CLASS, + ModelTester8Proc.TEST_METHOD_PARAMETERS_TYPE1_SOURCE); + env.addClass(projPath.append("src"), + ModelTester8Proc.TEST_METHOD_PARAMETERS_TYPE2_PKG, + ModelTester8Proc.TEST_METHOD_PARAMETERS_TYPE2_CLASS, + ModelTester8Proc.TEST_METHOD_PARAMETERS_TYPE2_SOURCE); + fullBuild(); + expectingNoProblems(); + assertFalse("Processor ran too early", ProcessorTestStatus.processorRan()); + + keepBinaryOnly(jproj, + ModelTester8Proc.TEST_METHOD_PARAMETERS_TYPE1_PKG, + ModelTester8Proc.TEST_METHOD_PARAMETERS_TYPE1_CLASS); + + keepBinaryOnly(jproj, + ModelTester8Proc.TEST_METHOD_PARAMETERS_TYPE2_PKG, + ModelTester8Proc.TEST_METHOD_PARAMETERS_TYPE2_CLASS); + + fullBuild(); + + env.addClass(projPath.append("src"), + ModelTester8Proc.TEST_METHOD_PARAMETERS_TYPE3_PKG, + ModelTester8Proc.TEST_METHOD_PARAMETERS_TYPE3_CLASS, + ModelTester8Proc.TEST_METHOD_PARAMETERS_TYPE3_SOURCE); + + AptConfig.setEnabled(jproj, true); + + fullBuild(); + expectingNoProblems(); + assertTrue("Processor did not run", ProcessorTestStatus.processorRan()); + assertEquals("Processor reported errors", ProcessorTestStatus.NO_ERRORS, ProcessorTestStatus.getErrors()); + } + + private void keepBinaryOnly(IJavaProject jproj, String packageName, String className) throws CoreException { + IFile realSourceFile = jproj.getProject().getFolder("src").getFolder(packageName).getFile(className + ".java"); + IFile compiledClassFile = jproj.getProject().getFolder("bin").getFolder(packageName).getFile(className + ".class"); + assertTrue("No compiled class for " + packageName + "." + className + ": ",compiledClassFile.exists()); + IFile prebuiltClassFile = jproj.getProject().getFolder("prebuilt").getFolder(packageName).getFile(className + ".class"); + assertFalse("Compiled class already in src: ",prebuiltClassFile.exists()); + compiledClassFile.copy(prebuiltClassFile.getFullPath(), true, new NullProgressMonitor()); + assertTrue("Compiled class not copied to src",prebuiltClassFile.exists()); + realSourceFile.delete(true, new NullProgressMonitor()); + assertFalse("Still source?: ", realSourceFile.exists()); + } + public boolean canRunJava8() { + try { + SourceVersion.valueOf("RELEASE_8"); + } catch(IllegalArgumentException iae) { + return false; + } + return true; + } } diff --git a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/TestBase.java b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/TestBase.java index 99789a0cea..1954d27bcc 100644 --- a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/TestBase.java +++ b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/TestBase.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2007, 2008 BEA Systems, Inc. and others. + * Copyright (c) 2007, 2014 BEA Systems, Inc. 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 @@ -7,6 +7,7 @@ * * Contributors: * BEA Systems, Inc. - initial API and implementation + * Jesper Steen Moller - Bug 412150 [1.8] [compiler] Enable reflected parameter names during annotation processing *******************************************************************************/ package org.eclipse.jdt.apt.pluggable.tests; @@ -20,8 +21,8 @@ import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IPath; import org.eclipse.jdt.apt.core.internal.util.FactoryContainer; -import org.eclipse.jdt.apt.core.internal.util.FactoryPath; import org.eclipse.jdt.apt.core.internal.util.FactoryContainer.FactoryType; +import org.eclipse.jdt.apt.core.internal.util.FactoryPath; import org.eclipse.jdt.apt.core.util.AptConfig; import org.eclipse.jdt.core.IJavaProject; import org.eclipse.jdt.core.tests.builder.BuilderTests; @@ -31,6 +32,7 @@ public class TestBase extends BuilderTests { protected static final String JAVA_16_COMPLIANCE = "1.6"; + protected static final String JAVA_18_COMPLIANCE = "1.8"; protected String _projectName; protected static int _projectSerial = 0; // used to create unique project names, to avoid resource deletion problems @@ -81,6 +83,32 @@ public class TestBase extends BuilderTests addAnnotationJar(javaProj); return javaProj; } + + /** + * Create a java project with java libraries and test annotations on classpath + * (compiler level is 1.8). Use "src" as source folder and "bin" as output folder. APT + * is not enabled. + * + * @param projectName + * @return a java project that has been added to the current workspace. + * @throws Exception + */ + protected static IJavaProject createJava8Project(final String projectName) throws Exception { + // Note, make sure this is run only with a JRE 8 and above. + IPath projectPath = env.addProject(projectName, JAVA_18_COMPLIANCE); + env.addExternalJars(projectPath, Util.getJavaClassLibs()); + + // remove old package fragment root so that names don't collide + env.removePackageFragmentRoot(projectPath, ""); //$NON-NLS-1$ + env.addPackageFragmentRoot(projectPath, "src"); //$NON-NLS-1$ + env.setOutputFolder(projectPath, "bin"); //$NON-NLS-1$ + final IJavaProject javaProj = env.getJavaProject(projectPath); + javaProj.getProject().getFolder("prebuilt").create(true, true, null); + javaProj.getProject().getFolder("prebuilt").getFolder("p").create(true, true, null); + env.addClassFolder(projectPath, projectPath.append("prebuilt"), true); + addAnnotationJar(javaProj); + return javaProj; + } /** * Ensure that there are no Java 5 processors on the factory path, as they can cause diff --git a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/annotations/ModelTest8Trigger.java b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/annotations/ModelTest8Trigger.java new file mode 100644 index 0000000000..d1a2c50a38 --- /dev/null +++ b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/annotations/ModelTest8Trigger.java @@ -0,0 +1,24 @@ +/******************************************************************************* + * Copyright (c) 2014 Jesper Steen Moller 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Jesper Steen Moller - initial API and implementation + * + *******************************************************************************/ + +package org.eclipse.jdt.apt.pluggable.tests.annotations; + +public @interface ModelTest8Trigger { + /** Name of test method to run */ + String test(); + + /** Arbitrary argument */ + String arg0() default ""; + + /** Arbitrary argument */ + String arg1() default ""; +} diff --git a/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/processors/modeltester/ModelTester8Proc.java b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/processors/modeltester/ModelTester8Proc.java new file mode 100644 index 0000000000..a1d691fb8b --- /dev/null +++ b/org.eclipse.jdt.apt.pluggable.tests/src/org/eclipse/jdt/apt/pluggable/tests/processors/modeltester/ModelTester8Proc.java @@ -0,0 +1,257 @@ +/******************************************************************************* + * Copyright (c) 2014 Jesper Steen Moller 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 + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * Jesper Steen Moller - initial API and implementation + * + *******************************************************************************/ + +package org.eclipse.jdt.apt.pluggable.tests.processors.modeltester; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.ProcessingEnvironment; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedOptions; +import javax.lang.model.SourceVersion; +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.ElementFilter; + +import org.eclipse.jdt.apt.pluggable.tests.ModelTests; +import org.eclipse.jdt.apt.pluggable.tests.ProcessorTestStatus; +import org.eclipse.jdt.apt.pluggable.tests.annotations.LookAt; +import org.eclipse.jdt.apt.pluggable.tests.annotations.ModelTest8Trigger; + +/** + * This processor tests features specific to JEP 118. + * One processor can run many tests. The JUnit tests specify which test to run by passing its name in to the + * ModelTest8Trigger annotation. + * + * Although this test processor only needs to be run with 1.8 JRE, we don't explicitly state the supported version. + * The clients invoking this must ensure that this is invoked only with JRE 1.8 and above, + * like it's done in {@link ModelTests#testMethodParameters}. + * + * @since 3.9 BETA_JAVA8 + */ +@SupportedAnnotationTypes( { "org.eclipse.jdt.apt.pluggable.tests.annotations.ModelTest8Trigger" }) +@SupportedOptions( {}) +public class ModelTester8Proc extends AbstractProcessor { + public static final String TEST_METHOD_PARAMETERS_TYPE1_PKG = "p"; + public static final String TEST_METHOD_PARAMETERS_TYPE1_CLASS = "Bar"; + public static final String TEST_METHOD_PARAMETERS_TYPE1_SOURCE = + "package p;\n" + + "public class Bar {\n" + + " public void otherStuff(final double fun, String beans) { }\n" + + "}"; + + public static final String TEST_METHOD_PARAMETERS_TYPE2_PKG = "p"; + public static final String TEST_METHOD_PARAMETERS_TYPE2_CLASS = "MyEnum"; + public static final String TEST_METHOD_PARAMETERS_TYPE2_SOURCE = + "package p;\n" + + "\n" + + "public enum MyEnum {\n" + + " ONE(1), TWO(2);\n" + + " \n" + + " private MyEnum(final int finalIntValue) { this.var = finalIntValue; }\n" + + " int var;\n" + + "}\n"; + + public static final String TEST_METHOD_PARAMETERS_TYPE3_PKG = "p"; + public static final String TEST_METHOD_PARAMETERS_TYPE3_CLASS = "Foo"; + public static final String TEST_METHOD_PARAMETERS_TYPE3_SOURCE = + "package p;\n" + + "import org.eclipse.jdt.apt.pluggable.tests.annotations.ModelTest8Trigger;\n" + + "import org.eclipse.jdt.apt.pluggable.tests.annotations.LookAt;\n" + + "@ModelTest8Trigger(test = \"testMethodParameters\")" + + "public class Foo {\n" + + " @LookAt\n" + + " public Bar doStuff(final int number, String textual) { return null; }\n" + + " @LookAt\n" + + " public MyEnum guess(final int isItOne) { return isItOne == 1 ? MyEnum.ONE : MyEnum.TWO; }\n" + + "}"; + + + @SuppressWarnings("unused") + private ProcessingEnvironment _processingEnv; + + /* + * (non-Javadoc) + * + * @see javax.annotation.processing.AbstractProcessor#init(javax.annotation.processing.ProcessingEnvironment) + */ + @Override + public synchronized void init(ProcessingEnvironment processingEnv) { + super.init(processingEnv); + _processingEnv = processingEnv; + } + + public SourceVersion getSupportedSourceVersion() { + return SourceVersion.latestSupported(); + } + + /* + * (non-Javadoc) + * + * @see javax.annotation.processing.AbstractProcessor#process(java.util.Set, + * javax.annotation.processing.RoundEnvironment) + */ + @Override + public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { + ProcessorTestStatus.setProcessorRan(); + if (!roundEnv.processingOver() && !annotations.isEmpty()) { + round(annotations, roundEnv); + } + return true; + } + + /** + * Perform a round of processing: for a given annotation instance, determine what test method it + * specifies, and invoke that method, passing in the annotated element. + */ + private void round(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) { + TypeElement modelTesterAnno = annotations.iterator().next(); + Set<? extends Element> annotatedEls = roundEnv.getElementsAnnotatedWith(modelTesterAnno); + for (Element annotatedEl : annotatedEls) { + ModelTest8Trigger modelTesterMirror = annotatedEl.getAnnotation(ModelTest8Trigger.class); + String testMethodName = modelTesterMirror.test(); + String arg0 = modelTesterMirror.arg0(); + String arg1 = modelTesterMirror.arg1(); + if (null != testMethodName && testMethodName.length() > 0) { + try { + Method testMethod = ModelTester8Proc.class.getMethod(testMethodName, + RoundEnvironment.class, Element.class, String.class, String.class); + testMethod.invoke(this, roundEnv, annotatedEl, arg0, arg1); + } catch (Exception e) { + Throwable t; + t = (e instanceof InvocationTargetException) ? t = e.getCause() : e; + t.printStackTrace(); + // IllegalStateException probably means test method called ProcessorTestStatus.fail() + String msg = (t instanceof IllegalStateException) ? + t.getMessage() : + t.getClass().getSimpleName() + " invoking test method " + + testMethodName + " - see console for details"; + ProcessorTestStatus.fail(msg); + } + } + } + } + + /** + * Check the types of some methods (check that the annotation processing uses the parsed MethodParameters + * attribute from class files according to JEP 118) + * @see #TEST_METHOD_PARAMETERS_TYPE1_SOURCE + * @see #TEST_METHOD_PARAMETERS_TYPE2_SOURCE + * @see #TEST_METHOD_PARAMETERS_TYPE3_SOURCE + */ + public void testMethodParameters(RoundEnvironment roundEnv, Element e, String arg0, String arg1) throws Exception { + Map<String, ExecutableElement> methods = new HashMap<String, ExecutableElement>(); + Iterable<? extends Element> elements; + + elements = roundEnv.getElementsAnnotatedWith(LookAt.class); + for (ExecutableElement method : ElementFilter.methodsIn(elements)) { + methods.put(method.getSimpleName().toString(), method); + } + + // Examine the easy case, the Foo.doStuff method + ExecutableElement mDoStuff = methods.get("doStuff"); + if (mDoStuff == null) { + ProcessorTestStatus.fail("Method doStuff() was not found"); + } + if (mDoStuff.getKind() != ElementKind.METHOD) { + ProcessorTestStatus.fail("ElementKind of method doStuff() was " + mDoStuff.getKind() + + ", expected METHOD"); + } + // Examine parameters + List<? extends VariableElement> parameters = mDoStuff.getParameters(); + if (parameters.size() != 2) { + ProcessorTestStatus.fail("Expected two parameters for doStuff()"); + } + ProcessorTestStatus.assertEquals("Wrong name", "number", parameters.get(0).getSimpleName().toString()); + ProcessorTestStatus.assertEquals("Wrong name", "textual", parameters.get(1).getSimpleName().toString()); + + /////////////////////////////////////////////////////////////////////////////////// + + // Cool, now check 'p.Bar.otherStuff' which is also the return type of doStuff + TypeMirror returnType = mDoStuff.getReturnType(); + if (returnType.getKind() != TypeKind.DECLARED) + ProcessorTestStatus.fail("TypeKind of method doStuff()'s return type " + returnType.getKind() + + ", expected DECLARED"); + + DeclaredType barType = (DeclaredType) returnType; + TypeElement bar = (TypeElement) barType.asElement(); + + for (Element method : bar.getEnclosedElements()) { + if (method.getKind() == ElementKind.METHOD) + methods.put(method.getSimpleName().toString(), (ExecutableElement)method); + } + + ExecutableElement mOtherStuff = methods.get("otherStuff"); + if (mOtherStuff == null) { + ProcessorTestStatus.fail("Method otherStuff() was not found"); + } + if (mOtherStuff.getKind() != ElementKind.METHOD) { + ProcessorTestStatus.fail("ElementKind of method otherStuff() was " + mOtherStuff.getKind() + + ", expected METHOD"); + } + // Examine parameters + List<? extends VariableElement> otherParameters = mOtherStuff.getParameters(); + if (otherParameters.size() != 2) { + ProcessorTestStatus.fail("Expected two parameters for otherStuff()"); + } + ProcessorTestStatus.assertEquals("Wrong name", "fun", otherParameters.get(0).getSimpleName().toString()); + ProcessorTestStatus.assertEquals("Wrong name", "beans", otherParameters.get(1).getSimpleName().toString()); + + /////////////////////////////////////////////////////////////////////////////////// + + // Examine the enum as returned by Foo.guess method + ExecutableElement mGuess = methods.get("guess"); + if (mGuess == null) { + ProcessorTestStatus.fail("Method guess() was not found"); + } + if (mGuess.getKind() != ElementKind.METHOD) { + ProcessorTestStatus.fail("ElementKind of method doStuff() was " + mGuess.getKind() + + ", expected METHOD"); + } + + // Cool, now check 'p.Bar.otherStuff' which is also the return type of doStuff + TypeMirror guessReturnType = mGuess.getReturnType(); + if (guessReturnType.getKind() != TypeKind.DECLARED) + ProcessorTestStatus.fail("TypeKind of method guess()'s return type " + guessReturnType.getKind() + + ", expected DECLARED"); + + DeclaredType myEnumType = (DeclaredType) guessReturnType; + TypeElement myEnumClass = (TypeElement) myEnumType.asElement(); + + List<ExecutableElement> ctors = new LinkedList<ExecutableElement>(); + for (Element method : myEnumClass.getEnclosedElements()) { + if (method.getKind() == ElementKind.CONSTRUCTOR) { + ctors.add((ExecutableElement)method); + } + } + + ProcessorTestStatus.assertEquals("Bad # of constructors for MyEnum", 1, ctors.size()); + // Examine parameters + List<? extends VariableElement> ctorParameters = ctors.get(0).getParameters(); + ProcessorTestStatus.assertEquals("Bad # of parameters for MyEnum ctor", 1, ctorParameters.size()); + ProcessorTestStatus.assertEquals("Wrong name", "finalIntValue", ctorParameters.get(0).getSimpleName().toString()); + } +} |