Skip to content

Commit

Permalink
Handle @AutoFactory constructors that throw checked exceptions.
Browse files Browse the repository at this point in the history
The generated `create` method needs to declare the same exceptions.

Fixes #90.

RELNOTES=`@AutoFactory` constructors can now declare checked exceptions. The same exceptions will be declared on the generated `create` method.
PiperOrigin-RevId: 347657308
  • Loading branch information
eamonnmcmanus authored and Google Java Core Libraries committed Dec 15, 2020
1 parent 9cc29ca commit 3141e79
Show file tree
Hide file tree
Showing 13 changed files with 262 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ private ImmutableSet<ImplementationMethodDescriptor> implementationMethods(
.publicMethod()
.passedParameters(passedParameters)
.isVarArgs(implementationMethod.isVarArgs())
.exceptions(implementationMethod.getThrownTypes())
.build());
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -194,12 +194,12 @@ private static ImmutableSet<FactoryMethodDescriptor> getDeduplicatedMethodDescri
duplicateMethodDescriptors.get(methodDescriptor);

FactoryMethodDescriptor newMethodDescriptor =
(duplicateMethodDescriptor != null)
? methodDescriptor
.toBuilder()
(duplicateMethodDescriptor != null)
? methodDescriptor.toBuilder()
.overridingMethod(true)
.publicMethod(duplicateMethodDescriptor.publicMethod())
.returnType(duplicateMethodDescriptor.returnType())
.exceptions(duplicateMethodDescriptor.exceptions())
.build()
: methodDescriptor;
deduplicatedMethodDescriptors.add(newMethodDescriptor);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,8 @@ FactoryMethodDescriptor generateDescriptorForConstructor(final AutoFactoryDeclar
.passedParameters(passedParameters)
.creationParameters(Parameter.forParameterList(constructor.getParameters(), types))
.isVarArgs(constructor.isVarArgs())
.exceptions(constructor.getThrownTypes())
.overridingMethod(false)
.build();
}

Expand All @@ -144,9 +146,12 @@ private ImmutableSet<FactoryMethodDescriptor> generateDescriptorForDefaultConstr
.name("create")
.returnType(type.asType())
.publicMethod(type.getModifiers().contains(PUBLIC))
.providedParameters(ImmutableSet.of())
.passedParameters(ImmutableSet.of())
.creationParameters(ImmutableSet.of())
.providedParameters(ImmutableSet.of())
.isVarArgs(false)
.exceptions(ImmutableSet.of())
.overridingMethod(false)
.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,17 @@ abstract class FactoryMethodDescriptor {
abstract ImmutableSet<Parameter> passedParameters();
abstract ImmutableSet<Parameter> providedParameters();
abstract ImmutableSet<Parameter> creationParameters();
abstract Builder toBuilder();
abstract boolean isVarArgs();
abstract ImmutableSet<TypeMirror> exceptions();
abstract Builder toBuilder();

final PackageAndClass factoryName() {
return declaration().getFactoryName();
}

static Builder builder(AutoFactoryDeclaration declaration) {
return new AutoValue_FactoryMethodDescriptor.Builder()
.declaration(checkNotNull(declaration))
.publicMethod(false)
.overridingMethod(false)
.isVarArgs(false);
.declaration(checkNotNull(declaration));
}

@AutoValue.Builder
Expand All @@ -64,6 +62,7 @@ abstract static class Builder {
abstract Builder providedParameters(Iterable<Parameter> providedParameters);
abstract Builder creationParameters(Iterable<Parameter> creationParameters);
abstract Builder isVarArgs(boolean isVarargs);
abstract Builder exceptions(Iterable<? extends TypeMirror> exceptions);
abstract FactoryMethodDescriptor buildImpl();

FactoryMethodDescriptor build() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import static com.squareup.javapoet.MethodSpec.methodBuilder;
import static com.squareup.javapoet.TypeSpec.classBuilder;
import static java.util.stream.Collectors.joining;
import static java.util.stream.Collectors.toList;
import static javax.lang.model.element.Modifier.FINAL;
import static javax.lang.model.element.Modifier.PRIVATE;
import static javax.lang.model.element.Modifier.PUBLIC;
Expand Down Expand Up @@ -152,6 +153,8 @@ private void addFactoryMethods(
if (methodDescriptor.publicMethod()) {
method.addModifiers(PUBLIC);
}
method.addExceptions(
methodDescriptor.exceptions().stream().map(TypeName::get).collect(toList()));
CodeBlock.Builder args = CodeBlock.builder();
method.addParameters(parameters(methodDescriptor.passedParameters()));
Iterator<Parameter> parameters = methodDescriptor.creationParameters().iterator();
Expand Down Expand Up @@ -199,6 +202,8 @@ private void addImplementationMethods(
if (methodDescriptor.publicMethod()) {
implementationMethod.addModifiers(PUBLIC);
}
implementationMethod.addExceptions(
methodDescriptor.exceptions().stream().map(TypeName::get).collect(toList()));
implementationMethod.addParameters(parameters(methodDescriptor.passedParameters()));
implementationMethod.addStatement(
"return create($L)",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,10 @@ abstract class ImplementationMethodDescriptor {
abstract boolean publicMethod();
abstract ImmutableSet<Parameter> passedParameters();
abstract boolean isVarArgs();
abstract ImmutableSet<TypeMirror> exceptions();

static Builder builder() {
return new AutoValue_ImplementationMethodDescriptor.Builder()
.publicMethod(true)
.isVarArgs(false);
return new AutoValue_ImplementationMethodDescriptor.Builder();
}

@AutoValue.Builder
Expand All @@ -49,6 +48,8 @@ final Builder publicMethod() {

abstract Builder isVarArgs(boolean isVarargs);

abstract Builder exceptions(Iterable<? extends TypeMirror> exceptions);

abstract ImplementationMethodDescriptor build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ public void simpleClass() {
.hasSourceEquivalentTo(loadExpectedFile("expected/SimpleClassFactory.java"));
}

@Test
public void simpleClassWithConstructorThrowsClause() {
Compilation compilation =
javac.compile(JavaFileObjects.forResource("good/SimpleClassThrows.java"));
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
.generatedSourceFile("tests.SimpleClassThrowsFactory")
.hasSourceEquivalentTo(loadExpectedFile("expected/SimpleClassThrowsFactory.java"));
}

@Test
public void nestedClasses() {
Compilation compilation = javac.compile(JavaFileObjects.forResource("good/NestedClasses.java"));
Expand Down Expand Up @@ -149,6 +159,15 @@ public void constructorAnnotated() {
.hasSourceEquivalentTo(loadExpectedFile("expected/ConstructorAnnotatedFactory.java"));
}

@Test
public void constructorWithThrowsClauseAnnotated() {
Compilation compilation =
javac.compile(JavaFileObjects.forResource("good/ConstructorAnnotatedThrows.java"));
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
.generatedSourceFile("tests.ConstructorAnnotatedThrowsFactory")
.hasSourceEquivalentTo(loadExpectedFile("expected/ConstructorAnnotatedThrowsFactory.java"));
}
@Test
public void constructorAnnotatedNonFinal() {
Compilation compilation =
Expand Down Expand Up @@ -256,6 +275,17 @@ public void factoryExtendingAbstractClass() {
loadExpectedFile("expected/FactoryExtendingAbstractClassFactory.java"));
}

@Test
public void factoryWithConstructorThrowsClauseExtendingAbstractClass() {
Compilation compilation =
javac.compile(JavaFileObjects.forResource("good/FactoryExtendingAbstractClassThrows.java"));
assertThat(compilation).succeededWithoutWarnings();
assertThat(compilation)
.generatedSourceFile("tests.FactoryExtendingAbstractClassThrowsFactory")
.hasSourceEquivalentTo(
loadExpectedFile("expected/FactoryExtendingAbstractClassThrowsFactory.java"));
}

@Test
public void factoryExtendingAbstractClass_withConstructorParams() {
JavaFileObject file =
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright 2020 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 tests;

import java.io.IOException;
import javax.annotation.processing.Generated;
import javax.inject.Inject;
import javax.inject.Provider;

@Generated(
value = "com.google.auto.factory.processor.AutoFactoryProcessor",
comments = "https://github.com/google/auto/tree/master/factory"
)
final class ConstructorAnnotatedThrowsFactory {
private final Provider<Object> objProvider;

@Inject ConstructorAnnotatedThrowsFactory(Provider<Object> objProvider) {
this.objProvider = checkNotNull(objProvider, 1);
}

ConstructorAnnotatedThrows create() throws IOException, InterruptedException {
return new ConstructorAnnotatedThrows();
}

ConstructorAnnotatedThrows create(String s) {
return new ConstructorAnnotatedThrows(checkNotNull(s, 1));
}

ConstructorAnnotatedThrows create(int i) throws IOException {
return new ConstructorAnnotatedThrows(checkNotNull(objProvider.get(), 1), i);
}

ConstructorAnnotatedThrows create(char c) throws InterruptedException {
return new ConstructorAnnotatedThrows(checkNotNull(objProvider.get(), 1), c);
}

private static <T> T checkNotNull(T reference, int argumentIndex) {
if (reference == null) {
throw new NullPointerException(
"@AutoFactory method argument is null but is not marked @Nullable. Argument index: "
+ argumentIndex);
}
return reference;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
* Copyright 2020 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 tests;

import java.io.IOException;
import javax.annotation.processing.Generated;
import javax.inject.Inject;

@Generated(
value = "com.google.auto.factory.processor.AutoFactoryProcessor",
comments = "https://github.com/google/auto/tree/master/factory"
)
final class FactoryExtendingAbstractClassThrowsFactory
extends FactoryExtendingAbstractClassThrows.AbstractFactory {
@Inject FactoryExtendingAbstractClassThrowsFactory() {}

FactoryExtendingAbstractClassThrows create() throws IOException, InterruptedException {
return new FactoryExtendingAbstractClassThrows();
}

@Override public FactoryExtendingAbstractClassThrows newInstance() throws Exception {
return create();
}
}
32 changes: 32 additions & 0 deletions factory/src/test/resources/expected/SimpleClassThrowsFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Copyright 2020 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 tests;

import java.io.IOException;
import javax.annotation.processing.Generated;
import javax.inject.Inject;

@Generated(
value = "com.google.auto.factory.processor.AutoFactoryProcessor",
comments = "https://github.com/google/auto/tree/master/factory"
)
final class SimpleClassThrowsFactory {
@Inject SimpleClassThrowsFactory() {}

SimpleClassThrows create() throws IOException, InterruptedException {
return new SimpleClassThrows();
}
}
29 changes: 29 additions & 0 deletions factory/src/test/resources/good/ConstructorAnnotatedThrows.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2020 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 tests;

import com.google.auto.factory.AutoFactory;
import com.google.auto.factory.Provided;
import java.io.IOException;

final class ConstructorAnnotatedThrows {
@AutoFactory ConstructorAnnotatedThrows() throws IOException, InterruptedException {}
ConstructorAnnotatedThrows(Object obj) {}
@AutoFactory ConstructorAnnotatedThrows(String s) {}
@AutoFactory ConstructorAnnotatedThrows(@Provided Object obj, int i) throws IOException {}
@AutoFactory ConstructorAnnotatedThrows(@Provided Object obj, char c)
throws InterruptedException {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
/*
* Copyright 2020 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 tests;

import com.google.auto.factory.AutoFactory;
import java.io.IOException;
import tests.FactoryExtendingAbstractClassThrows.AbstractFactory;

@AutoFactory(extending = AbstractFactory.class)
final class FactoryExtendingAbstractClassThrows {
FactoryExtendingAbstractClassThrows() throws IOException, InterruptedException {}

static abstract class AbstractFactory {
abstract FactoryExtendingAbstractClassThrows newInstance() throws Exception;
}
}
24 changes: 24 additions & 0 deletions factory/src/test/resources/good/SimpleClassThrows.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright 2020 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 tests;

import com.google.auto.factory.AutoFactory;
import java.io.IOException;

@AutoFactory
final class SimpleClassThrows {
SimpleClassThrows() throws IOException, InterruptedException {}
}

0 comments on commit 3141e79

Please sign in to comment.