make "catch (e) { ... }" catch Exception | AssertionError
See original GitHub issueException
and AssertionError
are basically interchangeable and without distinction:
- A reasonable application may catch either
- Neither provides specific information that can be used for recovery
- The decision to use one or the other is often one of syntactical convenience or habit:
assert
always throwsAssertionError
while code usingthrow
often (but not always) throwsException
s.
Currently, catch (e)
catches Exception
s, but not AssertionError
s. Given the above, this behavior is of limited (perhaps no?) value and can even be surprising.
I propose that catch (e)
be redefined to catch Exception | AssertionError
.
While there are countless real world code examples to back the, uh…, assertion that Exception
and AssertionError
are not meaningfully different, the following contrived example shows how error handling code may be affected by the current behavior of catch
.
The program has two bugs:
- The user input must be a positive integer (we’re passing the value to
Iterable.step()
), but the input is not validated - Due to a language module limitation,
1
is not an acceptable input. Again, the input is not validated for this.
suppressWarnings("expressionTypeNothing")
shared void run() {
process.write("Input step size: ");
value step = Integer.parse(process.readLine() else "");
if (!is Integer step) {
print("Step must be an Integer");
}
else {
value span = runtime.minIntegerValue..runtime.maxIntegerValue;
try {
print(span.by(step).take(10));
}
catch (e) {
process.writeErrorLine(
"Oops, that didn't work. Please report this bug!
``e.message``");
process.exit(1);
}
}
}
With user input “2”, the program works:
Input step size: 2
{ -9223372036854775808, -9223372036854775806, -9223372036854775804, -9223372036854775802, -9223372036854775800, -9223372036854775798, -9223372036854775796, -9223372036854775794, -9223372036854775792, -9223372036854775790 }
With user input “1”, the error handling code (catch (e) { ... }
) is triggered (good):
Input step size: 1
Oops, that didn't work. Please report this bug!
offset from -9223372036854775808 to 9223372036854775807 cannot be represented as a 64 bit integer.
With user input “0”, the error handling code is not triggered, resulting in an ugly stack trace (bad):
Input step size: 0
Exception in thread "main" ceylon.language.AssertionError "Assertion failed: step size must be greater than zero
violated step > 0
left-hand expression is 0
right-hand expression is 0"
at ceylon.language.Span.by(Span.ceylon:156)
at simple.run_.run(run.ceylon:11)
at simple.run_.main(run.ceylon)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at ceylon.modules.api.runtime.SecurityActions.invokeRunInternal(SecurityActions.java:57)
at ceylon.modules.api.runtime.SecurityActions.invokeRun(SecurityActions.java:48)
at ceylon.modules.api.runtime.AbstractRuntime.invokeRun(AbstractRuntime.java:68)
at ceylon.modules.api.runtime.AbstractRuntime.execute(AbstractRuntime.java:105)
at ceylon.modules.api.runtime.AbstractRuntime.execute(AbstractRuntime.java:101)
at ceylon.modules.Main.execute(Main.java:69)
at ceylon.modules.Main.main(Main.java:42)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.jboss.modules.Module.run(Module.java:308)
at org.jboss.modules.Main.main(Main.java:487)
at ceylon.modules.bootstrap.CeylonRunTool.run(CeylonRunTool.java:367)
at com.redhat.ceylon.common.tools.CeylonTool.run(CeylonTool.java:547)
at com.redhat.ceylon.common.tools.CeylonTool.execute(CeylonTool.java:423)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.redhat.ceylon.launcher.Launcher.runInJava7Checked(Launcher.java:108)
at com.redhat.ceylon.launcher.Launcher.run(Launcher.java:38)
at com.redhat.ceylon.launcher.Launcher.run(Launcher.java:31)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at com.redhat.ceylon.launcher.Bootstrap.runVersion(Bootstrap.java:162)
at com.redhat.ceylon.launcher.Bootstrap.runInternal(Bootstrap.java:117)
at com.redhat.ceylon.launcher.Bootstrap.run(Bootstrap.java:93)
at com.redhat.ceylon.launcher.Bootstrap.main(Bootstrap.java:85)
Issue Analytics
- State:
- Created 6 years ago
- Reactions:1
- Comments:12 (10 by maintainers)
Top GitHub Comments
I think you’ll find the exact definition (exact distinction w/Exception) to be academic, a least with respect to this issue. Regardless, before trying to come up with a high level definition…
we can help ourselves by identifying which of the expressions in my comment above (and others, like
assert(exists x)
and Java NPEs) we might want to catch, and if any are excluded, why? It would be forcatch (e) { ... }
as in the example program, or perhaps a web application like:if there are other uses for
catch (e) { ... }
that are meaningfully different (would lead to different sets of exceptions we might want to catch), those should be described as well.To even be able to make any progress at all in this discussion, we have to reach some sort of shared understanding of what
AssertionError
s are even for, and in what circumstances onecatch
es them.