Skip to content

Commit 7fdeece

Browse files
authored
feat(ast): Add support for Throwable causes [ggj] (#801)
* feat(ast): Add support for throwable causes * fix: make Throwable a static final in TypeNode
1 parent e654bfb commit 7fdeece

File tree

7 files changed

+167
-0
lines changed

7 files changed

+167
-0
lines changed

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

+17
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,9 @@ public abstract class ThrowExpr implements Expr {
2828
@Nullable
2929
public abstract Expr messageExpr();
3030

31+
@Nullable
32+
public abstract Expr causeExpr();
33+
3134
@Override
3235
public void accept(AstNodeVisitor visitor) {
3336
visitor.visit(this);
@@ -47,22 +50,36 @@ public Builder setMessageExpr(String message) {
4750

4851
public abstract Builder setMessageExpr(Expr expr);
4952

53+
public abstract Builder setCauseExpr(Expr expr);
54+
5055
// Private.
5156
abstract TypeNode type();
5257

5358
abstract Expr messageExpr();
5459

60+
abstract Expr causeExpr();
61+
5562
abstract ThrowExpr autoBuild();
5663

5764
public ThrowExpr build() {
5865
Preconditions.checkState(
5966
TypeNode.isExceptionType(type()),
6067
String.format("Type %s must be an exception type", type()));
68+
6169
if (messageExpr() != null) {
6270
Preconditions.checkState(
6371
messageExpr().type().equals(TypeNode.STRING),
6472
String.format("Message expression type must be a string for exception %s", type()));
6573
}
74+
75+
if (causeExpr() != null) {
76+
Preconditions.checkState(
77+
TypeNode.THROWABLE.reference().isSupertypeOrEquals(causeExpr().type().reference()),
78+
String.format(
79+
"Cause expression type must be a subclass of Throwable, but found %s",
80+
causeExpr().type()));
81+
}
82+
6683
return autoBuild();
6784
}
6885
}

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

+3
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ public enum TypeKind {
8181
public static final TypeNode STRING = withReference(ConcreteReference.withClazz(String.class));
8282
public static final TypeNode VOID_OBJECT = withReference(ConcreteReference.withClazz(Void.class));
8383

84+
public static final TypeNode THROWABLE =
85+
withReference(ConcreteReference.withClazz(Throwable.class));
86+
8487
public static final TypeNode DEPRECATED =
8588
withReference(ConcreteReference.withClazz(Deprecated.class));
8689

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

+3
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ public void visit(ThrowExpr throwExpr) {
234234
if (throwExpr.messageExpr() != null) {
235235
throwExpr.messageExpr().accept(this);
236236
}
237+
if (throwExpr.causeExpr() != null) {
238+
throwExpr.causeExpr().accept(this);
239+
}
237240
}
238241

239242
@Override

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

+7
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,13 @@ public void visit(ThrowExpr throwExpr) {
399399
if (throwExpr.messageExpr() != null) {
400400
throwExpr.messageExpr().accept(this);
401401
}
402+
if (throwExpr.causeExpr() != null) {
403+
if (throwExpr.messageExpr() != null) {
404+
buffer.append(COMMA);
405+
space();
406+
}
407+
throwExpr.causeExpr().accept(this);
408+
}
402409
rightParen();
403410
}
404411

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

+61
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,65 @@ public void createThrowExpr_badMessageExpr() {
6262
IllegalStateException.class,
6363
() -> ThrowExpr.builder().setType(npeType).setMessageExpr(messageExpr).build());
6464
}
65+
66+
@Test
67+
public void createThrowExpr_causeExpr() {
68+
TypeNode npeType =
69+
TypeNode.withReference(ConcreteReference.withClazz(NullPointerException.class));
70+
ThrowExpr.builder()
71+
.setType(npeType)
72+
.setCauseExpr(
73+
NewObjectExpr.builder()
74+
.setType(TypeNode.withReference(ConcreteReference.withClazz(Throwable.class)))
75+
.build())
76+
.build();
77+
// Successfully created a ThrowExpr.
78+
}
79+
80+
@Test
81+
public void createThrowExpr_causeExpr_throwableSubtype() {
82+
TypeNode npeType =
83+
TypeNode.withReference(ConcreteReference.withClazz(NullPointerException.class));
84+
ThrowExpr.builder()
85+
.setType(npeType)
86+
.setCauseExpr(
87+
NewObjectExpr.builder()
88+
.setType(TypeNode.withExceptionClazz(IllegalStateException.class))
89+
.build())
90+
.build();
91+
// Successfully created a ThrowExpr.
92+
}
93+
94+
@Test
95+
public void createThrowExpr_causeExpr_onThrowableSubtype() {
96+
TypeNode npeType =
97+
TypeNode.withReference(ConcreteReference.withClazz(NullPointerException.class));
98+
assertThrows(
99+
IllegalStateException.class,
100+
() ->
101+
ThrowExpr.builder()
102+
.setType(npeType)
103+
.setCauseExpr(NewObjectExpr.builder().setType(TypeNode.STRING).build())
104+
.build());
105+
}
106+
107+
@Test
108+
public void createThrowExpr_messageAndCauseExpr() {
109+
TypeNode npeType =
110+
TypeNode.withReference(ConcreteReference.withClazz(NullPointerException.class));
111+
Expr messageExpr =
112+
MethodInvocationExpr.builder()
113+
.setMethodName("foobar")
114+
.setReturnType(TypeNode.STRING)
115+
.build();
116+
ThrowExpr.builder()
117+
.setType(npeType)
118+
.setMessageExpr(messageExpr)
119+
.setCauseExpr(
120+
NewObjectExpr.builder()
121+
.setType(TypeNode.withReference(ConcreteReference.withClazz(Throwable.class)))
122+
.build())
123+
.build();
124+
// Successfully created a ThrowExpr.
125+
}
65126
}

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

+37
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
import com.google.common.base.Function;
5959
import com.google.common.base.Strings;
6060
import java.io.File;
61+
import java.io.FileNotFoundException;
6162
import java.io.FileOutputStream;
6263
import java.io.IOException;
6364
import java.util.ArrayList;
@@ -475,6 +476,42 @@ public void writeThrowExprImports_messageExpr() {
475476
writerVisitor.write());
476477
}
477478

479+
@Test
480+
public void writeThrowExprImports_messageAndCauseExpr() {
481+
TypeNode npeType = TypeNode.withExceptionClazz(NullPointerException.class);
482+
Expr messageExpr =
483+
MethodInvocationExpr.builder()
484+
.setStaticReferenceType(
485+
TypeNode.withReference(ConcreteReference.withClazz(IfStatement.class)))
486+
.setMethodName("conditionExpr")
487+
.setReturnType(TypeNode.withReference(ConcreteReference.withClazz(Expr.class)))
488+
.build();
489+
490+
messageExpr =
491+
MethodInvocationExpr.builder()
492+
.setExprReferenceExpr(messageExpr)
493+
.setMethodName("foobar")
494+
.setReturnType(TypeNode.STRING)
495+
.build();
496+
ThrowExpr throwExpr =
497+
ThrowExpr.builder()
498+
.setType(npeType)
499+
.setMessageExpr(messageExpr)
500+
.setCauseExpr(
501+
NewObjectExpr.builder()
502+
.setType(TypeNode.withExceptionClazz(FileNotFoundException.class))
503+
.build())
504+
.build();
505+
506+
throwExpr.accept(writerVisitor);
507+
assertEquals(
508+
LineFormatter.lines(
509+
"import com.google.api.generator.engine.ast.Expr;\n",
510+
"import com.google.api.generator.engine.ast.IfStatement;\n",
511+
"import java.io.FileNotFoundException;\n\n"),
512+
writerVisitor.write());
513+
}
514+
478515
@Test
479516
public void writeInstanceofExprImports_basic() {
480517
TypeNode exprType = TypeNode.withReference(ConcreteReference.withClazz(Expr.class));

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

+39
Original file line numberDiff line numberDiff line change
@@ -1041,6 +1041,22 @@ public void writeThrowExpr_basicWithMessage() {
10411041
assertEquals("throw new NullPointerException(\"Some message asdf\")", writerVisitor.write());
10421042
}
10431043

1044+
@Test
1045+
public void writeThrowExpr_basicWithCause() {
1046+
TypeNode npeType =
1047+
TypeNode.withReference(ConcreteReference.withClazz(NullPointerException.class));
1048+
ThrowExpr throwExpr =
1049+
ThrowExpr.builder()
1050+
.setType(npeType)
1051+
.setCauseExpr(
1052+
NewObjectExpr.builder()
1053+
.setType(TypeNode.withReference(ConcreteReference.withClazz(Throwable.class)))
1054+
.build())
1055+
.build();
1056+
throwExpr.accept(writerVisitor);
1057+
assertEquals("throw new NullPointerException(new Throwable())", writerVisitor.write());
1058+
}
1059+
10441060
@Test
10451061
public void writeThrowExpr_messageExpr() {
10461062
TypeNode npeType = TypeNode.withExceptionClazz(NullPointerException.class);
@@ -1055,6 +1071,29 @@ public void writeThrowExpr_messageExpr() {
10551071
assertEquals("throw new NullPointerException(foobar())", writerVisitor.write());
10561072
}
10571073

1074+
@Test
1075+
public void writeThrowExpr_messageAndCauseExpr() {
1076+
TypeNode npeType = TypeNode.withExceptionClazz(NullPointerException.class);
1077+
Expr messageExpr =
1078+
MethodInvocationExpr.builder()
1079+
.setMethodName("foobar")
1080+
.setReturnType(TypeNode.STRING)
1081+
.build();
1082+
ThrowExpr throwExpr =
1083+
ThrowExpr.builder()
1084+
.setType(npeType)
1085+
.setMessageExpr(messageExpr)
1086+
.setCauseExpr(
1087+
NewObjectExpr.builder()
1088+
.setType(TypeNode.withReference(ConcreteReference.withClazz(Throwable.class)))
1089+
.build())
1090+
.build();
1091+
1092+
throwExpr.accept(writerVisitor);
1093+
assertEquals(
1094+
"throw new NullPointerException(foobar(), new Throwable())", writerVisitor.write());
1095+
}
1096+
10581097
@Test
10591098
public void writeInstanceofExpr() {
10601099
Variable variable = Variable.builder().setName("x").setType(TypeNode.STRING).build();

0 commit comments

Comments
 (0)