Skip to content

Commit

Permalink
Reject an @AutoService type if it is an interface or abstract class.
Browse files Browse the repository at this point in the history
RELNOTES=The `@AutoService` annotation can no longer be applied to an interface or abstract class.
PiperOrigin-RevId: 532523629
  • Loading branch information
eamonnmcmanus authored and Google Java Core Libraries committed May 16, 2023
1 parent a5c82e1 commit 591731c
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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.
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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();
}
}
Original file line number Diff line number Diff line change
@@ -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 {}
Original file line number Diff line number Diff line change
@@ -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 {}

0 comments on commit 591731c

Please sign in to comment.