How to find potential unchecked exceptions in Java? -
in accordance java specification, java compiler verifies automatically checked exceptions caught, based on "throw" statements , method signatures, , ignores unchecked exceptions.
however, useful developer find out unchecked exceptions can thrown, instance 3rd party code might throw unchecked exceptions in situations developer tend expect checked exception (like long.parselong). or developer might throw unchecked exception placeholder future checked exception , forget replace it.
in these examples, theoretically possible find these uncaught unchecked exception. in first case, signature of long.parselong indicates throws numberformatexception, , in second case, source code available, compiler knows unchecked exceptions thrown.
my question is: there tool can report these cases? or maybe way let java compiler treat temporarily unchecked exceptions checked exceptions? useful verify manually , fix potential bugs otherwise cause crash of whole thread or application @ runtime.
edit: after answers, have underline goal not find exhaustive list of unchecked exceptions possible in system, potential bugs due unchecked exceptions. think boils down 2 cases:
- a method's signature indicates throws unchecked exception, , caller doesn't catch it
- a method's body throws explicitly unchecked exceptions, , caller doesn't catch them
yes can write static analysis this. i've done similar myself , wrote mine in program analysis tool called atlas. here: https://github.com/ensoftcorp/java-toolbox-commons/.../throwableanalysis.java code might helpful need, statically computes matches throw sites , potential catch sites in piece of software (conservatively in not consider path feasibility). case interested in throw sites not have corresponding catch block.
here important bits of analysis.
- all exceptions checked or unchecked must extend throwable. sounds interested in "unchecked" throwables should consider classes directly extend or children of class extends error or runtimeexception.
in atlas shell write following queries find unchecked throwables.
var supertypeedges = common.universe().edgestaggedwithany(xcsg.supertype) var errors = supertypeedges.reverse(common.typeselect("java.lang", "error")) var uncheckedexceptions = supertypeedges.reverse(common.typeselect("java.lang", "runtimeexception")) show(errors.union(uncheckedexceptions))
any exception can caught @ runtime (checked or unchecked) must have corresponding "throw" site. while thrown checked exception must declared in method signature, not required declared thrown unchecked exception. isn't important since can detect thrown unchecked exceptions looking @ type hierarchy discussed in step 1.
to match throw site corresponding catch block must remember thrown exception propogates call stack until caught (or crashes program when not caught main method or thread entry point). analysis need call graph (the more precise call graph more accurate analysis here). each throw of unchecked exception type step backwards along call graph callsite of method throw unchecked exception. check if callsite contained within try block (or has trap region if analyzing bytecode). if must check compatibility of catch blocks/trap regions , determine if exception caught. if exception not caught repeat process stepping backwards along call graph each callsite until exception caught or there no possible catch block.
using throwableanalysis code shared earlier bring find each uncaught thrown unchecked throwable types.
public class analysis { // execute show(analysis.run()) on atlas shell public static q run(){ q supertypeedges = common.universe().edgestaggedwithany(xcsg.supertype); q errors = supertypeedges.reverse(common.typeselect("java.lang", "error")); q uncheckedexceptions = supertypeedges.reverse(common.typeselect("java.lang", "runtimeexception")); q typeofedges = common.universe().edgestaggedwithany(xcsg.typeof); q thrownuncheckedthrowables = typeofedges.predecessors(errors.union(uncheckedexceptions)).nodestaggedwithany(xcsg.thrownvalue); atlasset<node> uncaughtthrownuncheckedthrowables = new atlashashset<node>(); for(node thrownuncheckedthrowable : thrownuncheckedthrowables.eval().nodes()){ if(throwableanalysis.findcatchforthrows(common.toq(thrownuncheckedthrowable)).eval().nodes().isempty()){ uncaughtthrownuncheckedthrowables.add(thrownuncheckedthrowable); } } q uncaughtthrownuncheckedthrowablemethods = common.toq(uncaughtthrownuncheckedthrowables).containers().nodestaggedwithany(xcsg.method); q calledges = common.universe().edgestaggedwithany(xcsg.call); q rootmethods = calledges.reverse(uncaughtthrownuncheckedthrowablemethods).roots(); q callchaintouncaughtthrowables = calledges.between(rootmethods, uncaughtthrownuncheckedthrowablemethods); return callchaintouncaughtthrowables.union(common.toq(uncaughtthrownuncheckedthrowables)); } }
here's screenshot of result of running code on following test case.
public class test { public static void main(string[] args) { foo(); } private static void foo(){ throw new pig("pigs can fly!"); } public static class pig extends runtimeexception { public pig(string message){ super(message); } } }
important caveats: must consider whether or not whole program analysis here. if analyze code , not full jdk (several million lines of code) detect uncaught runtime exceptions originate inside application. example not catch "test".substring(0,10) throws out of bounds exception inside substring method declared in string class in jdk. while atlas supports partial or whole program analysis using java source or bytecode , can scale full jdk, need allocate hour of pre-processing time , 20 gigabytes more memory if plan include full jdk.
Comments
Post a Comment