diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NullAnnotationModelTests.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NullAnnotationModelTests.java index 51a6b88..30a88f0 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NullAnnotationModelTests.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/NullAnnotationModelTests.java @@ -15,6 +15,7 @@ import java.io.StringBufferInputStream; import java.net.URL; import java.util.Hashtable; +import java.util.List; import junit.framework.Test; @@ -33,6 +34,11 @@ import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.CompilationUnit; +import org.eclipse.jdt.core.dom.MarkerAnnotation; +import org.eclipse.jdt.core.dom.MethodDeclaration; +import org.eclipse.jdt.core.dom.Modifier; +import org.eclipse.jdt.core.dom.SingleVariableDeclaration; +import org.eclipse.jdt.core.dom.TypeDeclaration; public class NullAnnotationModelTests extends ReconcilerTests { @@ -415,4 +421,64 @@ deleteProject("P"); } } + + // A synthetic annotation from a default should not be converted to DOM AST + public void testAnnotationAST1() throws CoreException, InterruptedException { + try { + // Resources creation + IJavaProject p = createJavaProject("P", new String[] {""}, new String[] {"JCL15_LIB", this.ANNOTATION_LIB}, "bin", "1.5"); + p.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); + + this.createFolder("/P/p1"); + // bug could only be triggered if ASTConvert actually finds a '@' + // so in addition to the synthetic annotation there must also be a real one: + String annotSourceString = + "package p1;\n" + + "import java.lang.annotation.ElementType;\n" + + "import java.lang.annotation.Target;\n" + + "@Target({ElementType.PARAMETER,ElementType.METHOD})\n" + + "public @interface Annot {}\n"; + this.createFile( + "/P/p1/Annot.java", + annotSourceString); + String c1SourceString = + "package p1;\n" + + "@org.eclipse.jdt.annotation.NonNullByDefault\n" + + "public class C1 {\n" + + " public @Annot Object foo(@Annot Object arg) {\n" + + " return this;\n" + + " }\n" + + "}\n"; + this.createFile( + "/P/p1/C1.java", + c1SourceString); + + this.problemRequestor.initialize(c1SourceString.toCharArray()); + + final ICompilationUnit unit = getCompilationUnit("/P/p1/C1.java").getWorkingCopy(this.wcOwner, null); + assertNoProblem(c1SourceString.toCharArray(), unit); + + ASTParser parser = ASTParser.newParser(AST.JLS4); + parser.setProject(p); + parser.setResolveBindings(true); + parser.setSource(unit); + CompilationUnit ast = (CompilationUnit) parser.createAST(null); + assertNotNull("ast should not be null", ast); + TypeDeclaration type = (TypeDeclaration) ast.types().get(0); + assertNotNull("type should not be null", type); + MethodDeclaration method = (MethodDeclaration) type.bodyDeclarations().get(0); + assertNotNull("method should not be null", method); + SingleVariableDeclaration arg = (SingleVariableDeclaration) method.parameters().get(0); + assertNotNull("argument should not be null", arg); + List modifiers = arg.modifiers(); + assertEquals("Should have exactly one modifier", 1, modifiers.size()); + assertEquals("Unexpected modifier", "@Annot", ((MarkerAnnotation)modifiers.get(0)).toString()); + modifiers = method.modifiers(); + assertEquals("Method should have exactly two modifiers", 2, modifiers.size()); + assertEquals("Unexpected modifier #1 for method", "public", ((Modifier)modifiers.get(0)).toString()); + assertEquals("Unexpected modifier #2 for method", "@Annot", ((MarkerAnnotation)modifiers.get(1)).toString()); + } finally { + deleteProject("P"); + } + } } diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java index b72a6fb..a2d1924 100644 --- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java +++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/ASTConverter.java @@ -4361,6 +4361,7 @@ int indexInAnnotations = 0; while ((token = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) { IExtendedModifier modifier = null; + switchToken: switch(token) { case TerminalTokens.TokenNameabstract: modifier = createModifier(Modifier.ModifierKeyword.ABSTRACT_KEYWORD); @@ -4398,7 +4399,13 @@ case TerminalTokens.TokenNameAT : // we have an annotation if (annotations != null && indexInAnnotations < annotations.length) { - org.eclipse.jdt.internal.compiler.ast.Annotation annotation = annotations[indexInAnnotations++]; + // method may have synthetic annotations, skip them: + org.eclipse.jdt.internal.compiler.ast.Annotation annotation; + do { + if (indexInAnnotations == annotations.length) + break switchToken; + annotation = annotations[indexInAnnotations++]; + } while ((annotation.bits & org.eclipse.jdt.internal.compiler.ast.ASTNode.IsSynthetic) != 0); modifier = convert(annotation); this.scanner.resetTo(annotation.declarationSourceEnd + 1, modifiersEnd); } @@ -4512,6 +4519,7 @@ int token; while ((token = this.scanner.getNextToken()) != TerminalTokens.TokenNameEOF) { IExtendedModifier modifier = null; + switchToken: switch(token) { case TerminalTokens.TokenNameabstract: modifier = createModifier(Modifier.ModifierKeyword.ABSTRACT_KEYWORD); @@ -4549,7 +4557,13 @@ case TerminalTokens.TokenNameAT : // we have an annotation if (annotations != null && indexInAnnotations < annotations.length) { - org.eclipse.jdt.internal.compiler.ast.Annotation annotation = annotations[indexInAnnotations++]; + // argument may have synthetic annotations, skip them: + org.eclipse.jdt.internal.compiler.ast.Annotation annotation; + do { + if (indexInAnnotations == annotations.length) + break switchToken; + annotation = annotations[indexInAnnotations++]; + } while ((annotation.bits & org.eclipse.jdt.internal.compiler.ast.ASTNode.IsSynthetic) != 0); modifier = convert(annotation); this.scanner.resetTo(annotation.declarationSourceEnd + 1, this.compilationUnitSourceLength); } @@ -4623,6 +4637,7 @@ break; case TerminalTokens.TokenNameAT : // we have an annotation + // (local variable has no synthetic annotations, no need to skip them) if (annotations != null && indexInAnnotations < annotations.length) { org.eclipse.jdt.internal.compiler.ast.Annotation annotation = annotations[indexInAnnotations++]; modifier = convert(annotation); @@ -4725,6 +4740,7 @@ break; case TerminalTokens.TokenNameAT : // we have an annotation + // (local variable has no synthetic annotations, no need to skip them) if (annotations != null && indexInAnnotations < annotations.length) { org.eclipse.jdt.internal.compiler.ast.Annotation annotation = annotations[indexInAnnotations++]; modifier = convert(annotation); @@ -4806,6 +4822,7 @@ break; case TerminalTokens.TokenNameAT : // we have an annotation + // (local variable has no synthetic annotations, no need to skip them) if (annotations != null && indexInAnnotations < annotations.length) { org.eclipse.jdt.internal.compiler.ast.Annotation annotation = annotations[indexInAnnotations++]; modifier = convert(annotation);