質問

I have a large codebase which has many, many instances of the

try {
   // attempt to do something important
} catch (Exception e) {
   // do something lame
}

antipattern. Roughly 700 instances. I'm wondering if there is a tool (e.g., an IDE or something) that could magically, and in mass, replace all of these things with the next tier of specialized catch blocks. Something that would apply mass-refactoring similar to IntelliJ's or Eclipse's "wrap with try/catch" in the manner shown below. NOTE: I'm not saying all 700 are known IOExceptions; this is just an example assuming the only exception type thrown in the specific case was an IOException).

The refactoring I'm seeking is something that does the same semantic analysis of the try{} block that IntelliJ/Eclipse does to provide that "wrap with catch" or "add to throws clause" quick-fixes:

try {
   // attempt to do something important
   // where an IOException is thrown
} catch (IOException e) {
   // still do something lame
}

I understand that this doesn't "fix" the problem, but it saves a step-by-step manual bit of archaeology.

Ideas?

Related question on detecting general exception catch antipattern.

役に立ちましたか?

解決

Given that the problem is:

Find all blocks of the form:

 try { ... }
 catch (Exception e) { ... }

with

 try { ... }
 catch ( T1 e ) { ... }
 ....
 catch ( Tn e ) { ... }

for all T1, ... Tn e, a program transformation system that can do the code changes, and reason about the exceptions thrown, seems like a solution.

Our DMS Software Reengineering Toolkit with its Java Front End could likely do this.

You need an analyzer that can compute the set of exceptions thrown from inside the main block. You likely want to classify those exceptions according to object hierarchy too; if Tm is a specialization of Tn, you may want to generate a handler for Tm and Tn as nested tries. DMS has full type analysis, so it can determine the types in code, and thrown by a code block. One would have to write custom DMS code to compute the object hierarchy for exceptions.

You need a call graph, so one can propagate the exceptions found up the call graph to the try site. We have that but it needs some work for Java 1.7.

You need a transformation applier, to revised code. I think you need to worry about chained catches to handle the general case. With DMS, it should look like:

rule specialize_catch(b_try: block,
      E: qualifed_identifer, e: IDENTIFIER, b_recover: block,
      c_pre: catches, c_post: catches):
   statement -> statement
 " try { \b_try } 
   \c_pre
   catch ( \E \e ) { \b_recover }
   \c_post "
 -> 
  " try { \b_try }
    \c_pre 
    catch ( \least_specialized\(\E\,\b_try\,\c_pre\) \e ) { \b_recover }
    catch ( \E e ) { \b_recover }
    \c_post "
    if exists_specialized_exception(E,b_try,c_pre);

The syntax to be transformed is code in *meta*quotes "..." to seperate it from the syntax of the rewrite rules. The rewrite rule has a number of parts. It specifies a name (we often have hundreds). It provides a set of named placeholders (b_try, E, b_recover, ...) representing specific, named syntactic forms in the target language (in this case, Java), and written in bare form outside of metaquote and escaped (backslashed) form inside the metaquotes. c_pre is the name of a series of catch constructs; we can do this because "catches" form an associative list in the abstract, and similarly for the c_post. It provides for meta-function calls (e.g., least_specialized, exists_specialized_exception) that can can call on custom DMS machinery to compute results (booleans or new syntax [trees]). You'll note the metafunction call to least_specialized even has the syntax for the metafunction call escaped (e.g., the commas and parentheses) since they are not part of the Java language; outside the Java code, such metafunction calls don't need escaping. And most importantly it has a left hand side ("match this", binding the metavariables) and a right hand side ("replace by this" if the rule condition is true.

The metafunctions least_specialized and exists_specialized would compute, for exceptions that the main code block b_try might throw, and those handled by the existing catches c_pre, and the current exception type E, the most general exception above the c_pre's and below E, and a new catch block is inserted. If none exist, the if fails and no more exception insertions are made. You might want additonal transforms to de-clone the duplicated b_recover block.

I obviously haven't implemented this and may not have thought it through completely. But I can see this as path to a possible solution. YMMV.

I'll say for 700 instances it is probably pretty marginal to do this with DMS. If it took you personally 10 minutes apiece (edit, compile, oops...) that's 7000 minutes or ~~ 100 hours, about 2 weeks. I doubt you could configure DMS to do this that fast, especially having never done it before. (I have expert DMS users at my company for which this is likely a workable time frame).

But the claim is that a tool likely does exist.

他のヒント

I don't think any tools exist to do what you are trying. I can think of following algorithm but automating it in a script may take some time...it may even take as much time to write a script as it would take to make the changes manually.

  1. Define a new exception

class NeverThrown extends RuntimeException {}

  1. Global Replace catch (Exception e) with catch (NeverThrown e)

  2. Compile all files

  3. For each occurrence of unreported exception .... must be caught... identify the exception and add it to the next catch block if not already added, e.g. replace catch (NeverThrown e) with catch (NeverThrown, IOException e)

  4. Delete all occurrences of NeverThrown, and delete class definition.

Of course, the larger intellectual effort is in determining what to do instead of "something lame". That you will not be able to do in bulk, each case will have to be inspected individually. If I were you, I would stop at step #3 and go through the errors trying to figure out how to handle each uncaught exception reported.

Or you could completely denounce checked exceptions and remove all the try and catch blocks, adding throws Exception after each method :)

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top