From e2a1f7efb43c8285c30d65946b3bb6b1c17836ca Mon Sep 17 00:00:00 2001 From: JBYoshi Date: Sun, 5 Mar 2017 20:12:16 -0600 Subject: [PATCH] Add error message for @Listener methods. --- .../plugin/processor/ListenerProcessor.java | 104 ++++++++++++++++++ .../plugin/processor/PluginProcessor.java | 18 +-- .../plugin/processor/ProcessorUtils.java | 51 +++++++++ .../javax.annotation.processing.Processor | 1 + 4 files changed, 157 insertions(+), 17 deletions(-) create mode 100644 src/ap/java/org/spongepowered/plugin/processor/ListenerProcessor.java create mode 100644 src/ap/java/org/spongepowered/plugin/processor/ProcessorUtils.java diff --git a/src/ap/java/org/spongepowered/plugin/processor/ListenerProcessor.java b/src/ap/java/org/spongepowered/plugin/processor/ListenerProcessor.java new file mode 100644 index 00000000000..d038e9fd56c --- /dev/null +++ b/src/ap/java/org/spongepowered/plugin/processor/ListenerProcessor.java @@ -0,0 +1,104 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.plugin.processor; + +import static javax.tools.Diagnostic.Kind.ERROR; + +import org.spongepowered.api.event.Event; +import org.spongepowered.api.event.Listener; + +import java.util.List; +import java.util.Set; + +import javax.annotation.processing.AbstractProcessor; +import javax.annotation.processing.Messager; +import javax.annotation.processing.RoundEnvironment; +import javax.annotation.processing.SupportedAnnotationTypes; +import javax.annotation.processing.SupportedSourceVersion; +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.Modifier; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.VariableElement; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.util.Elements; +import javax.lang.model.util.Types; +import javax.tools.Diagnostic; + +@SupportedAnnotationTypes(ListenerProcessor.LISTENER_ANNOTATION_CLASS) +@SupportedSourceVersion(SourceVersion.RELEASE_8) +public class ListenerProcessor extends AbstractProcessor { + + static final String LISTENER_ANNOTATION_CLASS = "org.spongepowered.api.event.Listener"; + private static final String EVENT_CLASS = Event.class.getName(); + + @Override + public boolean process(Set annotations, RoundEnvironment roundEnv) { + if (ProcessorUtils.contains(annotations, Listener.class)) { + for (Element e : roundEnv.getElementsAnnotatedWith(Listener.class)) { + if (e.getKind() != ElementKind.METHOD) { + processingEnv.getMessager().printMessage(ERROR, "Invalid element of type " + e.getKind() + " annotated with @Listener", e); + continue; + } + ExecutableElement method = (ExecutableElement) e; + + Messager msg = this.processingEnv.getMessager(); + if (method.getModifiers().contains(Modifier.STATIC)) { + msg.printMessage(Diagnostic.Kind.ERROR, "method must not be static", method); + } + if (!method.getModifiers().contains(Modifier.PUBLIC)) { + msg.printMessage(Diagnostic.Kind.ERROR, "method must be public", method); + } + if (method.getModifiers().contains(Modifier.ABSTRACT)) { + msg.printMessage(Diagnostic.Kind.ERROR, "method must not be abstract", method); + } + if (method.getEnclosingElement().getKind().isInterface()) { + msg.printMessage(Diagnostic.Kind.ERROR, "interfaces cannot declare listeners", method); + } + if (method.getReturnType().getKind() != TypeKind.VOID) { + msg.printMessage(Diagnostic.Kind.ERROR, "method must return void", method); + } + List parameters = method.getParameters(); + if (parameters.isEmpty() || !isTypeSubclass(parameters.get(0), EVENT_CLASS)) { + msg.printMessage(Diagnostic.Kind.ERROR, "method must have an Event as its first parameter", method); + } + } + } + + return false; + } + + private boolean isTypeSubclass(Element typedElement, String subclass) { + Elements elements = processingEnv.getElementUtils(); + Types types = processingEnv.getTypeUtils(); + + TypeMirror event = types.getDeclaredType(elements.getTypeElement(subclass)); + return types.isAssignable(typedElement.asType(), event); + } + +} diff --git a/src/ap/java/org/spongepowered/plugin/processor/PluginProcessor.java b/src/ap/java/org/spongepowered/plugin/processor/PluginProcessor.java index 9fcd3f352d4..4adc8114db7 100644 --- a/src/ap/java/org/spongepowered/plugin/processor/PluginProcessor.java +++ b/src/ap/java/org/spongepowered/plugin/processor/PluginProcessor.java @@ -39,7 +39,6 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; -import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.List; @@ -118,7 +117,7 @@ public boolean process(Set annotations, RoundEnvironment return false; } - if (!contains(annotations, Plugin.class)) { + if (!ProcessorUtils.contains(annotations, Plugin.class)) { return false; } @@ -200,19 +199,4 @@ private Messager getMessager() { return this.processingEnv.getMessager(); } - private static boolean contains(Collection elements, Class clazz) { - if (elements.isEmpty()) { - return false; - } - - final String name = clazz.getName(); - for (TypeElement element : elements) { - if (element.getQualifiedName().contentEquals(name)) { - return true; - } - } - - return false; - } - } diff --git a/src/ap/java/org/spongepowered/plugin/processor/ProcessorUtils.java b/src/ap/java/org/spongepowered/plugin/processor/ProcessorUtils.java new file mode 100644 index 00000000000..76d195919da --- /dev/null +++ b/src/ap/java/org/spongepowered/plugin/processor/ProcessorUtils.java @@ -0,0 +1,51 @@ +/* + * This file is part of SpongeAPI, licensed under the MIT License (MIT). + * + * Copyright (c) SpongePowered + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + * THE SOFTWARE. + */ +package org.spongepowered.plugin.processor; + +import java.util.Collection; + +import javax.lang.model.element.TypeElement; + +final class ProcessorUtils { + + private ProcessorUtils() { + } + + static boolean contains(Collection elements, Class clazz) { + if (elements.isEmpty()) { + return false; + } + + final String name = clazz.getName(); + for (TypeElement element : elements) { + if (element.getQualifiedName().contentEquals(name)) { + return true; + } + } + + return false; + } + +} diff --git a/src/ap/resources/META-INF/services/javax.annotation.processing.Processor b/src/ap/resources/META-INF/services/javax.annotation.processing.Processor index ef971b11e2f..b1635cc9aa0 100644 --- a/src/ap/resources/META-INF/services/javax.annotation.processing.Processor +++ b/src/ap/resources/META-INF/services/javax.annotation.processing.Processor @@ -1 +1,2 @@ org.spongepowered.plugin.processor.PluginProcessor +org.spongepowered.plugin.processor.ListenerProcessor