| /******************************************************************************* |
| * Copyright (c) 2011, 2016 GK Software AG and others. |
| * |
| * This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| * |
| * Contributors: |
| * Stephan Herrmann - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.core.tests.model; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.lang.annotation.ElementType; |
| import java.lang.annotation.Target; |
| import java.io.ByteArrayInputStream; |
| import java.net.URL; |
| import java.util.Hashtable; |
| import java.util.List; |
| |
| import junit.framework.Test; |
| |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IncrementalProjectBuilder; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.FileLocator; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.jdt.annotation.NonNull; |
| import org.eclipse.jdt.core.IAnnotation; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaElement; |
| import org.eclipse.jdt.core.IJavaModelMarker; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IMemberValuePair; |
| import org.eclipse.jdt.core.IType; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.core.compiler.IProblem; |
| 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; |
| import org.osgi.framework.Bundle; |
| |
| @SuppressWarnings("rawtypes") |
| public class NullAnnotationModelTests extends ReconcilerTests { |
| |
| String ANNOTATION_LIB; |
| String ANNOTATION_LIB_V1; |
| |
| public static Test suite() { |
| return buildModelTestSuite(NullAnnotationModelTests.class); |
| } |
| |
| public NullAnnotationModelTests(String name) { |
| super(name); |
| } |
| |
| static { |
| // TESTS_NAMES = new String[] { "testConvertedSourceType1" }; |
| } |
| |
| /** |
| * @deprecated indirectly uses deprecated class PackageAdmin |
| */ |
| public void setUp() throws Exception { |
| super.setUp(); |
| Bundle[] bundles = org.eclipse.jdt.core.tests.Activator.getPackageAdmin().getBundles("org.eclipse.jdt.annotation", "[2.0.0,3.0.0)"); |
| File bundleFile = FileLocator.getBundleFile(bundles[0]); |
| this.ANNOTATION_LIB = bundleFile.isDirectory() ? bundleFile.getPath()+"/bin" : bundleFile.getPath(); |
| |
| bundles = org.eclipse.jdt.core.tests.Activator.getPackageAdmin().getBundles("org.eclipse.jdt.annotation", "[1.1.0,2.0.0)"); |
| bundleFile = FileLocator.getBundleFile(bundles[0]); |
| this.ANNOTATION_LIB_V1 = bundleFile.isDirectory() ? bundleFile.getPath()+"/bin" : bundleFile.getPath(); |
| } |
| |
| protected String testJarPath(String jarName) throws IOException { |
| URL libEntry = Platform.getBundle("org.eclipse.jdt.core.tests.model").getEntry("/workspace/NullAnnotations/lib/"+jarName); |
| return FileLocator.toFileURL(libEntry).getPath(); |
| } |
| |
| public void testConvertedSourceType1() 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"); |
| String c1SourceString = |
| "package p1;\n" + |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "@org.eclipse.jdt.annotation.NonNullByDefault\n" + |
| "public class C1 {\n" + |
| " public String foo(@Nullable Object arg) {\n" + // this is consumed via SourceTypeConverter |
| " return arg == null ? \"\" : arg.toString();\n" + |
| " }\n" + |
| "}\n"; |
| this.createFile( |
| "/P/p1/C1.java", |
| c1SourceString); |
| |
| this.createFolder("/P/p2"); |
| String c2SourceString = |
| "package p2;\n" + |
| "@org.eclipse.jdt.annotation.NonNullByDefault\n" + |
| "public class C2 {\n" + |
| " String bar(p1.C1 c, C2 c2) {;\n" + |
| " return c.foo(null);\n" + // don't complain despite default nonnull, foo has explicit @Nullable |
| " }\n" + |
| " String foo(Object arg) {\n" + |
| " return arg == null ? null : arg.toString();\n" + |
| " }\n" + |
| "}\n"; |
| this.createFile( |
| "/P/p2/C2.java", |
| c2SourceString); |
| |
| char[] c2SourceChars = c2SourceString.toCharArray(); |
| this.problemRequestor.initialize(c2SourceChars); |
| |
| getCompilationUnit("/P/p2/C2.java").getWorkingCopy(this.wcOwner, null); |
| |
| assertProblems("Unexpected problems", "----------\n" + |
| "1. WARNING in /P/p2/C2.java (at line 8)\n" + |
| " return arg == null ? null : arg.toString();\n" + |
| " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + |
| "Null type safety: The expression of type 'String' needs unchecked conversion to conform to \'@NonNull String\'\n" + |
| "----------\n"); |
| } finally { |
| deleteProject("P"); |
| } |
| } |
| |
| public void testBinaryType1() throws CoreException, InterruptedException, IOException { |
| try { |
| // Resources creation |
| IJavaProject p = createJavaProject("P", new String[] {""}, |
| new String[] {"JCL15_LIB", this.ANNOTATION_LIB, testJarPath("example.jar")}, |
| "bin", "1.5"); |
| p.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); |
| |
| // example.jar contains p1/C1.java just like testConvertedSourceType1() |
| |
| this.createFolder("/P/p2"); |
| String c2SourceString = |
| "package p2;\n" + |
| "@org.eclipse.jdt.annotation.NonNullByDefault\n" + |
| "public class C2 {\n" + |
| " String bar(p1.C1 c) {;\n" + |
| " return c.foo(null);\n" + // don't complain despite default nonnull, foo has explicit @Nullable |
| " }\n" + |
| " String foo(Object arg) {\n" + |
| " return arg == null ? null : arg.toString();\n" + |
| " }\n" + |
| "}\n"; |
| this.createFile( |
| "/P/p2/C2.java", |
| c2SourceString); |
| |
| char[] c2SourceChars = c2SourceString.toCharArray(); |
| this.problemRequestor.initialize(c2SourceChars); |
| |
| getCompilationUnit("/P/p2/C2.java").getWorkingCopy(this.wcOwner, null); |
| |
| assertProblems("Unexpected problems", "----------\n" + |
| "1. WARNING in /P/p2/C2.java (at line 8)\n" + |
| " return arg == null ? null : arg.toString();\n" + |
| " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n" + |
| "Null type safety: The expression of type 'String' needs unchecked conversion to conform to \'@NonNull String\'\n" + |
| "----------\n"); |
| } finally { |
| deleteProject("P"); |
| } |
| } |
| |
| // DISABLED: no longer a problem since bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults |
| public void _testMissingAnnotation1() throws CoreException { |
| 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); |
| p.setOption(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "in.valid"); |
| |
| this.createFolder("/P/p1"); |
| String c1SourceString = |
| "package p1;\n" + |
| "@org.eclipse.jdt.annotation.NonNullByDefault\n" + |
| "public class C1 {\n" + |
| " public String foo(Object arg) {\n" + |
| " return arg == null ? \"\" : arg.toString();\n" + |
| " }\n" + |
| "}\n"; |
| this.createFile( |
| "/P/p1/C1.java", |
| c1SourceString); |
| |
| this.problemRequestor.initialize(c1SourceString.toCharArray()); |
| |
| getCompilationUnit("/P/p1/C1.java").getWorkingCopy(this.wcOwner, null); |
| |
| assertProblems("Unexpected problems", |
| "----------\n" + |
| "1. ERROR in /P/p1/C1.java (at line 1)\n" + |
| " package p1;\n" + |
| " ^\n" + |
| "Buildpath problem: the type in.valid, which is configured as a null annotation type, cannot be resolved\n" + |
| "----------\n"); |
| } finally { |
| deleteProject("P"); |
| } |
| } |
| |
| // DISABLED: no longer a problem since bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults |
| public void _testMissingAnnotation2() throws CoreException { |
| Hashtable<String, String> javaOptions = JavaCore.getOptions(); |
| try { |
| // Resources creation |
| IJavaProject p = createJavaProject("P", new String[] {""}, new String[] {"JCL15_LIB", this.ANNOTATION_LIB}, "bin", "1.5"); |
| IFile settings = (IFile) p.getProject().findMember(".settings/org.eclipse.jdt.core.prefs"); |
| settings.appendContents(new ByteArrayInputStream("\norg.eclipse.jdt.core.compiler.annotation.nonnull=not.valid\n".getBytes()), 0, null); |
| p.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); |
| |
| this.createFolder("/P/p1"); |
| String c1SourceString = |
| "package p1;\n" + |
| "@org.eclipse.jdt.annotation.NonNullByDefault\n" + |
| "public class C1 {\n" + |
| " public String foo(Object arg) {\n" + |
| " return arg == null ? \"\" : arg.toString();\n" + |
| " }\n" + |
| "}\n"; |
| this.createFile( |
| "/P/p1/C1.java", |
| c1SourceString); |
| |
| this.problemRequestor.initialize(c1SourceString.toCharArray()); |
| |
| getCompilationUnit("/P/p1/C1.java").getWorkingCopy(this.wcOwner, null); |
| |
| assertProblems("Unexpected problems", |
| "----------\n" + |
| "1. ERROR in /P/p1/C1.java (at line 1)\n" + |
| " package p1;\n" + |
| " ^\n" + |
| "Buildpath problem: the type not.valid, which is configured as a null annotation type, cannot be resolved\n" + |
| "----------\n"); |
| } finally { |
| deleteProject("P"); |
| JavaCore.setOptions(javaOptions); |
| // work against side-effect of JavaRuntime listening to change of prefs-file. |
| // see https://bugs.eclipse.org/bugs/show_bug.cgi?id=302850#c25 |
| } |
| } |
| |
| // Bug 363858 - [dom] early throwing of AbortCompilation causes NPE in CompilationUnitResolver |
| // currently not actually challenging the NPE, because we no longer report |
| // "Cannot use the unqualified name \'invalid\' as an annotation name for null specification" |
| // DISABLED: no longer a problem since bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults |
| public void _testMissingAnnotation3() throws CoreException { |
| 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); |
| p.setOption(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "invalid"); |
| |
| this.createFolder("/P/p1"); |
| String c1SourceString = |
| "package p1;\n" + |
| "@org.eclipse.jdt.annotation.NonNullByDefault\n" + |
| "public class C1 {\n" + |
| " public String foo(Object arg) {\n" + |
| " return arg == null ? \"\" : arg.toString();\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); |
| assertProblems("Unexpected problems", |
| "----------\n" + |
| "1. ERROR in /P/p1/C1.java (at line 1)\n" + |
| " package p1;\n" + |
| " ^\n" + |
| "Buildpath problem: the type invalid, which is configured as a null annotation type, cannot be resolved\n" + |
| "----------\n"); |
| |
| ASTParser parser = ASTParser.newParser(AST_INTERNAL_JLS11); |
| parser.setProject(p); |
| parser.setResolveBindings(true); |
| parser.setSource(unit); |
| CompilationUnit ast = (CompilationUnit) parser.createAST(null); |
| assertNotNull("ast should not be null", ast); |
| this.problemRequestor.reset(); |
| this.problemRequestor.beginReporting(); |
| IProblem[] problems = ast.getProblems(); |
| for (int i=0; i<problems.length; i++) |
| this.problemRequestor.acceptProblem(problems[i]); |
| assertProblems("Unexpected problems (2)", |
| "----------\n" + |
| "1. ERROR in /P/p1/C1.java (at line 1)\n" + |
| " package p1;\n" + |
| " ^\n" + |
| "Buildpath problem: the type invalid, which is configured as a null annotation type, cannot be resolved\n" + |
| "----------\n"); |
| } finally { |
| deleteProject("P"); |
| } |
| } |
| |
| // initialization of null annotations is triggered from package-info.java: illegal simple name |
| public void testMissingAnnotation4() 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); |
| p.setOption(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "invalid"); |
| |
| this.createFolder("/P/p1"); |
| String piSourceString = |
| "@org.eclipse.jdt.annotation.NonNullByDefault\n" + |
| "package p1;\n"; |
| this.createFile( |
| "/P/p1/package-info.java", |
| piSourceString); |
| |
| this.problemRequestor.initialize(piSourceString.toCharArray()); |
| |
| // Challenge CompilationUnitProblemFinder: |
| final ICompilationUnit unit = getCompilationUnit("/P/p1/package-info.java").getWorkingCopy(this.wcOwner, null); |
| // This error is not raised currently: |
| // String expectedError = "----------\n" + |
| // "1. ERROR in /P/p1/package-info.java (at line 0)\n" + |
| // " @org.eclipse.jdt.annotation.NonNullByDefault\n" + |
| // " ^\n" + |
| // "Cannot use the unqualified name \'invalid\' as an annotation name for null specification\n" + |
| // "----------\n"; |
| // assertProblems("Unexpected problems from CompilationUnitProblemFinder", expectedError); |
| assertNoProblem(unit.getBuffer().getCharacters(), unit); |
| |
| // Challenge JavaBuilder: |
| p.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null); |
| IMarker[] markers = p.getProject().findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE); |
| // assertMarkers("Unexpected markers", |
| // "Cannot use the unqualified name 'invalid' as an annotation name for null specification", |
| // markers); |
| // assertEquals("Unexpected marker path", "/P", markers[0].getResource().getFullPath().toString()); |
| assertEquals("Should have no markers", 0, markers.length); |
| |
| // Challenge CompilationUnitResolver: |
| ASTParser parser = ASTParser.newParser(AST_INTERNAL_JLS11); |
| parser.setProject(p); |
| parser.setResolveBindings(true); |
| parser.setSource(unit); |
| CompilationUnit ast = (CompilationUnit) parser.createAST(null); |
| assertNotNull("ast should not be null", ast); |
| // this.problemRequestor.reset(); |
| // this.problemRequestor.beginReporting(); |
| // IProblem[] problems = ast.getProblems(); |
| // for (int i=0; i<problems.length; i++) |
| // this.problemRequestor.acceptProblem(problems[i]); |
| // assertProblems("Unexpected problems from CompilationUnitResolver", expectedError); |
| assertEquals("Should have no problems", 0, ast.getProblems().length); |
| } finally { |
| deleteProject("P"); |
| } |
| } |
| |
| // initialization of null annotations is |
| // - triggered from resolveTypesFor(MethodBinding) |
| // - default is defined in package-info.java: |
| // must detect missing non-null annotation and report against the project |
| // DISABLED: no longer a problem since bug 365531 - [compiler][null] investigate alternative strategy for internally encoding nullness defaults |
| public void _testMissingAnnotation5() 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); |
| p.setOption(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "pack.Missing"); |
| |
| this.createFolder("/P/p1"); |
| String piSourceString = |
| "@org.eclipse.jdt.annotation.NonNullByDefault\n" + |
| "package p1;\n"; |
| this.createFile("/P/p1/package-info.java", piSourceString); |
| |
| String c1SourceString = |
| "package p1;\n" + |
| "public class C1 {\n" + |
| " String foo(String arg) { return arg; }\n" + |
| "}\n"; |
| this.createFile("/P/p1/C1.java", c1SourceString); |
| |
| this.problemRequestor.initialize(piSourceString.toCharArray()); |
| |
| // Challenge CompilationUnitProblemFinder: |
| assertNoProblem(piSourceString.toCharArray(), getCompilationUnit("/P/p1/package-info.java")); |
| |
| this.problemRequestor.initialize(c1SourceString.toCharArray()); |
| |
| // Challenge CompilationUnitProblemFinder: |
| ICompilationUnit unit = getCompilationUnit("/P/p1/C1.java").getWorkingCopy(this.wcOwner, null); |
| String expectedError = "----------\n" + |
| "1. ERROR in /P/p1/C1.java (at line 1)\n" + |
| " package p1;\n" + |
| " ^\n" + |
| "Buildpath problem: the type pack.Missing, which is configured as a null annotation type, cannot be resolved\n" + |
| "----------\n"; |
| assertProblems("Unexpected problems", expectedError); |
| |
| // Challenge JavaBuilder: |
| p.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null); |
| IMarker[] markers = p.getProject().findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE); |
| assertMarkers("Unexpected markers", |
| "Buildpath problem: the type pack.Missing, which is configured as a null annotation type, cannot be resolved", |
| markers); |
| // toggle expectation depending on how CAT_BUILDPATH problems are reported (see AbstractImageBuilder.storeProblemsFor(..)) |
| // assertEquals("Unexpected marker path", "/P", markers[0].getResource().getFullPath().toString()); |
| assertEquals("Unexpected marker path", "/P/p1/C1.java", markers[0].getResource().getFullPath().toString()); |
| |
| // Challenge CompilationUnitResolver: |
| ASTParser parser = ASTParser.newParser(AST_INTERNAL_JLS11); |
| parser.setProject(p); |
| parser.setResolveBindings(true); |
| parser.setSource(unit); |
| CompilationUnit ast = (CompilationUnit) parser.createAST(null); |
| assertNotNull("ast should not be null", ast); |
| this.problemRequestor.reset(); |
| this.problemRequestor.beginReporting(); |
| IProblem[] problems = ast.getProblems(); |
| for (int i=0; i<problems.length; i++) |
| this.problemRequestor.acceptProblem(problems[i]); |
| assertProblems("Unexpected problems (2)", expectedError); |
| } finally { |
| 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_INTERNAL_JLS11); |
| 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"); |
| } |
| } |
| // https://bugs.eclipse.org/bugs/show_bug.cgi?id=372012 |
| // no problem should be created for a compilation unit in a package missing package-info when the warning is enabled |
| public void testBug372012() throws JavaModelException, IOException, 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); |
| p.setOption(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "in.valid"); |
| p.setOption(JavaCore.COMPILER_PB_MISSING_NONNULL_BY_DEFAULT_ANNOTATION, JavaCore.ERROR); |
| |
| this.createFolder("/P/p1"); |
| String c1SourceString = |
| "package p1;\n" + |
| "public class C1 {\n" + |
| " public String foo(Object arg) {\n" + |
| " return arg == null ? \"\" : arg.toString();\n" + |
| " }\n" + |
| "}\n"; |
| |
| assertNoProblem(c1SourceString.toCharArray(), getCompilationUnit("/P/p1/C1.java")); |
| } finally { |
| deleteProject("P"); |
| } |
| } |
| |
| // see https://bugs.eclipse.org/418233 |
| public void testNonNullDefaultInInner() throws CoreException, IOException, InterruptedException { |
| IJavaProject project15 = null; |
| try { |
| project15 = createJavaProject("TestAnnot", new String[] {"src"}, new String[] {"JCL15_LIB", this.ANNOTATION_LIB}, "bin", "1.5"); |
| createFolder("/TestAnnot/src/p1"); |
| createFile( |
| "/TestAnnot/src/p1/Interfaces.java", |
| "package p1;\n" + |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "\n" + |
| "@NonNullByDefault\n" + |
| "public interface Interfaces {\n" + |
| " public interface InnerInterface {\n" + |
| " Object doSomethingElse(Object o);\n" + |
| " }\n" + |
| "}" |
| ); |
| String source = |
| "package p1;\n" + |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "\n" + |
| "@NonNullByDefault\n" + |
| "public class Implementations implements Interfaces.InnerInterface {\n" + |
| " public Object doSomethingElse(Object o) {\n" + |
| " return o; \n" + |
| " }\n" + |
| "}"; |
| createFile( |
| "/TestAnnot/src/p1/Implementations.java", |
| source |
| ); |
| project15.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_1_5); |
| project15.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_1_5); |
| project15.setOption(JavaCore.COMPILER_PB_NULL_REFERENCE, JavaCore.ERROR); |
| project15.setOption(JavaCore.COMPILER_PB_POTENTIAL_NULL_REFERENCE, JavaCore.ERROR); |
| project15.setOption(JavaCore.COMPILER_PB_REDUNDANT_NULL_CHECK, JavaCore.ERROR); |
| project15.setOption(JavaCore.COMPILER_PB_INCLUDE_ASSERTS_IN_NULL_ANALYSIS, JavaCore.ENABLED); |
| project15.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); |
| |
| this.workingCopies = new ICompilationUnit[1]; |
| char[] sourceChars = source.toCharArray(); |
| this.problemRequestor.initialize(sourceChars); |
| this.workingCopies[0] = getCompilationUnit("/TestAnnot/src/p1/Implementations.java").getWorkingCopy(this.wcOwner, null); |
| this.workingCopies[0].makeConsistent(null); |
| this.workingCopies[0].reconcile(ICompilationUnit.NO_AST, false, null, null); |
| |
| assertNoProblem(sourceChars, this.workingCopies[0]); |
| } finally { |
| if (project15 != null) |
| deleteProject(project15); |
| } |
| } |
| /* |
| * Bug 405843 - [1.8] Support type annotations in Java Model(https://bugs.eclipse.org/bugs/show_bug.cgi?id=405843) |
| */ |
| public void testBug405843() throws CoreException, IOException, InterruptedException { |
| IJavaProject project = null; |
| try { |
| project = createJavaProject("Bug405843", new String[] {"src"}, new String[] {"JCL18_LIB", this.ANNOTATION_LIB}, "bin", "1.8"); |
| createFolder("/Bug405843/src/p1"); |
| createFile("/Bug405843/src/p1/Function.java", |
| "package p1;\n" + |
| "public interface Function <I, O> {\n" + |
| "}\n;"); |
| |
| createFile("/Bug405843/src/p1/FunctionImpl.java", |
| "package p1;\n" + |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "public class FunctionImpl implements Function<@NonNull String, @Nullable Object> {\n" + |
| "}\n"); |
| |
| project.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); |
| |
| setUpWorkingCopy("/Bug405843/src/p1/X.java", |
| "package p1;\n" + |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "public class X {\n" + |
| " public Object foo() {\n" + |
| " Function<@NonNull String, @Nullable Object> impl = new FunctionImpl();\n" + |
| " return impl;\n" + |
| " }\n" + |
| "}\n"); |
| assertProblems( |
| "Unexpected problems", |
| "----------\n" + |
| "----------\n" |
| ); |
| |
| } finally { |
| if (project != null) |
| deleteProject(project); |
| } |
| } |
| public void testBug405843a() throws CoreException, IOException, InterruptedException { |
| IJavaProject project = null; |
| try { |
| project = createJavaProject("Bug405843", new String[] {"src"}, new String[] {"JCL18_LIB", this.ANNOTATION_LIB}, "bin", "1.8"); |
| createFolder("/Bug405843/src/p1"); |
| createFile("/Bug405843/src/p1/Y.java", |
| "package p1;\n" + |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "public class Y {\n" + |
| " void foo(@NonNull String @NonNull [] array) {}\n" + |
| "}\n;"); |
| |
| project.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); |
| |
| setUpWorkingCopy("/Bug405843/src/p1/X.java", |
| "package p1;\n" + |
| "public class X {\n" + |
| " public void foo(Y y) {\n" + |
| " y.foo(null);\n" + |
| " }\n" + |
| "}\n"); |
| assertProblems( |
| "Unexpected problems", |
| "----------\n" + |
| "1. ERROR in /Bug405843/src/p1/X.java (at line 4)\n" + |
| " y.foo(null);\n" + |
| " ^^^^\n" + |
| "Null type mismatch: required \'@NonNull String @NonNull[]\' but the provided value is null\n" + |
| "----------\n" |
| ); |
| |
| } finally { |
| if (project != null) |
| deleteProject(project); |
| } |
| } |
| |
| // was: NPE in ProblemReporter.illegalReturnRedefinition() from ImplicitNullAnnotationVerifier.checkNullSpecInheritance() |
| public void testBug458361a() throws CoreException { |
| IJavaProject project = null; |
| try { |
| project = createJavaProject("Bug458361", new String[] {"src"}, new String[] {"JCL18_LIB", this.ANNOTATION_LIB}, "bin", "1.8"); |
| |
| project.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); |
| project.setOption(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION, JavaCore.ERROR); |
| |
| setUpWorkingCopy("/Bug458361/src/MyCollection.java", |
| "import java.util.Collection;\n" + |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "public interface MyCollection<T> extends Collection<T> {\n" + |
| " public @Nullable T get(int i);\n" + |
| "}\n"); |
| assertProblems( |
| "Unexpected problems", |
| "----------\n" + |
| "1. ERROR in /Bug458361/src/MyCollection.java (at line 4)\n" + |
| " public @Nullable T get(int i);\n" + |
| " ^^^^^^^^^^^\n" + |
| "The return type is incompatible with the free type variable \'T\' returned from Collection<T>.get(int) (mismatching null constraints)\n" + |
| "----------\n"); |
| } finally { |
| if (project != null) |
| deleteProject(project); |
| } |
| } |
| public void testBug458361b() throws CoreException { |
| IJavaProject project = null; |
| try { |
| project = createJavaProject("Bug458361", new String[] {"src"}, new String[] {"JCL17_LIB", this.ANNOTATION_LIB_V1}, "bin", "1.7"); |
| |
| project.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); |
| project.setOption(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION, JavaCore.ERROR); |
| |
| createFile("/Bug458361/src/Super.java", |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "public interface Super {\n" + |
| " @NonNull String getName();\n" + |
| "}\n"); |
| |
| setUpWorkingCopy("/Bug458361/src/Sub.java", |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "public interface Sub extends Super {\n" + |
| " @Nullable String getName();\n" + |
| "}\n"); |
| assertProblems( |
| "Unexpected problems", |
| "----------\n" + |
| "1. ERROR in /Bug458361/src/Sub.java (at line 3)\n" + |
| " @Nullable String getName();\n" + |
| " ^^^^^^^^^^^^^^^^\n" + |
| "The return type is incompatible with \'@NonNull String\' returned from Super.getName() (mismatching null constraints)\n" + |
| "----------\n"); |
| } finally { |
| if (project != null) |
| deleteProject(project); |
| } |
| } |
| |
| public void testBug487781() throws CoreException, IOException, InterruptedException { |
| IJavaProject project = null; |
| try { |
| project = createJavaProject("Bug487781", new String[] {"src"}, new String[] {"JCL18_LIB", this.ANNOTATION_LIB}, "bin", "1.8"); |
| project.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); |
| project.setOption(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION, JavaCore.ERROR); |
| createFolder("/Bug487781/src/test"); |
| |
| createFile("/Bug487781/src/test/Util.java", |
| "package test;\n" + |
| "import org.eclipse.jdt.annotation.NonNullByDefault;\n" + |
| "import org.eclipse.jdt.annotation.Nullable;\n" + |
| "class A<T> {}\n" + |
| "interface I {}\n" + |
| "@NonNullByDefault\n" + |
| "class Util2<T extends @Nullable I> {}\n" + |
| "@NonNullByDefault\n" + |
| "public class Util {\n" + |
| " public static <T extends @Nullable I> void uniqueMapOfUniqueable(A<T> set) {\n" + |
| " }\n" + |
| "}\n"); |
| |
| |
| setUpWorkingCopy("/Bug487781/src/test/Usage.java", |
| "package test;\n" + |
| "import org.eclipse.jdt.annotation.Nullable;\n" + |
| "public class Usage {\n" + |
| " public void f() {\n" + |
| " Util.uniqueMapOfUniqueable(new A<@Nullable I>());\n" + |
| " new Util2<@Nullable I>();\n" + |
| " }\n" + |
| "}\n;"); |
| assertProblems( |
| "Unexpected problems", |
| "----------\n" + |
| "----------\n" |
| ); |
| |
| } finally { |
| if (project != null) |
| deleteProject(project); |
| } |
| } |
| |
| public void testBug495635() throws CoreException, IOException, InterruptedException { |
| IJavaProject project = null; |
| try { |
| project = createJavaProject("Bug495635", new String[] {"src"}, new String[] {"JCL18_LIB", this.ANNOTATION_LIB}, "bin", "1.8"); |
| project.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); |
| project.setOption(JavaCore.COMPILER_PB_NULL_SPECIFICATION_VIOLATION, JavaCore.ERROR); |
| |
| createFile("/Bug495635/src/AURegObject.java", |
| "public interface AURegObject {}\n" |
| ); |
| createFile("/Bug495635/src/AURegKey.java", |
| "public interface AURegKey<O extends AURegObject> {}\n" |
| ); |
| createFile("/Bug495635/src/Person.java", |
| "public interface Person<O extends Person<O>> extends AURegObject, PersonKey<O> {}\n" |
| ); |
| createFile("/Bug495635/src/PersonKey.java", |
| "public interface PersonKey<O extends Person<?>> extends AURegKey<O> {}\n" |
| ); |
| |
| setUpWorkingCopy("/Bug495635/src/Person.java", |
| "public interface Person<O extends Person<O>> extends AURegObject, PersonKey<O> {}\n" |
| ); |
| assertProblems( |
| "Unexpected problems", |
| "----------\n" + |
| "----------\n" |
| ); |
| |
| String str = this.workingCopy.getSource(); |
| |
| int start = str.indexOf("PersonKey"); |
| int length = "PersonKey".length(); |
| |
| IJavaElement[] elements = this.workingCopy.codeSelect(start, length); |
| assertElementsEqual( |
| "Unexpected elements", |
| "PersonKey [in PersonKey.java [in <default> [in src [in Bug495635]]]]", |
| elements |
| ); |
| |
| } finally { |
| if (project != null) |
| deleteProject(project); |
| } |
| } |
| |
| public void testBug460491WithOldBinary() throws CoreException, InterruptedException, IOException { |
| IJavaProject project = null; |
| try { |
| project = createJavaProject("Bug460491", new String[] {"src"}, new String[] {"JCL18_LIB", this.ANNOTATION_LIB, testJarPath("bug460491-compiled-with-4.6.jar")}, "bin", "1.8"); |
| project.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); |
| |
| // bug460491-compiled-with-4.6.jar contains classes compiled with eclipse 4.6: |
| /*- |
| package test1; |
| |
| import org.eclipse.jdt.annotation.DefaultLocation; |
| import org.eclipse.jdt.annotation.NonNullByDefault; |
| import org.eclipse.jdt.annotation.Nullable; |
| |
| public abstract class Base<B> { |
| static public class Static { |
| public class Middle1 { |
| public class Middle2<M> { |
| public class Middle3 { |
| public class GenericInner<T> { |
| } |
| } |
| } |
| } |
| } |
| |
| @NonNullByDefault(DefaultLocation.PARAMETER) |
| public Object method( Static.Middle1.Middle2<Object>.Middle3.@Nullable GenericInner<String> nullable) { |
| return new Object(); |
| } |
| } |
| */ |
| |
| this.createFolder("/Bug460491/src/test2"); |
| String c2SourceString = |
| "package test2;\n" + |
| "\n" + |
| "import test1.Base;\n" + |
| "\n" + |
| "class Derived extends Base<Object> {\n" + |
| " void test() {\n" + |
| " method(null);\n" + |
| " }\n" + |
| "}\n"; |
| this.createFile( |
| "/Bug460491/src/test2/Derived.java", |
| c2SourceString); |
| |
| char[] c2SourceChars = c2SourceString.toCharArray(); |
| this.problemRequestor.initialize(c2SourceChars); |
| |
| getCompilationUnit("/Bug460491/src/test2/Derived.java").getWorkingCopy(this.wcOwner, null); |
| assertProblems( |
| "Unexpected problems", |
| "----------\n" + |
| "----------\n" |
| ); |
| } finally { |
| if (project != null) |
| deleteProject(project); |
| } |
| } |
| |
| public void testBug460491WithOldBinary_b() throws CoreException, InterruptedException, IOException { |
| IJavaProject project = null; |
| try { |
| project = createJavaProject("Bug460491", new String[] {"src"}, new String[] {"JCL18_LIB", this.ANNOTATION_LIB, testJarPath("bug460491b-compiled-with-4.6.jar")}, "bin", "1.8"); |
| project.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); |
| |
| // bug460491b-compiled-with-4.6.jar contains classes compiled with eclipse 4.6: |
| /*- |
| package test1; |
| |
| import org.eclipse.jdt.annotation.DefaultLocation; |
| import org.eclipse.jdt.annotation.NonNullByDefault; |
| import org.eclipse.jdt.annotation.Nullable; |
| |
| public abstract class Base<B> { |
| static public class Static1 { |
| public static class Static2<X> { |
| public class Middle1<M> { |
| public class Middle2 { |
| public class GenericInner<T> { |
| } |
| } |
| } |
| } |
| } |
| |
| @NonNullByDefault(DefaultLocation.PARAMETER) |
| public Object method( Static1.Static2<Exception>.Middle1<Object>.Middle2.@Nullable GenericInner<String> nullable) { |
| return new Object(); |
| } |
| } |
| */ |
| |
| this.createFolder("/Bug460491/src/test2"); |
| String c2SourceString = |
| "package test2;\n" + |
| "\n" + |
| "import test1.Base;\n" + |
| "\n" + |
| "class Derived extends Base<Object> {\n" + |
| " void testOK(Static1.Static2<Exception>.Middle1<Object>.Middle2.GenericInner<String> gi) {\n" + |
| " method(gi);\n" + |
| " }\n" + |
| " void testNOK(Static1.Static2<String>.Middle1<Object>.Middle2.GenericInner<String> gi) {\n" + |
| " method(gi);\n" + |
| " }\n" + |
| "}\n"; |
| this.createFile( |
| "/Bug460491/src/test2/Derived.java", |
| c2SourceString); |
| |
| char[] c2SourceChars = c2SourceString.toCharArray(); |
| this.problemRequestor.initialize(c2SourceChars); |
| |
| getCompilationUnit("/Bug460491/src/test2/Derived.java").getWorkingCopy(this.wcOwner, null); |
| assertProblems( |
| "Unexpected problems", |
| "----------\n" + |
| "1. ERROR in /Bug460491/src/test2/Derived.java (at line 10)\n" + |
| " method(gi);\n" + |
| " ^^^^^^\n" + |
| "The method method(Base.Static1.Static2<Exception>.Middle1<Object>.Middle2.GenericInner<String>) in the type Base<Object> is not applicable for the arguments (Base.Static1.Static2<String>.Middle1<Object>.Middle2.GenericInner<String>)\n" + |
| "----------\n" |
| ); |
| } finally { |
| if (project != null) |
| deleteProject(project); |
| } |
| } |
| |
| public void testTargetTypeUse() throws CoreException { |
| IJavaProject project = null; |
| try { |
| project = createJavaProject("TargetTypeUse", new String[] {"src"}, new String[] {"JCL18_LIB", this.ANNOTATION_LIB}, "bin", "1.8"); |
| IType nonNull = project.findType(NonNull.class.getName()); |
| IAnnotation annot = nonNull.getAnnotation(Target.class.getName()); |
| for (IMemberValuePair memberValuePair : annot.getMemberValuePairs()) { |
| if (memberValuePair.getValue().equals(ElementType.class.getName()+'.'+ElementType.TYPE_USE)) |
| return; |
| } |
| fail("TYPE_USE target not found"); |
| } finally { |
| if (project != null) |
| deleteProject(project); |
| } |
| } |
| public void testBug543304() throws Exception { |
| IJavaProject annotations = createJavaProject("Annotations", new String[] {"src"}, new String[] {"JCL18_LIB"}, "bin", "1.8"); |
| IJavaProject client = createJavaProject("Client", new String[] {"src"}, new String[] {"JCL17_LIB"}, "bin", "1.7"); |
| try { |
| createFolder("Annotations/src/p"); |
| createFile("Annotations/src/p/NonNull.java", |
| "package p;\n" + |
| "import java.lang.annotation.*;\n" + |
| "import static java.lang.annotation.ElementType.*;\n" + |
| "@Target({METHOD, PARAMETER, FIELD, TYPE_USE})\n" + |
| "public @interface NonNull {}\n"); |
| createFile("Annotations/src/p/Nullable.java", |
| "package p;\n" + |
| "import java.lang.annotation.*;\n" + |
| "import static java.lang.annotation.ElementType.*;\n" + |
| "@Target({METHOD, PARAMETER, FIELD, TYPE_USE})\n" + |
| "public @interface Nullable {}\n"); |
| |
| addClasspathEntry(client, JavaCore.newProjectEntry(annotations.getPath())); |
| client.setOption(JavaCore.COMPILER_NONNULL_ANNOTATION_NAME, "p.NonNull"); |
| client.setOption(JavaCore.COMPILER_NULLABLE_ANNOTATION_NAME, "p.Nullable"); |
| client.setOption(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); |
| |
| createFile("Client/src/Test.java", |
| "import p.*;\n" + |
| "public class Test {\n" + |
| " @Nullable int[] ints = null;\n" + |
| " public @NonNull Object foo(@NonNull byte[] data) {\n" + |
| " return data;\n" + |
| " }\n" + |
| "}\n"); |
| |
| getWorkspace().build(IncrementalProjectBuilder.FULL_BUILD, null); |
| IMarker[] markers = client.getProject().findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE); |
| assertMarkers("Unexpected markers", "", markers); |
| } finally { |
| deleteProject(annotations); |
| deleteProject(client); |
| } |
| } |
| } |