Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.
Bug 387612 - Unreachable catch block...exception is never thrown from the try
Summary: Unreachable catch block...exception is never thrown from the try
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 4.2   Edit
Hardware: PC Windows 7
: P3 major (vote)
Target Milestone: 4.3 M2   Edit
Assignee: Stephan Herrmann CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2012-08-20 10:32 EDT by Leon Finker CLA
Modified: 2012-09-17 23:19 EDT (History)
4 users (show)

See Also:


Attachments
patch under test (7.00 KB, patch)
2012-09-14 18:37 EDT, Stephan Herrmann CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Leon Finker CLA 2012-08-20 10:32:13 EDT
The following produces error in Eclipse, but no error happens from Oracle/Sun java compiler or from other IDEs:

interface A{
void authenticateUser(String username, String password)
                throws javax.naming.NameNotFoundException,
                       javax.naming.NamingException;        
}

interface B extends A{
void authenticateUser(String username, String password)
                throws javax.naming.NamingException;        
}

B b = new B() {
    @Override
    public void authenticateUser(String username, String password) {}
};
try {
    b.authenticateUser("", "");
} catch (NameNotFoundException e) {
} 
catch(javax.naming.InterruptedNamingException e){
//Eclipse ERROR: Unreachable catch block for InterruptedNamingException. 
//This exception is never thrown from the try statement body
}

No problem when going through the base interface. I have 3rd party lib that has this hierarchy and throws specifications (which I've simplified above). Not something I can change. But also there is no error from standard Java compiler or other IDEs.
Comment 1 Olivier Thomann CLA 2012-08-20 13:38:44 EDT
Please provide a complete test case that reproduces this issue. This will help identifying what could be wrong.
Thanks.
Comment 2 Olivier Thomann CLA 2012-09-12 20:38:03 EDT
Reducing severity. Might reassess once a test case is provided.
Comment 3 Stephan Herrmann CLA 2012-09-13 17:16:07 EDT
I can reproduce (in 4.2.0) given the example from comment 0. 
The method in B declares to throw NamingException, which is a common supertype of NameNotFoundException, InterruptedNamingException and others.

It seems that ECJ erroneously believes that catching NameNotFoundException already covers InterruptedNameingException, too, which is not true.

The bug does not occur when B is a class and no anonymous class is involved.

The bug also disappears when removing the redundant NameNotFoundException in the throws list in A.

Also: the redundant declaration must be in A, not in B to trigger the bug.

Another symptom of the bug is that a real error is not reported:
NamingException itself is not handled.
Comment 4 Leon Finker CLA 2012-09-13 23:38:20 EDT
I provided the complete case. It simply needs to be copy/pasted into java source file in eclipse. Why else is needed? There is no 3rd party source. It's just simple standard java.
Comment 5 Stephan Herrmann CLA 2012-09-14 02:48:52 EDT
(In reply to comment #4)
> I provided the complete case. It simply needs to be copy/pasted into java
> source file in eclipse. Why else is needed? There is no 3rd party source.
> It's just simple standard java.

Don't worry:

(In reply to comment #3)
> I can reproduce (in 4.2.0) given the example from comment 0.
Comment 6 Stephan Herrmann CLA 2012-09-14 18:32:23 EDT
I have a patch under test, which fixes the problem based on the following understanding:

The root of the problem is the redundant throws clause in A
(avoiding this redundancy also avoids the problem, i.e., a workaround exists).

If method lookup passes through Scope.mostSpecificMethodBinding() and if shouldIntersectExceptions is set to true, a flat list of exceptions is filtered as to eliminate more general exceptions in favour of more specific ones.

This elimination is only correct for exceptions from different interfaces, like:

(A, B independent, E1 subtype of E):

A#foo throws E
B#foo throws E1

I extends A, B
-> I#foo can only throw E1

But concluding from (A1 subtype of A)

A#foo throws E, E1
A1#foo throws E
C implements A1

that C#foo can only throw E1 is wrong.

My patch addresses this by splitting the filtering of exceptions inside Scope.mostSpecificMethodBinding():

(1) exceptions thrown from the same method are first filtered to favour more general exceptions (so we don't lose any exceptions that can possibly be thrown)

(2) only after that first step for filtering, eliminate by favouring more specific exceptions over more general ones.
Comment 7 Stephan Herrmann CLA 2012-09-14 18:37:24 EDT
Created attachment 221112 [details]
patch under test

This patch fixes the problem and passes tests.compiler.regression.

More testing still to be done.
Comment 8 Stephan Herrmann CLA 2012-09-15 15:01:12 EDT
Tests & fix have been released for 4.3 M2 via commit bfb7aec1d503f7f6d62839e0fbbc7dbce023c93b.

I briefly considered caching the result from exception filtering, because this function has O(n^2) effort, but experiments with compiling the jdt.ui plugin showed a total of only 18 executions of this method (not counting very early exits), all of which had n==2, so there wasn't really much time spent in this method, not enough to justify the memory for caching.
Comment 9 Srikanth Sankaran CLA 2012-09-17 23:18:52 EDT
(In reply to comment #4)
> I provided the complete case. It simply needs to be copy/pasted into java
> source file in eclipse. Why else is needed? There is no 3rd party source.
> It's just simple standard java.

Nope, comment#0 case needs slight fixing:

Here is a proper case:

//------
import javax.naming.NameNotFoundException;
import javax.naming.NamingException;

interface A {
	void authenticateUser(String username, String password)
			throws javax.naming.NameNotFoundException,
			javax.naming.NamingException;        
}

interface B extends A{
	void authenticateUser(String username, String password)
			throws javax.naming.NamingException;        
}

class C {

	public static void main(String[] args) {


		B b = new B() {
			public void authenticateUser(String username, String password) {}
		};
		try {
			b.authenticateUser("", "");
		} catch (NameNotFoundException e) {
		} 
		catch(javax.naming.InterruptedNamingException e){
			
		} catch (NamingException e) {
			
			e.printStackTrace();
		}
	}
}
Comment 10 Srikanth Sankaran CLA 2012-09-17 23:19:22 EDT
Verified for 4.3 M2 using Build id: I20120917-0800