Community
Participate
Working Groups
I20111207-2118. Not sure whether related to null-annotations work, but it at least got triggered by such code. !ENTRY org.eclipse.jdt.core 4 4 2011-12-08 10:22:16.107 !MESSAGE Exception occurred during problem detection: ----------------------------------- SOURCE BEGIN ------------------------------------- package snippet; public class Snippet { public void foo(@NonNull Object o1) { System.out.println(o1.toString()); // OK: o1 cannot be null } @NonNull Object bar(@Nullable String s1) { foo(null); // cannot pass null argument @NonNull String s= null; // cannot assign null value @NonNull String t= s1; // cannot assign potentially null value return null; // cannot return null value } } org.eclipse.User.User(@NonNull String name, int uid, @Nullable String email) ----------------------------------- SOURCE END ------------------------------------- !STACK 0 java.lang.ClassCastException: org.eclipse.jdt.internal.compiler.lookup.MethodBinding cannot be cast to org.eclipse.jdt.internal.compiler.lookup.LocalVariableBinding at org.eclipse.jdt.internal.compiler.ast.ASTNode.resolveAnnotations(ASTNode.java:639) at org.eclipse.jdt.internal.compiler.ast.Argument.createBinding(Argument.java:47) at org.eclipse.jdt.internal.compiler.ast.Argument.bind(Argument.java:52) at org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.bindArguments(AbstractMethodDeclaration.java:138) at org.eclipse.jdt.internal.compiler.ast.AbstractMethodDeclaration.resolve(AbstractMethodDeclaration.java:495) at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.resolve(TypeDeclaration.java:1155) at org.eclipse.jdt.internal.compiler.ast.TypeDeclaration.resolve(TypeDeclaration.java:1265) at org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration.resolve(CompilationUnitDeclaration.java:539) at org.eclipse.jdt.internal.compiler.Compiler.resolve(Compiler.java:920) at org.eclipse.jdt.internal.core.CompilationUnitProblemFinder.process(CompilationUnitProblemFinder.java:195) at org.eclipse.jdt.internal.core.CompilationUnit.buildStructure(CompilationUnit.java:195) at org.eclipse.jdt.internal.core.Openable.generateInfos(Openable.java:258) at org.eclipse.jdt.internal.core.JavaElement.openWhenClosed(JavaElement.java:518) at org.eclipse.jdt.internal.core.CompilationUnit.makeConsistent(CompilationUnit.java:1079) at org.eclipse.jdt.internal.core.ReconcileWorkingCopyOperation.makeConsistent(ReconcileWorkingCopyOperation.java:170) 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.reconcile(JavaReconcilingStrategy.java:151) at org.eclipse.jdt.internal.ui.text.CompositeReconcilingStrategy.reconcile(CompositeReconcilingStrategy.java:86) at org.eclipse.jdt.internal.ui.text.JavaCompositeReconcilingStrategy.reconcile(JavaCompositeReconcilingStrategy.java:104) at org.eclipse.jface.text.reconciler.MonoReconciler.process(MonoReconciler.java:77) at org.eclipse.jface.text.reconciler.AbstractReconciler$BackgroundThread.run(AbstractReconciler.java:206)
This problem has been there for a long time and is not a regression. I could reproduce this on 3.6.2 and looking at the test case, I am sure it dates back even longer. Basically the test case is so broken that we have contradictory state in our bindings. We should recover rather than crash in these situations. org.eclipse.User.User(@NonNull String name, int uid, @Nullable String email) appears as a method binding, but also has a local variable. The null annotations are red herrings. They may as well have been @Blah and @BlahBlah to have reproduced this problem. As this is not a regression, I think we can defer this to 3.8 M5. Dani, do you agree ?
Version: 3.7.0 Build id: I20110613-1736 against the following test case I see the same problem: public class Snippet { public void foo(@Blah Object o1) { System.out.println(o1.toString()); // OK: o1 cannot be null } @Blah Object bar(@BlahBlah String s1) { foo(null); // cannot pass null argument @Blah String s= null; // cannot assign null value @Blah String t= s1; // cannot assign potentially null value return null; // cannot return null value } } org.eclipse.User.User(@Blah String name, int uid, @BlahBlah String email) // end of source
> As this is not a regression, I think we can defer this to 3.8 M5. > Dani, do you agree ? Yes, I agree. Thanks for the quick investigation.
Stripped-down example that also throws a CCE in 3.6.2 (at least one annotation seems to be necessary to reproduce): package snippet; public class Snippet { void foo(Object o1) { } org.User(@Bla String a)
Stephan, please see if we can recover gracefully from this situation. It is not connected to null annotations per se, but since it was discovered while testing your functionality :) you can have the privilege.
(In reply to comment #5) > Stephan, please see if we can recover gracefully from this situation. > > It is not connected to null annotations per se, but since it was discovered > while testing your functionality :) you can have the privilege. investigating
The bug is deep inside the parser, which for this particular case fails to create a valid AST, meaning we get a non-tree structure: during syntax recovery the same (Marker)Annotation instance is recorded both for the method and the argument. Subsequent phases have no chance to correctly set the recipient for this annotation, as it must serve two masters. It seems we need a way to tell the RecoveredType that a pendingAnnotation has been consumed from consumeFormalParameter(). Currently there is no connection at all in this direction. When implementing this in consumeFormalParameter() we can check whether arg.annotations and ((RecoveredType)currentElement).pendingAnnotations share common Annotation instances, and if so, mark that these have been taken care of and no longer should be considered "pending". Not sure if other locations could produce the same problem. The ambiguity is initially created when during recovery an annotation is both pushed on the expression stack *and* recoreded in pendingAnnotations (via a RecoveredAnnotation). So anybody consuming either side should probably inform the other, too.
Created attachment 208361 [details] snapshot of test & proposed fix Here's a snapshot of a patch that will fix the root cause. For the particular situation it does its job. I'm only concerned about similar locations needing a similar fix as consumeFormalParameter(). consumeCatchFormalParameter() is an obvious candidate but I find it tremendously difficult to create a test that reproduces the same situation for other syntactic elements.
Created attachment 208373 [details] complete fix + tests A few issues with the previous patch: 1. The index in annotationsConsumed(..) for if (consumedAnnotations[i] == pendingAnnotationAST) should be 'j' and not 'i'. The previous patch throws CCE with this- @Blah org.User(@Bla String str){}. Fixed in new patch. 2. The fix was also needed in other implementations of the consumeFormalParameter(..) method. A case in point: org.User(@Bla String... str) - try code select on str: CCE thrown Fixed in new patch. 3. Added ap parser test to make sure that the annoatation @Bla appears only on the parameter and not the method. Stephan, please see if this patch is ok and release. Btw, I think tests added in NullAnnotationTest should be moved to a more general place, such as AnnotationTest.java
(In reply to comment #9) > Created attachment 208373 [details] > complete fix + tests All tests pass with this patch.
(In reply to comment #9) > Created attachment 208373 [details] > complete fix + tests > > A few issues with the previous patch: > 1. The index in annotationsConsumed(..) for if (consumedAnnotations[i] == > pendingAnnotationAST) should be 'j' and not 'i'. The previous patch throws CCE > with this- @Blah org.User(@Bla String str){}. Fixed in new patch. Thanks! Silly beginner's mistake... good I marked the patch as a snapshot. > Stephan, please see if this patch is ok and release. Btw, I think tests added > in NullAnnotationTest should be moved to a more general place, such as > AnnotationTest.java Done. (In reply to comment #8) > [...]. I'm only concerned about > similar locations needing a similar fix as consumeFormalParameter(). > consumeCatchFormalParameter() is an obvious candidate but I find it > tremendously difficult to create a test that reproduces the same > situation for other syntactic elements. Update: running all JDT/Core tests with a conditional breakpoint yielded the following candidate: CompletionParserTest.testMB_1FHSLMQ_2() This one test enters consumeCatchFormalParameter() with currentElement set. From this test it could be possible to construct a witness for a related problem in consumeCatchFormalParameter(). However, now these bits don't fit: - run as a completion test the CompletionParser sets annotationRecoveryActivated to false => no pendingAnnotations recorded - run as a compiler test, annotationRecoveryActivated is true but now statementRecoveryActivated is false and thus we bail out on error. => no success reproducing the exception against a catch formal param. I guess at this point I should just release what we already have (with some minor polish) unless s.o. has a quick pointer how to get a parser with both mentioned flags set to true.
(In reply to comment #11) > I guess at this point I should just release what we already have (with > some minor polish) unless s.o. has a quick pointer how to get a parser > with both mentioned flags set to true. +1 to release what we have right now. Srikanth, any objections?
Patch from comment 9 has been polished, retested and pushed as http://git.eclipse.org/c/jdt/eclipse.jdt.core.git/commit/?id=44a65692ed8ea84bd05a0826234a3cf62365dd1a
(In reply to comment #12) > (In reply to comment #11) > > I guess at this point I should just release what we already have (with > > some minor polish) unless s.o. has a quick pointer how to get a parser > > with both mentioned flags set to true. > > +1 to release what we have right now. Srikanth, any objections? No, this plan sounds, good.
Verified for 3.8 M5 using build id: I20120122-2000