Bug 331872 - [compiler] NPE in Scope.createArrayType when attempting qualified access from type parameter
Summary: [compiler] NPE in Scope.createArrayType when attempting qualified access from...
Status: VERIFIED FIXED
Alias: None
Product: JDT
Classification: Eclipse Project
Component: Core (show other bugs)
Version: 3.7   Edit
Hardware: Other Linux
: P3 normal (vote)
Target Milestone: 3.7 M4   Edit
Assignee: Stephan Herrmann CLA
QA Contact:
URL:
Whiteboard:
Keywords:
Depends on:
Blocks:
 
Reported: 2010-12-05 11:59 EST by Stephan Herrmann CLA
Modified: 2010-12-07 12:37 EST (History)
3 users (show)

See Also:
srikanth_sankaran: review+


Attachments
proposed patch (4.12 KB, patch)
2010-12-05 12:45 EST, Stephan Herrmann CLA
no flags Details | Diff
Proposed fix + regression test (4.16 KB, patch)
2010-12-06 10:34 EST, Olivier Thomann CLA
no flags Details | Diff
Proposed fix + regression tests (8.47 KB, patch)
2010-12-06 12:18 EST, Olivier Thomann CLA
no flags Details | Diff
Proposed fix + regression tests (11.17 KB, patch)
2010-12-06 12:40 EST, Olivier Thomann CLA
no flags Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Stephan Herrmann CLA 2010-12-05 11:59:23 EST
The following test

	if (new CompilerOptions(getCompilerOptions()).complianceLevel < ClassFileConstants.JDK1_5)
		return;
	this.runNegativeTest(
		new String[] {
			"X.java",
			"public class X<p> {\n" + 
			"	void foo(p.O[] longs)  {\n" + 
			"	}\n" + 
			"}\n",
		},
		"----------\n" + 
		"1. ERROR in X.java (at line 2)\n" + 
		"	void foo(p.O[] longs)  {\n" + 
		"	         ^^^^^\n" + 
		"Illegal qualified access from the type parameter p\n" + 
		"----------\n");

currently throws

 java.lang.NullPointerException at org.eclipse.jdt.internal.compiler.lookup.Scope.createArrayType(Scope.java:701)

I admit the example is stupid, but NPE still isn't a good answer.
Comment 1 Stephan Herrmann CLA 2010-12-05 12:20:09 EST
Here's what happens:

Inside ArrayQualifiedTypeReference.getTypeBinding(Scope) it is assumed
that "super.getTypeBinding(scope)" will return @NonNull TypeBinding.

From scanning through implementations of TypeReference.getTypeBinding(Scope)
I see three exits with "return null", all others take care to return a
problem binding on error.

From those three exits two *could* even be safe: SingleTypeReference and
QualifiedTypeReference both have s.t. like
  if (..detectHierarchyCycle(..))
    return null;
One could argue that hierarchy cycles can probably only occur during
ClassScope.findSupertype(TypeReference)  the result of which is indeed
checked for null, hm..

BUT, the third occurrence is in QualifiedTypeReference just after
  problemReporter().illegalAccessFromTypeVariable(..)

Instead of adding one more null-check I'd prefer to make @NonNull the contract
and make everybody adhere to that.
Comment 2 Stephan Herrmann CLA 2010-12-05 12:45:50 EST
Created attachment 184557 [details]
proposed patch

This patch fixes the issue by returning a ProblemReferenceBinding.

This is safe and avoids the NPE.

A tiny question remains: what should be the appropriate ProblemReason?
By saying NotFound we get the following errors:

public class X<p> {
   void foo(p.O[] elems)  {
   }
   void bar() {
        foo(new Object[0]);
   }
}
	
----------
1. ERROR in X.java (at line 2)
	void foo(p.O[] elems)  {
	         ^^^^^
Illegal qualified access from the type parameter p
----------
2. ERROR in X.java (at line 2)
void foo(p.O[] elems)  {
         ^^^
p.O cannot be resolved to a type
----------
3. ERROR in X.java (at line 5)
	foo(new Object[0]);
	^^^
The method foo(O[]) from the type X<p> refers to the missing type O
----------

the 2. error is useless, but it's probably better to live with it rather
than, e.g., creating a ProblemReferenceBinding(..ProblemReasons.NoError),
which might cause confusion downstream, or creating a new constant just
for this special case.
Comment 3 Ayushman Jain CLA 2010-12-06 02:10:03 EST
Srikanth, please review. Thanks!
Comment 4 Olivier Thomann CLA 2010-12-06 10:33:12 EST
I wonder if we should not rather handle the null value inside org.eclipse.jdt.internal.compiler.ast.ArrayQualifiedTypeReference.getTypeBinding(Scope).
In this case, I don't think reporting the second error helps. If the type is really a missing type, it is already handled as such. So we can only get null when trying to use a type variable inside a qualified type reference.

Stephan, I'll propose another way to take care of this. I don't think we can simply use the missing type support for this as I don't see a good way to describe this missing type. It is not really a missing type.
Comment 5 Olivier Thomann CLA 2010-12-06 10:34:39 EST
Created attachment 184614 [details]
Proposed fix + regression test

I kept the regression test with a small modification about the expected errors.
Stephan, what do you think ?
Comment 6 Stephan Herrmann CLA 2010-12-06 11:19:28 EST
(In reply to comment #5)
> Created an attachment (id=184614) [details]
> Proposed fix + regression test
> 
> I kept the regression test with a small modification about the expected errors.
> Stephan, what do you think ?

I agree that the secondary error is not ideal.

How sure are we that ArrayQualifiedTypeReference.getTypeBinding(Scope)
is the only client that has to handle the null result from
QualifiedTypeReference.getTypeBinding(Scope) ?

Anyway, I agree with releasing your patch for M4. I might challenge it
afterwards to see if more clients are potentially affected.

If you want this released for the next I-build could you please do it?
I don't have the workspace configured on this machine.
Comment 7 Olivier Thomann CLA 2010-12-06 12:08:25 EST
I only see:
org.eclipse.jdt.internal.compiler.ast.JavadocArgumentExpression.internalResolveType(Scope)
that might require more work.

I wrote a small test case that produced:
java.lang.NullPointerException
at
org.eclipse.jdt.internal.compiler.ast.JavadocArgumentExpression.internalResolveType(JavadocArgumentExpression.java:53)
at
org.eclipse.jdt.internal.compiler.ast.JavadocArgumentExpression.resolveType(JavadocArgumentExpression.java:97)
at
org.eclipse.jdt.internal.compiler.ast.JavadocMessageSend.internalResolveType(JavadocMessageSend.java:64)
at
org.eclipse.jdt.internal.compiler.ast.JavadocMessageSend.resolveType(JavadocMessageSend.java:200)
at
org.eclipse.jdt.internal.compiler.ast.Javadoc.resolveReference(Javadoc.java:367)
at org.eclipse.jdt.internal.compiler.ast.Javadoc.resolve(Javadoc.java:269)
at
org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.resolveJavadoc(AbstractMethodDeclaration.java:440)
at
org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.resolve(AbstractMethodDeclaration.java:420)
at
org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.resolve(TypeDeclaration.java:1147)
at
org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.resolve(TypeDeclaration.java:1257)
at
org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration.resolve(CompilationUnitDeclaration.java:540)
at org.eclipse.jdt.internal.compiler.Compiler.resolve(Compiler.java:899)
at org.eclipse.jdt.internal.compiler.Compiler.resolve(Compiler.java:944)
at
org.eclipse.jdt.internal.core.CompilationUnitProblemFinder.process(CompilationUnitProblemFinder.java:202)
at
org.eclipse.jdt.internal.core.CompilationUnitProblemFinder.process(CompilationUnitProblemFinder.java:268)
at
org.eclipse.jdt.internal.core.ReconcileWorkingCopyOperation.makeConsistent(ReconcileWorkingCopyOperation.java:190)
at
org.eclipse.jdt.internal.core.ReconcileWorkingCopyOperation.executeOperation(ReconcileWorkingCopyOperation.java:89)
at
org.eclipse.jdt.internal.core.JavaModelOperation.run(JavaModelOperation.java:728)
at
org.eclipse.jdt.internal.core.JavaModelOperation.runOperation(JavaModelOperation.java:788)
at
org.eclipse.jdt.internal.core.CompilationUnit.reconcile(CompilationUnit.java:1244)
at
org.eclipse.jdt.internal.ui.text.java.JavaReconcilingStrategy.reconcile(JavaReconcilingStrategy.java:126)
at
org.eclipse.jdt.internal.ui.text.java.JavaReconcilingStrategy.access$0(JavaReconcilingStrategy.java:108)
at
org.eclipse.jdt.internal.ui.text.java.JavaReconcilingStrategy$1.run(JavaReconcilingStrategy.java:89)
at org.eclipse.core.runtime.SafeRunner.run(SafeRunner.java:42)
at
org.eclipse.jdt.internal.ui.text.java.JavaReconcilingStrategy.reconcile(JavaReconcilingStrategy.java:87)
at
org.eclipse.jdt.internal.ui.text.java.JavaReconcilingStrategy.initialReconcile(JavaReconcilingStrategy.java:178)
at
org.eclipse.jdt.internal.ui.text.CompositeReconcilingStrategy.initialReconcile(CompositeReconcilingStrategy.java:114)
at
org.eclipse.jdt.internal.ui.text.JavaCompositeReconcilingStrategy.initialReconcile(JavaCompositeReconcilingStrategy.java:133)
at
org.eclipse.jface.text.reconciler.MonoReconciler.initialProcess(MonoReconciler.java:105)
at
org.eclipse.jdt.internal.ui.text.JavaReconciler.initialProcess(JavaReconciler.java:398)
at
org.eclipse.jface.text.reconciler.AbstractReconciler$BackgroundThread.run(AbstractReconciler.java:173)


So I'll target 6pm build and not 1pm.
Comment 8 Olivier Thomann CLA 2010-12-06 12:18:01 EST
Created attachment 184635 [details]
Proposed fix + regression tests

This is fixing the javadoc case as well.
Comment 9 Olivier Thomann CLA 2010-12-06 12:18:37 EST
I'll run all tests and if ok, I'll release for M4.
Comment 10 Olivier Thomann CLA 2010-12-06 12:40:23 EST
Created attachment 184639 [details]
Proposed fix + regression tests

More regression tests.
Comment 11 Ayushman Jain CLA 2010-12-06 12:44:14 EST
I could see that there are quite a few calls to Scope.createArrayType(..) from other places as well, and all of them are not protected with a null check. Shouldn't we guard every call (or atleast those where we cannot ascertain nullity of the first argument) to prevent an NPE ?
Comment 12 Stephan Herrmann CLA 2010-12-06 12:52:35 EST
(In reply to comment #11)
> I could see that there are quite a few calls to Scope.createArrayType(..) from
> other places as well, and all of them are not protected with a null check.
> Shouldn't we guard every call (or atleast those where we cannot ascertain
> nullity of the first argument) to prevent an NPE ?

My impression is that those callers are right in not expecting null.
I suggest to release Olivier's patch to M4 and during M5 I will propose
a solution that avoids the null within createArrayType, which might 
include introducing a new ProblemReason and defer reporting until
ProblemReport.invalidType(..)

(I might have a more qualified statement within 2 hours or so ...)
Comment 13 Olivier Thomann CLA 2010-12-06 13:42:43 EST
I opened bug 331936 to follow up post M4.
Comment 14 Olivier Thomann CLA 2010-12-06 13:44:31 EST
Released last patch to HEAD.
Regression tests added in:
org.eclipse.jdt.core.tests.compiler.regression.ArrayTest#test018
org.eclipse.jdt.core.tests.compiler.regression.JavadocTest_1_5#testBug331872
org.eclipse.jdt.core.tests.compiler.regression.JavadocTest_1_5#testBug331872b
org.eclipse.jdt.core.tests.compiler.regression.JavadocTest_1_5#testBug331872c
org.eclipse.jdt.core.tests.compiler.regression.JavadocTest_1_5#testBug331872d
Comment 15 Srikanth Sankaran CLA 2010-12-07 06:23:12 EST
I agree with the latest patch and the approach planned.
Comment 16 Olivier Thomann CLA 2010-12-07 12:37:25 EST
Verified using I20101207-0250 (4.1 I-build)