diff --git a/service/processor/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java b/service/processor/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java index 06e391e98a..26c1435d6b 100644 --- a/service/processor/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java +++ b/service/processor/src/main/java/com/google/auto/service/processor/AutoServiceProcessor.java @@ -46,6 +46,7 @@ import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.AnnotationValue; import javax.lang.model.element.Element; +import javax.lang.model.element.Modifier; import javax.lang.model.element.PackageElement; import javax.lang.model.element.TypeElement; import javax.lang.model.type.DeclaredType; @@ -233,13 +234,18 @@ private boolean checkImplementer( return true; } - // TODO: We're currently only enforcing the subtype relationship - // constraint. It would be nice to enforce them all. + // We check that providerImplementer does indeed inherit from providerType, and that it is not + // abstract (an abstract class or interface). For ServiceLoader, we could also check that it has + // a public no-arg constructor. But it turns out that people also use AutoService in contexts + // where the META-INF/services entries are read by things other than ServiceLoader. Those things + // still require the class to exist and inherit from providerType, but they don't necessarily + // require a public no-arg constructor. + // More background: https://github.com/google/auto/issues/1505. Types types = processingEnv.getTypeUtils(); if (types.isSubtype(providerImplementer.asType(), providerType.asType())) { - return true; + return checkNotAbstract(providerImplementer, annotationMirror); } // Maybe the provider has generic type, but the argument to @AutoService can't be generic. @@ -255,12 +261,31 @@ private boolean checkImplementer( providerImplementer, annotationMirror); } - return true; + return checkNotAbstract(providerImplementer, annotationMirror); } + String message = + "ServiceProviders must implement their service provider interface. " + + providerImplementer.getQualifiedName() + + " does not implement " + + providerType.getQualifiedName(); + error(message, providerImplementer, annotationMirror); + return false; } + private boolean checkNotAbstract( + TypeElement providerImplementer, AnnotationMirror annotationMirror) { + if (providerImplementer.getModifiers().contains(Modifier.ABSTRACT)) { + error( + "@AutoService cannot be applied to an abstract class or an interface", + providerImplementer, + annotationMirror); + return false; + } + return true; + } + private static boolean suppresses(Element element, String warning) { for (; element != null; element = element.getEnclosingElement()) { SuppressWarnings suppress = element.getAnnotation(SuppressWarnings.class); diff --git a/service/processor/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java b/service/processor/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java index 560aa99570..7f2bf0264e 100644 --- a/service/processor/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java +++ b/service/processor/src/test/java/com/google/auto/service/processor/AutoServiceProcessorTest.java @@ -23,6 +23,7 @@ import com.google.testing.compile.Compilation; import com.google.testing.compile.Compiler; import com.google.testing.compile.JavaFileObjects; +import javax.tools.JavaFileObject; import javax.tools.StandardLocation; import org.junit.Test; import org.junit.runner.RunWith; @@ -218,4 +219,38 @@ public void missing() { assertThat(compilation).failed(); assertThat(processor.exceptionStacks()).isEmpty(); } + + @Test + public void autoServiceOnInterface() { + AutoServiceProcessor processor = new AutoServiceProcessor(); + JavaFileObject autoServiceOnInterface = + JavaFileObjects.forResource("test/AutoServiceOnInterface.java"); + Compilation compilation = + Compiler.javac() + .withProcessors(processor) + .withOptions("-Averify=true") + .compile(autoServiceOnInterface); + assertThat(compilation) + .hadErrorContaining("@AutoService cannot be applied to an abstract class or an interface") + .inFile(autoServiceOnInterface) + .onLineContaining("@AutoService"); + assertThat(processor.exceptionStacks()).isEmpty(); + } + + @Test + public void autoServiceOnAbstractClass() { + AutoServiceProcessor processor = new AutoServiceProcessor(); + JavaFileObject autoServiceOnAbstractClass = + JavaFileObjects.forResource("test/AutoServiceOnAbstractClass.java"); + Compilation compilation = + Compiler.javac() + .withProcessors(processor) + .withOptions("-Averify=true") + .compile(JavaFileObjects.forResource("test/AutoServiceOnAbstractClass.java")); + assertThat(compilation) + .hadErrorContaining("@AutoService cannot be applied to an abstract class or an interface") + .inFile(autoServiceOnAbstractClass) + .onLineContaining("@AutoService"); + assertThat(processor.exceptionStacks()).isEmpty(); + } } diff --git a/service/processor/src/test/resources/test/AutoServiceOnAbstractClass.java b/service/processor/src/test/resources/test/AutoServiceOnAbstractClass.java new file mode 100644 index 0000000000..1669ca29ea --- /dev/null +++ b/service/processor/src/test/resources/test/AutoServiceOnAbstractClass.java @@ -0,0 +1,21 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package test; + +import com.google.auto.service.AutoService; + +@AutoService(SomeService.class) +public abstract class AutoServiceOnAbstractClass implements SomeService {} diff --git a/service/processor/src/test/resources/test/AutoServiceOnInterface.java b/service/processor/src/test/resources/test/AutoServiceOnInterface.java new file mode 100644 index 0000000000..84fcf12613 --- /dev/null +++ b/service/processor/src/test/resources/test/AutoServiceOnInterface.java @@ -0,0 +1,21 @@ +/* + * Copyright 2023 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package test; + +import com.google.auto.service.AutoService; + +@AutoService(SomeService.class) +public interface AutoServiceOnInterface extends SomeService {}