Some Eclipse Foundation services are deprecated, or will be soon. Please ensure you've read this important communication.

Bug 510217

Summary: NullPointerException in MessageSend.resolveType
Product: [Eclipse Project] JDT Reporter: Stefan Xenos <sxenos>
Component: CoreAssignee: JDT-Core-Inbox <jdt-core-inbox>
Status: CLOSED WONTFIX QA Contact:
Severity: normal    
Priority: P3 CC: eclipse.sprigogin, manoj.palat, register.eclipse, sasikanth.bharadwaj, stephan.herrmann
Version: 4.6   
Target Milestone: ---   
Hardware: PC   
OS: Linux   
See Also: https://git.eclipse.org/r/88521
Whiteboard: stalebug

Description Stefan Xenos CLA 2017-01-10 15:54:33 EST
User has reported the following exception in the JDT compiler. I have access to the user's logs but don't (currently) have their code.

Exception is as follows:

java.lang.NullPointerException
        at org.eclipse.jdt.internal.compiler.ast.MessageSend.resolveType(MessageSend.java:898)
        at org.eclipse.jdt.internal.compiler.ast.LocalDeclaration.resolve(LocalDeclaration.java:252)
        at org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.resolveStatements(AbstractMethodDeclaration.java:634)
        at org.eclipse.jdt.internal.compiler.ast.MethodDeclaration.resolveStatements(MethodDeclaration.java:306)
        at org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.resolve(AbstractMethodDeclaration.java:544)
        at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.resolve(TypeDeclaration.java:1195)
        at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.resolve(TypeDeclaration.java:1308)
        at org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration.resolve(CompilationUnitDeclaration.java:593)
        at org.eclipse.jdt.internal.compiler.Compiler.process(Compiler.java:867)
        at org.eclipse.jdt.internal.compiler.ProcessTaskManager.run(ProcessTaskManager.java:141)
        at java.lang.Thread.run(Thread.java:745)

From static analysis of MessageSend.resolveType, there appears to be a bug at the end of the method, which unconditionally dereferences this.resolvedType:

	return (this.resolvedType.tagBits & TagBits.HasMissingType) == 0
				? this.resolvedType
				: null;

However, resolvedType is assigned like this earlier in the method:

TypeBinding returnType;
if ((this.bits & ASTNode.Unchecked) != 0 && this.genericTypeArguments == null) {
	// https://bugs.eclipse.org/bugs/show_bug.cgi?id=277643, align with javac on JLS 15.12.2.6
	returnType = this.binding.returnType;
	if (returnType != null) {
		returnType = scope.environment().convertToRawType(returnType.erasure(), true);
	}
} else {
	returnType = this.binding.returnType;
	if (returnType != null) {
		returnType = returnType.capture(scope, this.sourceStart, this.sourceEnd);
	}
}
this.resolvedType = returnType;

The null checks indicate that null is an expected value, so resolvedType shouldn't be unconditionally dereferenced at that point.
Comment 1 Stefan Xenos CLA 2017-01-10 15:56:56 EST
Bug 427749 seems related. The fix in that case was to return null in situations where the resolved type was null (however, I don't understand this code well enough to understand the implications of this).
Comment 2 Stephan Herrmann CLA 2017-01-10 16:22:58 EST
Cc'ing Srikanth is of little help :)
Either Sasi or me will have to look at this.

From what you write we seem to have a case where:
- this.binding.returnType is null (which then propagates via returnType)
- this.binding.isValidBinding() (otherwise would have exited before)

While this situation is hard to believe, how about:

  ((this.binding.tagBits & TagBits.HasMissingType) != 0)

if that's true, maybe that explains? But shouldn't this.binding.returnType be an instance of MissingTypeBinding in that case?
Comment 3 Stefan Xenos CLA 2017-01-10 16:25:13 EST
The problematic dereference was added by commit 48d5cc392e6d910c33f7049ee7ced65653038398 as part of bug 196200. Even at that time, it was possible for this.resolvedType to be null at that point so it's possible this is a very old bug.
Comment 4 Stefan Xenos CLA 2017-01-10 16:28:30 EST
I've just gotten access to the code that produced this error. I'll try to reduce it to something that can be attached here.
Comment 5 Stephan Herrmann CLA 2017-01-10 16:33:18 EST
(In reply to Stefan Xenos from comment #4)
> I've just gotten access to the code that produced this error. I'll try to
> reduce it to something that can be attached here.

Cool, hopefully that will also answer my questions from comment 2 (you may want to first inspect this in the debugger - might give hints about the relevant details in that code).
Comment 6 Stefan Xenos CLA 2017-01-11 13:35:02 EST
I've reduced my test case to the point where I can attach some source here. This is a source file that generates the NPE:

NpeClass.java ---------------------------

package npetest;

import javax.inject.Inject;
import testpackage.ClassWithMissingInterface;

interface Foo {
}

class GenericClass<RS extends Foo> {
}

public class NpeClass {
  @Inject
  public NpeClass() {
  }

  public void getMachineCountOfOwnerByLocation()
      throws Exception {
    GenericClass<ClassWithMissingInterface> machineList = getMachinesList();
  }

  private GenericClass<ClassWithMissingInterface> getMachinesList() throws Exception {
    return null;
  }
}

--------------------------------------------

Note that ClassWithMissingInterface is a class that exists on the class path but that has an interface whose .class file can't be found on the classpath. I generated it from the following source. After compiling it, I created a .jar from it and then manually deleted the .class file for MissingInterface.

ClassWithMissingInterface.java -----------------------------

package testpackage;

public class ClassWithMissingInterface implements MissingInterface {}

----------------------------------------------------

Some observations:

- The @Inject annotation seems critical. If I remove it or change it to an annotation that I declare in the same source file, the NPE goes away.

- The argument to the GenericClass needs to have the "indirectly referenced from class files" error on one of its base classes. If I pick some other valid class that doesn't implement Foo or a class that is missing entirely, the NPE goes away.

- I can't reproduce this in a new empty project. I'd guess the difference between a new empty project and the user's original project is that the user had a bunch of annotation processors defined on their factory classpath. I'll keep experimenting.
Comment 7 Eclipse Genie CLA 2017-01-11 21:06:05 EST
New Gerrit change created: https://git.eclipse.org/r/88521
Comment 8 Stefan Xenos CLA 2017-01-11 22:50:32 EST
The attached patch doesn't fix the problem - it just moves the NPE (new stack attached).

This suggests that either binding.returnType really isn't supposed to be null or there's additional places in the code that need additional null checks.

!STACK 0
java.lang.NullPointerException
	at org.eclipse.jdt.internal.compiler.ClassFile.completeMethodInfo(ClassFile.java:2166)
	at org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.generateCode(AbstractMethodDeclaration.java:360)
	at org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.generateCode(AbstractMethodDeclaration.java:276)
	at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.generateCode(TypeDeclaration.java:568)
	at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.generateCode(TypeDeclaration.java:637)
	at org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration.generateCode(CompilationUnitDeclaration.java:383)
	at org.eclipse.jdt.internal.compiler.Compiler.process(Compiler.java:878)
	at org.eclipse.jdt.internal.compiler.ProcessTaskManager.run(ProcessTaskManager.java:141)
	at java.lang.Thread.run(Thread.java:745)
Comment 9 Stephan Herrmann CLA 2017-01-12 05:22:35 EST
(In reply to Stefan Xenos from comment #8)
> The attached patch doesn't fix the problem - it just moves the NPE (new
> stack attached).
> 
> This suggests that either binding.returnType really isn't supposed to be
> null or there's additional places in the code that need additional null
> checks.

Right, that's exactly why we almost never simply add a null check when an NPE has been reported. We need to understand what's going on.

Thanks for the repro in comment 6. You haven't by chance crafted this into a JUnit?
Comment 10 Stefan Xenos CLA 2017-01-12 11:46:40 EST
> Thanks for the repro in comment 6. You haven't by chance crafted this
> into a JUnit?

No. It's not a complete set of repro steps. That's all the source code you need, but you additionally need an annotation processor and I can't figure out how to set one up. It looks to me like the project was just using Google Guice and nothing else, and I'm trying to get that working now.
Comment 11 Stefan Xenos CLA 2017-01-13 13:58:39 EST
That's as close as I can get to repro steps at the moment. I need to move on to a higher priority item.
Comment 12 Stephan Herrmann CLA 2017-01-13 14:43:35 EST
(In reply to Stefan Xenos from comment #11)
> That's as close as I can get to repro steps at the moment. I need to move on
> to a higher priority item.

did you perhaps mean to attach something? Or does "That" refer to comment 6?
Comment 13 Stefan Xenos CLA 2017-01-13 14:50:55 EST
> Or does "That" refer to comment 6?

Yes. Along with the notes attached to the other observations, above:
- The attached patch appears to fix the problem (providing an indication as to what was null and where it was passed).
- Reproduction requires an annotation processor.
- The project in question was using guice.
- The argument to the generic type needs to have the "Indirectly referenced by class files" error on it.
Comment 14 Till Brychcy CLA 2017-01-13 16:41:09 EST
(In reply to Stefan Xenos from comment #6)
> - I can't reproduce this in a new empty project. I'd guess the difference
> between a new empty project and the user's original project is that the user
> had a bunch of annotation processors defined on their factory classpath.
> I'll keep experimenting.

It might be the annotation processors, but it could also simply be a compilation order thing.

When you were able to reproduce it with the files you posted, were all other source files removed from the user's original project?
Comment 15 Stefan Xenos CLA 2017-01-13 16:42:19 EST
> When you were able to reproduce it with the files you posted, were all
> other source files removed from the user's original project?

Yes. It was the only .java file in the project.
Comment 16 Eclipse Genie CLA 2020-05-03 20:35:11 EDT
This bug hasn't had any activity in quite some time. Maybe the problem got resolved, was a duplicate of something else, or became less pressing for some reason - or maybe it's still relevant but just hasn't been looked at yet. As such, we're closing this bug.

If you have further information on the current state of the bug, please add it and reopen this bug. The information can be, for example, that the problem still occurs, that you still want the feature, that more information is needed, or that the bug is (for whatever reason) no longer relevant.

--
The automated Eclipse Genie.