Skip to content

Commit

Permalink
feat(ast): support throwing all kinds of expressions (#810)
Browse files Browse the repository at this point in the history
  • Loading branch information
miraleung committed Aug 2, 2021
1 parent 650850f commit 0817650
Show file tree
Hide file tree
Showing 6 changed files with 164 additions and 0 deletions.
26 changes: 26 additions & 0 deletions src/main/java/com/google/api/generator/engine/ast/ThrowExpr.java
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
public abstract class ThrowExpr implements Expr {
// TODO(miraleung): Refactor with StringObjectValue and possibly with NewObjectExpr.

@Nullable
public abstract Expr throwExpr();

@Override
public abstract TypeNode type();

Expand All @@ -42,6 +45,9 @@ public static Builder builder() {

@AutoValue.Builder
public abstract static class Builder {
public abstract Builder setThrowExpr(Expr throwExpr);

// No-op if setThrowExpr is called.
public abstract Builder setType(TypeNode type);

public Builder setMessageExpr(String message) {
Expand All @@ -53,6 +59,8 @@ public Builder setMessageExpr(String message) {
public abstract Builder setCauseExpr(Expr expr);

// Private.
abstract Expr throwExpr();

abstract TypeNode type();

abstract Expr messageExpr();
Expand All @@ -62,6 +70,24 @@ public Builder setMessageExpr(String message) {
abstract ThrowExpr autoBuild();

public ThrowExpr build() {
if (throwExpr() != null) {
setType(throwExpr().type());
Preconditions.checkState(
messageExpr() == null && causeExpr() == null,
"Only one of throwExpr or [messageExpr or causeExpr, inclusive] can be present.");

if (throwExpr() instanceof VariableExpr) {
Preconditions.checkState(
!((VariableExpr) throwExpr()).isDecl(), "Cannot throw a variable declaration");
}

Preconditions.checkState(
TypeNode.isExceptionType(throwExpr().type()),
String.format("Only exception types can be thrown, found %s", throwExpr().type()));

return autoBuild();
}

Preconditions.checkState(
TypeNode.isExceptionType(type()),
String.format("Type %s must be an exception type", type()));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,13 @@ public void visit(AnonymousClassExpr anonymousClassExpr) {
@Override
public void visit(ThrowExpr throwExpr) {
throwExpr.type().accept(this);
// If throwExpr is present, then messageExpr and causeExpr will not be present. Relies on AST
// build-time checks.
if (throwExpr.throwExpr() != null) {
throwExpr.throwExpr().accept(this);
return;
}

if (throwExpr.messageExpr() != null) {
throwExpr.messageExpr().accept(this);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,13 @@ public void visit(AnonymousClassExpr anonymousClassExpr) {
public void visit(ThrowExpr throwExpr) {
buffer.append(THROW);
space();
// If throwExpr is present, then messageExpr and causeExpr will not be present. Relies on AST
// build-time checks.
if (throwExpr.throwExpr() != null) {
throwExpr.throwExpr().accept(this);
return;
}

buffer.append(NEW);
space();
throwExpr.type().accept(this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

package com.google.api.generator.engine.ast;

import static junit.framework.Assert.assertEquals;
import static org.junit.Assert.assertThrows;

import org.junit.Test;
Expand All @@ -24,7 +25,24 @@ public void createThrowExpr_basic() {
TypeNode npeType = TypeNode.withExceptionClazz(NullPointerException.class);
ThrowExpr.builder().setType(npeType).build();
// No exception thrown, we're good.
}

@Test
public void createThrowExpr_basicExpr() {
TypeNode npeType = TypeNode.withExceptionClazz(NullPointerException.class);
VariableExpr throwVarExpr =
VariableExpr.builder()
.setVariable(
Variable.builder()
.setName("e")
.setType(TypeNode.withExceptionClazz(RuntimeException.class))
.build())
.build();
ThrowExpr throwExpr = ThrowExpr.builder().setThrowExpr(throwVarExpr).build();
assertEquals(throwVarExpr.variable().type(), throwExpr.type());
// Setting the type doesn't matter.
throwExpr = ThrowExpr.builder().setThrowExpr(throwVarExpr).setType(npeType).build();
assertEquals(throwVarExpr.variable().type(), throwExpr.type());
}

@Test
Expand Down Expand Up @@ -123,4 +141,75 @@ public void createThrowExpr_messageAndCauseExpr() {
.build();
// Successfully created a ThrowExpr.
}

@Test
public void createThrowExpr_cannotThrowVariableDeclaration() {
VariableExpr throwVarExpr =
VariableExpr.builder()
.setVariable(
Variable.builder()
.setName("e")
.setType(TypeNode.withExceptionClazz(RuntimeException.class))
.build())
.build();
assertThrows(
IllegalStateException.class,
() ->
ThrowExpr.builder()
.setThrowExpr(throwVarExpr.toBuilder().setIsDecl(true).build())
.build());
}

@Test
public void createThrowExpr_cannotThrowNonExceptionTypedExpr() {
VariableExpr throwVarExpr =
VariableExpr.builder()
.setVariable(Variable.builder().setName("str").setType(TypeNode.STRING).build())
.build();
assertThrows(
IllegalStateException.class, () -> ThrowExpr.builder().setThrowExpr(throwVarExpr).build());
}

@Test
public void createThrowExpr_cannotHaveThrowVariableAndMessageExprPresent() {
Expr messageExpr =
MethodInvocationExpr.builder()
.setMethodName("foobar")
.setReturnType(TypeNode.STRING)
.build();
VariableExpr throwVarExpr =
VariableExpr.builder()
.setVariable(
Variable.builder()
.setName("e")
.setType(TypeNode.withExceptionClazz(RuntimeException.class))
.build())
.build();
assertThrows(
IllegalStateException.class,
() -> ThrowExpr.builder().setThrowExpr(throwVarExpr).setMessageExpr(messageExpr).build());
}

@Test
public void createThrowExpr_cannotHaveThrowVariableAndCauseExprPresent() {
VariableExpr throwVarExpr =
VariableExpr.builder()
.setVariable(
Variable.builder()
.setName("e")
.setType(TypeNode.withExceptionClazz(RuntimeException.class))
.build())
.build();
assertThrows(
IllegalStateException.class,
() ->
ThrowExpr.builder()
.setThrowExpr(throwVarExpr)
.setCauseExpr(
NewObjectExpr.builder()
.setType(
TypeNode.withReference(ConcreteReference.withClazz(Throwable.class)))
.build())
.build());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,26 @@ public void writeThrowExprImports_basic() {
assertEquals("import java.io.IOException;\n\n", writerVisitor.write());
}

@Test
public void writeThrowExprImports_throwExpr() {
Expr exprToThrow =
MethodInvocationExpr.builder()
.setStaticReferenceType(
TypeNode.withReference(ConcreteReference.withClazz(Statement.class)))
.setMethodName("createException")
.setReturnType(TypeNode.withExceptionClazz(Exception.class))
.build();

TypeNode ignoredExceptionType =
TypeNode.withReference(ConcreteReference.withClazz(IOException.class));
ThrowExpr throwExpr =
ThrowExpr.builder().setType(ignoredExceptionType).setThrowExpr(exprToThrow).build();
throwExpr.accept(writerVisitor);
assertEquals(
LineFormatter.lines("import com.google.api.generator.engine.ast.Statement;\n\n"),
writerVisitor.write());
}

@Test
public void writeThrowExprImports_messageExpr() {
TypeNode npeType = TypeNode.withExceptionClazz(NullPointerException.class);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1031,6 +1031,21 @@ public void writeThrowExpr_basic() {
assertEquals("throw new NullPointerException()", writerVisitor.write());
}

@Test
public void writeThrowExpr_basicThrowExpr() {
Expr exprToThrow =
MethodInvocationExpr.builder()
.setStaticReferenceType(
TypeNode.withReference(ConcreteReference.withClazz(Statement.class)))
.setMethodName("createException")
.setReturnType(TypeNode.withExceptionClazz(Exception.class))
.build();

ThrowExpr throwExpr = ThrowExpr.builder().setThrowExpr(exprToThrow).build();
throwExpr.accept(writerVisitor);
assertEquals("throw Statement.createException()", writerVisitor.write());
}

@Test
public void writeThrowExpr_basicWithMessage() {
TypeNode npeType =
Expand Down

0 comments on commit 0817650

Please sign in to comment.