Skip to content

Commit 0817650

Browse files
authored
feat(ast): support throwing all kinds of expressions (#810)
1 parent 650850f commit 0817650

File tree

6 files changed

+164
-0
lines changed

6 files changed

+164
-0
lines changed

src/main/java/com/google/api/generator/engine/ast/ThrowExpr.java

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@
2222
public abstract class ThrowExpr implements Expr {
2323
// TODO(miraleung): Refactor with StringObjectValue and possibly with NewObjectExpr.
2424

25+
@Nullable
26+
public abstract Expr throwExpr();
27+
2528
@Override
2629
public abstract TypeNode type();
2730

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

4346
@AutoValue.Builder
4447
public abstract static class Builder {
48+
public abstract Builder setThrowExpr(Expr throwExpr);
49+
50+
// No-op if setThrowExpr is called.
4551
public abstract Builder setType(TypeNode type);
4652

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

5561
// Private.
62+
abstract Expr throwExpr();
63+
5664
abstract TypeNode type();
5765

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

6472
public ThrowExpr build() {
73+
if (throwExpr() != null) {
74+
setType(throwExpr().type());
75+
Preconditions.checkState(
76+
messageExpr() == null && causeExpr() == null,
77+
"Only one of throwExpr or [messageExpr or causeExpr, inclusive] can be present.");
78+
79+
if (throwExpr() instanceof VariableExpr) {
80+
Preconditions.checkState(
81+
!((VariableExpr) throwExpr()).isDecl(), "Cannot throw a variable declaration");
82+
}
83+
84+
Preconditions.checkState(
85+
TypeNode.isExceptionType(throwExpr().type()),
86+
String.format("Only exception types can be thrown, found %s", throwExpr().type()));
87+
88+
return autoBuild();
89+
}
90+
6591
Preconditions.checkState(
6692
TypeNode.isExceptionType(type()),
6793
String.format("Type %s must be an exception type", type()));

src/main/java/com/google/api/generator/engine/writer/ImportWriterVisitor.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,13 @@ public void visit(AnonymousClassExpr anonymousClassExpr) {
231231
@Override
232232
public void visit(ThrowExpr throwExpr) {
233233
throwExpr.type().accept(this);
234+
// If throwExpr is present, then messageExpr and causeExpr will not be present. Relies on AST
235+
// build-time checks.
236+
if (throwExpr.throwExpr() != null) {
237+
throwExpr.throwExpr().accept(this);
238+
return;
239+
}
240+
234241
if (throwExpr.messageExpr() != null) {
235242
throwExpr.messageExpr().accept(this);
236243
}

src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -392,6 +392,13 @@ public void visit(AnonymousClassExpr anonymousClassExpr) {
392392
public void visit(ThrowExpr throwExpr) {
393393
buffer.append(THROW);
394394
space();
395+
// If throwExpr is present, then messageExpr and causeExpr will not be present. Relies on AST
396+
// build-time checks.
397+
if (throwExpr.throwExpr() != null) {
398+
throwExpr.throwExpr().accept(this);
399+
return;
400+
}
401+
395402
buffer.append(NEW);
396403
space();
397404
throwExpr.type().accept(this);

src/test/java/com/google/api/generator/engine/ast/ThrowExprTest.java

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

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

17+
import static junit.framework.Assert.assertEquals;
1718
import static org.junit.Assert.assertThrows;
1819

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

30+
@Test
31+
public void createThrowExpr_basicExpr() {
32+
TypeNode npeType = TypeNode.withExceptionClazz(NullPointerException.class);
33+
VariableExpr throwVarExpr =
34+
VariableExpr.builder()
35+
.setVariable(
36+
Variable.builder()
37+
.setName("e")
38+
.setType(TypeNode.withExceptionClazz(RuntimeException.class))
39+
.build())
40+
.build();
41+
ThrowExpr throwExpr = ThrowExpr.builder().setThrowExpr(throwVarExpr).build();
42+
assertEquals(throwVarExpr.variable().type(), throwExpr.type());
43+
// Setting the type doesn't matter.
44+
throwExpr = ThrowExpr.builder().setThrowExpr(throwVarExpr).setType(npeType).build();
45+
assertEquals(throwVarExpr.variable().type(), throwExpr.type());
2846
}
2947

3048
@Test
@@ -123,4 +141,75 @@ public void createThrowExpr_messageAndCauseExpr() {
123141
.build();
124142
// Successfully created a ThrowExpr.
125143
}
144+
145+
@Test
146+
public void createThrowExpr_cannotThrowVariableDeclaration() {
147+
VariableExpr throwVarExpr =
148+
VariableExpr.builder()
149+
.setVariable(
150+
Variable.builder()
151+
.setName("e")
152+
.setType(TypeNode.withExceptionClazz(RuntimeException.class))
153+
.build())
154+
.build();
155+
assertThrows(
156+
IllegalStateException.class,
157+
() ->
158+
ThrowExpr.builder()
159+
.setThrowExpr(throwVarExpr.toBuilder().setIsDecl(true).build())
160+
.build());
161+
}
162+
163+
@Test
164+
public void createThrowExpr_cannotThrowNonExceptionTypedExpr() {
165+
VariableExpr throwVarExpr =
166+
VariableExpr.builder()
167+
.setVariable(Variable.builder().setName("str").setType(TypeNode.STRING).build())
168+
.build();
169+
assertThrows(
170+
IllegalStateException.class, () -> ThrowExpr.builder().setThrowExpr(throwVarExpr).build());
171+
}
172+
173+
@Test
174+
public void createThrowExpr_cannotHaveThrowVariableAndMessageExprPresent() {
175+
Expr messageExpr =
176+
MethodInvocationExpr.builder()
177+
.setMethodName("foobar")
178+
.setReturnType(TypeNode.STRING)
179+
.build();
180+
VariableExpr throwVarExpr =
181+
VariableExpr.builder()
182+
.setVariable(
183+
Variable.builder()
184+
.setName("e")
185+
.setType(TypeNode.withExceptionClazz(RuntimeException.class))
186+
.build())
187+
.build();
188+
assertThrows(
189+
IllegalStateException.class,
190+
() -> ThrowExpr.builder().setThrowExpr(throwVarExpr).setMessageExpr(messageExpr).build());
191+
}
192+
193+
@Test
194+
public void createThrowExpr_cannotHaveThrowVariableAndCauseExprPresent() {
195+
VariableExpr throwVarExpr =
196+
VariableExpr.builder()
197+
.setVariable(
198+
Variable.builder()
199+
.setName("e")
200+
.setType(TypeNode.withExceptionClazz(RuntimeException.class))
201+
.build())
202+
.build();
203+
assertThrows(
204+
IllegalStateException.class,
205+
() ->
206+
ThrowExpr.builder()
207+
.setThrowExpr(throwVarExpr)
208+
.setCauseExpr(
209+
NewObjectExpr.builder()
210+
.setType(
211+
TypeNode.withReference(ConcreteReference.withClazz(Throwable.class)))
212+
.build())
213+
.build());
214+
}
126215
}

src/test/java/com/google/api/generator/engine/writer/ImportWriterVisitorTest.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,26 @@ public void writeThrowExprImports_basic() {
449449
assertEquals("import java.io.IOException;\n\n", writerVisitor.write());
450450
}
451451

452+
@Test
453+
public void writeThrowExprImports_throwExpr() {
454+
Expr exprToThrow =
455+
MethodInvocationExpr.builder()
456+
.setStaticReferenceType(
457+
TypeNode.withReference(ConcreteReference.withClazz(Statement.class)))
458+
.setMethodName("createException")
459+
.setReturnType(TypeNode.withExceptionClazz(Exception.class))
460+
.build();
461+
462+
TypeNode ignoredExceptionType =
463+
TypeNode.withReference(ConcreteReference.withClazz(IOException.class));
464+
ThrowExpr throwExpr =
465+
ThrowExpr.builder().setType(ignoredExceptionType).setThrowExpr(exprToThrow).build();
466+
throwExpr.accept(writerVisitor);
467+
assertEquals(
468+
LineFormatter.lines("import com.google.api.generator.engine.ast.Statement;\n\n"),
469+
writerVisitor.write());
470+
}
471+
452472
@Test
453473
public void writeThrowExprImports_messageExpr() {
454474
TypeNode npeType = TypeNode.withExceptionClazz(NullPointerException.class);

src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1031,6 +1031,21 @@ public void writeThrowExpr_basic() {
10311031
assertEquals("throw new NullPointerException()", writerVisitor.write());
10321032
}
10331033

1034+
@Test
1035+
public void writeThrowExpr_basicThrowExpr() {
1036+
Expr exprToThrow =
1037+
MethodInvocationExpr.builder()
1038+
.setStaticReferenceType(
1039+
TypeNode.withReference(ConcreteReference.withClazz(Statement.class)))
1040+
.setMethodName("createException")
1041+
.setReturnType(TypeNode.withExceptionClazz(Exception.class))
1042+
.build();
1043+
1044+
ThrowExpr throwExpr = ThrowExpr.builder().setThrowExpr(exprToThrow).build();
1045+
throwExpr.accept(writerVisitor);
1046+
assertEquals("throw Statement.createException()", writerVisitor.write());
1047+
}
1048+
10341049
@Test
10351050
public void writeThrowExpr_basicWithMessage() {
10361051
TypeNode npeType =

0 commit comments

Comments
 (0)