8173636: Results from Processor.getSupportedAnnotationTypes should be intepreted strictly

Co-authored-by: Joe Darcy <joe.darcy@oracle.com>
Reviewed-by: darcy, jjg
This commit is contained in:
Jan Lahoda 2017-02-06 15:57:35 +01:00
parent d6090047be
commit eb5ba01b02
4 changed files with 114 additions and 28 deletions

View file

@ -83,7 +83,7 @@ public abstract class AbstractProcessor implements Processor {
if (so == null) if (so == null)
return Collections.emptySet(); return Collections.emptySet();
else else
return arrayToSet(so.value()); return arrayToSet(so.value(), false);
} }
/** /**
@ -92,21 +92,31 @@ public abstract class AbstractProcessor implements Processor {
* same set of strings as the annotation. If the class is not so * same set of strings as the annotation. If the class is not so
* annotated, an empty set is returned. * annotated, an empty set is returned.
* *
* If the {@link ProcessingEvironment#getSourceVersion source
* version} does not support modules, in other words if it is less
* than or equal to {@link SourceVersion#RELEASE_8 RELEASE_8},
* then any leading {@link Processor#getSupportedAnnotationTypes
* module prefixes} are stripped from the names.
*
* @return the names of the annotation types supported by this * @return the names of the annotation types supported by this
* processor, or an empty set if none * processor, or an empty set if none
*/ */
public Set<String> getSupportedAnnotationTypes() { public Set<String> getSupportedAnnotationTypes() {
SupportedAnnotationTypes sat = this.getClass().getAnnotation(SupportedAnnotationTypes.class); SupportedAnnotationTypes sat = this.getClass().getAnnotation(SupportedAnnotationTypes.class);
boolean initialized = isInitialized();
if (sat == null) { if (sat == null) {
if (isInitialized()) if (initialized)
processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING, processingEnv.getMessager().printMessage(Diagnostic.Kind.WARNING,
"No SupportedAnnotationTypes annotation " + "No SupportedAnnotationTypes annotation " +
"found on " + this.getClass().getName() + "found on " + this.getClass().getName() +
", returning an empty set."); ", returning an empty set.");
return Collections.emptySet(); return Collections.emptySet();
} else {
boolean stripModulePrefixes =
initialized &&
processingEnv.getSourceVersion().compareTo(SourceVersion.RELEASE_8) <= 0;
return arrayToSet(sat.value(), stripModulePrefixes);
} }
else
return arrayToSet(sat.value());
} }
/** /**
@ -185,11 +195,18 @@ public abstract class AbstractProcessor implements Processor {
return initialized; return initialized;
} }
private static Set<String> arrayToSet(String[] array) { private static Set<String> arrayToSet(String[] array,
boolean stripModulePrefixes) {
assert array != null; assert array != null;
Set<String> set = new HashSet<>(array.length); Set<String> set = new HashSet<>(array.length);
for (String s : array) for (String s : array) {
if (stripModulePrefixes) {
int index = s.indexOf('/');
if (index != -1)
s = s.substring(index + 1);
}
set.add(s); set.add(s);
}
return Collections.unmodifiableSet(set); return Collections.unmodifiableSet(set);
} }
} }

View file

@ -254,6 +254,14 @@ public interface Processor {
* a.B} which reside in different modules. To only support {@code * a.B} which reside in different modules. To only support {@code
* a.B} in the {@code Foo} module, instead use {@code "Foo/a.B"}. * a.B} in the {@code Foo} module, instead use {@code "Foo/a.B"}.
* *
* If a module name is included, only an annotation in that module
* is matched. In particular, if a module name is given in an
* environment where modules are not supported, such as an
* annotation processing environment configured for a {@linkplain
* javax.annotation.processing.ProcessingEnvironment#getSourceVersion
* source version} without modules, then the annotation types with
* a module name do <em>not</em> match.
*
* Finally, {@code "*"} by itself represents the set of all * Finally, {@code "*"} by itself represents the set of all
* annotation types, including the empty set. Note that a * annotation types, including the empty set. Note that a
* processor should not claim {@code "*"} unless it is actually * processor should not claim {@code "*"} unless it is actually
@ -280,6 +288,14 @@ public interface Processor {
* where <i>TypeName</i> is as defined in * where <i>TypeName</i> is as defined in
* <cite>The Java&trade; Language Specification</cite>. * <cite>The Java&trade; Language Specification</cite>.
* *
* @apiNote When running in an environment which supports modules,
* processors are encouraged to include the module prefix when
* describing their supported annotation types. The method {@link
* AbstractProcessor.getSupportedAnnotationTypes
* AbstractProcessor.getSupportedAnnotationTypes} provides support
* for stripping off the module prefix when running in an
* environment without modules.
*
* @return the names of the annotation types supported by this processor * @return the names of the annotation types supported by this processor
* @see javax.annotation.processing.SupportedAnnotationTypes * @see javax.annotation.processing.SupportedAnnotationTypes
* @jls 3.8 Identifiers * @jls 3.8 Identifiers

View file

@ -1671,14 +1671,14 @@ public class JavacProcessingEnvironment implements ProcessingEnvironment, Closea
if (s.equals("*")) { if (s.equals("*")) {
return MatchingUtils.validImportStringToPattern(s); return MatchingUtils.validImportStringToPattern(s);
} }
module = ".*/"; module = allowModules ? ".*/" : "";
pkg = s; pkg = s;
} else { } else {
module = Pattern.quote(s.substring(0, slash + 1)); module = Pattern.quote(s.substring(0, slash + 1));
pkg = s.substring(slash + 1); pkg = s.substring(slash + 1);
} }
if (MatchingUtils.isValidImportString(pkg)) { if (MatchingUtils.isValidImportString(pkg)) {
return Pattern.compile((allowModules ? module : "") + MatchingUtils.validImportStringToPatternString(pkg)); return Pattern.compile(module + MatchingUtils.validImportStringToPatternString(pkg));
} else { } else {
log.warning("proc.malformed.supported.string", s, p.getClass().getName()); log.warning("proc.malformed.supported.string", s, p.getClass().getName());
return noMatches; // won't match any valid identifier return noMatches; // won't match any valid identifier

View file

@ -23,7 +23,7 @@
/** /**
* @test * @test
* @bug 8133884 8162711 8133896 8172158 8172262 * @bug 8133884 8162711 8133896 8172158 8172262 8173636
* @summary Verify that annotation processing works. * @summary Verify that annotation processing works.
* @library /tools/lib * @library /tools/lib
* @modules * @modules
@ -173,7 +173,7 @@ public class AnnotationProcessing extends ModuleTestBase {
String[] module2Packages = moduleDef.split("=>"); String[] module2Packages = moduleDef.split("=>");
module2ExpectedEnclosedElements.put(module2Packages[0], module2ExpectedEnclosedElements.put(module2Packages[0],
Arrays.asList(module2Packages[1].split(":"))); List.of(module2Packages[1].split(":")));
} }
} }
@ -359,7 +359,7 @@ public class AnnotationProcessing extends ModuleTestBase {
.writeAll() .writeAll()
.getOutput(Task.OutputKind.DIRECT); .getOutput(Task.OutputKind.DIRECT);
List<String> expected = Arrays.asList("Note: field: m1x"); List<String> expected = List.of("Note: field: m1x");
for (Mode mode : new Mode[] {Mode.API, Mode.CMDLINE}) { for (Mode mode : new Mode[] {Mode.API, Mode.CMDLINE}) {
List<String> log = new JavacTask(tb, mode) List<String> log = new JavacTask(tb, mode)
@ -424,7 +424,7 @@ public class AnnotationProcessing extends ModuleTestBase {
.writeAll() .writeAll()
.getOutputLines(Task.OutputKind.STDERR); .getOutputLines(Task.OutputKind.STDERR);
assertEquals(Arrays.asList("module: m1x"), log); assertEquals(List.of("module: m1x"), log);
} }
@SupportedAnnotationTypes("*") @SupportedAnnotationTypes("*")
@ -472,8 +472,8 @@ public class AnnotationProcessing extends ModuleTestBase {
.writeAll() .writeAll()
.getOutputLines(Task.OutputKind.DIRECT); .getOutputLines(Task.OutputKind.DIRECT);
List<String> expectedLog = Arrays.asList("Note: AP Invoked", List<String> expectedLog = List.of("Note: AP Invoked",
"Note: AP Invoked"); "Note: AP Invoked");
assertEquals(expectedLog, log); assertEquals(expectedLog, log);
@ -600,7 +600,7 @@ public class AnnotationProcessing extends ModuleTestBase {
JavaCompiler comp = ToolProvider.getSystemJavaCompiler(); JavaCompiler comp = ToolProvider.getSystemJavaCompiler();
try (StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null)) { try (StandardJavaFileManager fm = comp.getStandardFileManager(null, null, null)) {
List<String> options = Arrays.asList("-d", scratchClasses.toString()); List<String> options = List.of("-d", scratchClasses.toString());
Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(scratchSrc); Iterable<? extends JavaFileObject> files = fm.getJavaFileObjects(scratchSrc);
CompilationTask task = comp.getTask(null, fm, null, options, null, files); CompilationTask task = comp.getTask(null, fm, null, options, null, files);
@ -939,7 +939,7 @@ public class AnnotationProcessing extends ModuleTestBase {
.writeAll() .writeAll()
.getOutputLines(OutputKind.STDERR); .getOutputLines(OutputKind.STDERR);
expected = Arrays.asList(""); expected = List.of("");
if (!expected.equals(log)) { if (!expected.equals(log)) {
throw new AssertionError("Output does not match; output: " + log); throw new AssertionError("Output does not match; output: " + log);
@ -954,15 +954,17 @@ public class AnnotationProcessing extends ModuleTestBase {
.writeAll() .writeAll()
.getOutputLines(OutputKind.STDERR); .getOutputLines(OutputKind.STDERR);
expected = Arrays.asList("SelectAnnotationBTestAP", expected = List.of("SelectAnnotationBTestAP",
"SelectAnnotationBTestAP"); "SelectAnnotationBTestAP");
if (!expected.equals(log)) { if (!expected.equals(log)) {
throw new AssertionError("Output does not match; output: " + log); throw new AssertionError("Output does not match; output: " + log);
} }
log = new JavacTask(tb) log = new JavacTask(tb)
.options("-processor", SelectAnnotationATestAP.class.getName() + "," + SelectAnnotationBTestAP.class.getName(), .options("-processor", SelectAnnotationATestAP.class.getName() + "," +
SelectAnnotationBTestAP.class.getName() + "," +
SelectAnnotationAStrictTestAP.class.getName(),
"--module-source-path", src.toString(), "--module-source-path", src.toString(),
"-m", "m4x") "-m", "m4x")
.outdir(classes) .outdir(classes)
@ -970,10 +972,43 @@ public class AnnotationProcessing extends ModuleTestBase {
.writeAll() .writeAll()
.getOutputLines(OutputKind.STDERR); .getOutputLines(OutputKind.STDERR);
expected = Arrays.asList("SelectAnnotationATestAP", expected = List.of("SelectAnnotationATestAP",
"SelectAnnotationBTestAP", "SelectAnnotationBTestAP",
"SelectAnnotationATestAP", "SelectAnnotationAStrictTestAP",
"SelectAnnotationBTestAP"); "SelectAnnotationATestAP",
"SelectAnnotationBTestAP",
"SelectAnnotationAStrictTestAP");
if (!expected.equals(log)) {
throw new AssertionError("Output does not match; output: " + log);
}
}
@Test
public void testDisambiguateAnnotationsUnnamedModule(Path base) throws Exception {
Path classes = base.resolve("classes");
Files.createDirectories(classes);
Path src = base.resolve("src");
tb.writeJavaFiles(src,
"package api; public @interface A {}",
"package api; public @interface B {}",
"package impl; import api.*; @A @B public class T {}");
List<String> log = new JavacTask(tb)
.options("-processor", SelectAnnotationATestAP.class.getName() + "," +
SelectAnnotationBTestAP.class.getName() + "," +
SelectAnnotationAStrictTestAP.class.getName())
.outdir(classes)
.files(findJavaFiles(src))
.run()
.writeAll()
.getOutputLines(OutputKind.STDERR);
List<String> expected = List.of("SelectAnnotationBTestAP",
"SelectAnnotationBTestAP");
if (!expected.equals(log)) { if (!expected.equals(log)) {
throw new AssertionError("Output does not match; output: " + log); throw new AssertionError("Output does not match; output: " + log);
@ -994,7 +1029,9 @@ public class AnnotationProcessing extends ModuleTestBase {
"package impl; import api.*; @A @B public class T {}"); "package impl; import api.*; @A @B public class T {}");
List<String> log = new JavacTask(tb) List<String> log = new JavacTask(tb)
.options("-processor", SelectAnnotationATestAP.class.getName() + "," + SelectAnnotationBTestAP.class.getName(), .options("-processor", SelectAnnotationATestAP.class.getName() + "," +
SelectAnnotationBTestAP.class.getName() + "," +
SelectAnnotationAStrictTestAP.class.getName(),
"-source", "8", "-target", "8") "-source", "8", "-target", "8")
.outdir(classes) .outdir(classes)
.files(findJavaFiles(src)) .files(findJavaFiles(src))
@ -1002,10 +1039,10 @@ public class AnnotationProcessing extends ModuleTestBase {
.writeAll() .writeAll()
.getOutputLines(OutputKind.STDERR); .getOutputLines(OutputKind.STDERR);
List<String> expected = Arrays.asList("SelectAnnotationATestAP", List<String> expected = List.of("SelectAnnotationATestAP",
"SelectAnnotationBTestAP", "SelectAnnotationBTestAP",
"SelectAnnotationATestAP", "SelectAnnotationATestAP",
"SelectAnnotationBTestAP"); "SelectAnnotationBTestAP");
if (!expected.equals(log)) { if (!expected.equals(log)) {
throw new AssertionError("Output does not match; output: " + log); throw new AssertionError("Output does not match; output: " + log);
@ -1036,6 +1073,22 @@ public class AnnotationProcessing extends ModuleTestBase {
} }
public static final class SelectAnnotationAStrictTestAP extends AbstractProcessor {
@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
System.err.println("SelectAnnotationAStrictTestAP");
return false;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
return Set.of("m2x/api.A");
}
}
private static void writeFile(String content, Path base, String... pathElements) throws IOException { private static void writeFile(String content, Path base, String... pathElements) throws IOException {
Path file = resolveFile(base, pathElements); Path file = resolveFile(base, pathElements);