Search

Categories

Links

Referers

Throwing unchecked exceptions

Mar 02 2006, 07:13:48 PM PST »Java Comments [11]
I'm doing spring cleaning of the compiler API (JSR 199, javax.tools). Yury suggested that while documenting which unchecked exceptions are thrown, perhaps they should not be listed in throws clauses.

So while this is good style:

/** * ... * @throws java.lang.RuntimeException if an unrecoverable error * occurred in a user supplied component. The cause will be the * error in user code. */

This is not:

void run() throws RuntimeException;

So how can I reliably find all unchecked exceptions? Very easily if I use an annotation processor. I used this annotation processor:

@SupportedAnnotationTypes("*") @SupportedOptions("summary") public class FindRuntimeException extends AbstractProcessor { // For summary of unchecked exceptions used in throws clauses final Set<TypeMirror> uncheckedExceptions = new LinkedHashSet<TypeMirror>(); public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnvironment) { // Get various utility classes final Elements elements = processingEnv.getElementUtils(); final Types types = processingEnv.getTypeUtils(); final Messager log = processingEnv.getMessager(); // Get the types (type mirrors) of RuntimeException and Error final TypeMirror runtimeException = elements.getTypeElement("java.lang.RuntimeException").asType(); final TypeMirror error = elements.getTypeElement("java.lang.Error").asType(); // Scanner for traversing the structure of a class, interface, etc. ElementScanner6<Void,Void> scan = new ElementScanner6<Void,Void>() { @Override public Void visitExecutable(ExecutableElement e, Void p) { // Executable means method or constructor, this method is called for // every method or constructor. So we just have to look at the throws // declaration... for (TypeMirror t :e.getThrownTypes()) // ...and see if any of the thrown types are subtypes of // RuntimeException or Error. if (types.isSubtype(t, runtimeException) || types.isSubtype(t, error)) { uncheckedExceptions.add(t); // print a warning so you can use your favorite editor to jump // to the problematic method log.printMessage(WARNING, "Throwing unchecked exception " + t, e); } return null; } }; // examine all the classes provided on the command line for (TypeElement e : roundEnvironment.getSpecifiedTypeElements()) scan.scan(e); // print a summary of found unchecked exceptions if (roundEnvironment.processingOver() && processingEnv.getOptions().containsKey("summary")) { System.out.println("Summary of unchecked exceptions found in throws clauses:"); for (TypeMirror t : uncheckedExceptions) System.out.println("\t" + t); } return true; } }

UPDATE: I have updated the code with comments and colors (using htmlize.el by Hrvoje Niksic)

Then I compiled the annotation processor:

javac -classpath /usr/java/jdk1.6.0/lib/tools.jar FindRuntimeException.java

You need a fairly recent Mustang snapshot to compile this. Also, from b74 (Thanks Kelly!), you won't have to put tools.jar on the classpath anymore. Then run the annotation processor:

javac -Asummary -proc:only -processor FindRuntimeException src/share/classes/javax/tools/*.java

Enjoy...

Post a Comment:
Comments are closed for this entry.
Comments:

Hmm, maybe some information on what the code actually does? :-) Because I have no idea how an annotations processor could help you find unchecked exceptions.

Posted by Tako Schotanus on March 03, 2006 at 04:18 AM PST #

Interesting!

For those poor souls (like me) who wish to compile the example, here's the imports:

import java.util.*;
import javax.annotation.processing.*;
import javax.lang.model.element.*;
import javax.lang.model.type.*;
import javax.lang.model.util.*;
import static javax.tools.DiagnosticMessage.Kind.*;

Posted by VVS on March 03, 2006 at 09:11 AM PST #

Why wouldn't you list them in the throws cause?

Posted by Bob Lee on March 03, 2006 at 11:01 AM PST #

Tako, the annotation processor doesn't find unchecked exceptions. I just finds were they are declared to be thrown.

Posted by Peter von der Ahe on March 03, 2006 at 11:27 AM PST #

Bob, the reason why I think it is appropriate to strip unchecked exceptions from throws clauses is that they have no semantic meaning. As I'm properly documenting the exceptions I don't see a need to clutter the API with repeated information.

Besides, I only added the throws clauses to keep the tool I used to check doc comments happy.

Posted by Peter von der Ahe on March 03, 2006 at 11:30 AM PST #

I think you should declare them in the throws clause if clients can handle them but you don't want to force them to. Yes, the exceptions are documented in the Javadocs, but specifying them in the throws clause makes life easier for tools as demonstrated by your processor example. An equivalent tool to find exceptions specified in Javadocs would have to use a doclet or qdox and would be more complex. For example, when you generate a try/catch block in an IDE, it could generate catch blocks for all the exceptions in the throws clause. If the exception is unchecked, the IDE could generate code that simply rethrows it by default (whereas it might wrap checked exceptions in a RuntimeException by default). It's a matter of taste, but I prefer the more explicit documentation. Javadocs should be a supplement to the throws clause, not a replacement.

Posted by Bob Lee on March 03, 2006 at 11:41 AM PST #

Bob,
There is a pretty good document from Javadoc authors with recommendations on javadoc style and common practices.
It also explicitly recommends not to use runtime exceptions in throws clauses, but rather in @throws:

http://java.sun.com/j2se/javadoc/writingdoccomments/#throwstag

To me, consistency is always a good thing.

Posted by VVS on March 03, 2006 at 06:09 PM PST #

Javadoc is but one tool of many, and that is written from their perspective. As another tool author following their convention, now I have to parse comments instead of plain Java or just using the reflection API? I value consistency, too (especially within a codebase, not as much so across codebases). Apparently I value it moreso than Sun based on the state of their coding conventions document and the fact that they don't follow it themselves.

Posted by Bob Lee on March 12, 2006 at 09:05 AM PST #

Bob,

In my opinion, it doesn't matter if the job of a wrting a tool is harder than the job of using a tool.

When it comes to listing unchecked exceptions in throws clauses, I find it redundant and unnecessary and thus prefer to leave them out. I only put them there in the first place because the tool I used to validate the doc comments is very limited. This is also why all the @throws tags used fully qualified names before the clean up.

I think I should have the freedom to write code (when otherwise semantically equivalent) like I prefer it, not how my tool dictates it. So while we might disagree on the style issue, I hope we can agree that tool authors have to accomodate many styles, not just their own.

Posted by Peter von der Ahe on March 12, 2006 at 09:34 AM PST #

I completely agree that tools should be more complicated if it makes users' lives easier. Whether or not to user the throws clause is a matter of taste.

As for whether or not JSR 199 should use the throws clause, the Javadoc guidelines referenced in an earlier comment point out that the JLS uses the throws clause in the specification of the API but not in the implementation (we're talking about specifying an API and not implementing it, right?).

Posted by Bob Lee on March 12, 2006 at 10:12 AM PST #

Then again, the JLS predates Javadoc, so maybe they've just stuck to the old convention instead of retrofitting everything?

Posted by Bob Lee on March 12, 2006 at 10:16 AM PST #

Java is a trademark of Sun Microsystems, Inc.
Copyright © 2006,2007 Peter von der Ahé