| /******************************************************************************* |
| * Copyright (c) 2014, 2015 GK Software AG, and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Stephan Herrmann - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.jdt.core.tests.model; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.Hashtable; |
| import java.util.Map; |
| |
| import junit.framework.Test; |
| |
| 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.IPath; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.jdt.core.IClasspathAttribute; |
| import org.eclipse.jdt.core.IClasspathContainer; |
| import org.eclipse.jdt.core.IClasspathEntry; |
| import org.eclipse.jdt.core.ICompilationUnit; |
| import org.eclipse.jdt.core.IJavaModelMarker; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IPackageFragment; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.compiler.IProblem; |
| import org.eclipse.jdt.core.dom.AST; |
| import org.eclipse.jdt.core.dom.CompilationUnit; |
| import org.eclipse.jdt.core.tests.util.AbstractCompilerTest; |
| import org.eclipse.jdt.core.tests.util.Util; |
| import org.eclipse.jdt.internal.core.ClasspathAttribute; |
| import org.osgi.framework.Bundle; |
| |
| @SuppressWarnings({"rawtypes", "unchecked"}) |
| public class ExternalAnnotations18Test extends ModifyingResourceTests { |
| |
| /** Bridge to hook the host JRE into the registered ContainerInitializer. */ |
| static class TestContainerInitializer implements ContainerInitializer.ITestInitializer { |
| |
| /** Use this container name in test projects. */ |
| private static final String TEST_CONTAINER_NAME = "org.eclipse.jdt.core.tests.model.TEST_CONTAINER"; |
| |
| static class TestContainer implements IClasspathContainer { |
| IPath path; |
| IClasspathEntry[] entries; |
| TestContainer(IPath path, IClasspathEntry[] entries){ |
| this.path = path; |
| this.entries = entries; |
| } |
| public IPath getPath() { return this.path; } |
| public IClasspathEntry[] getClasspathEntries() { return this.entries; } |
| public String getDescription() { return this.path.toString(); } |
| public int getKind() { return 0; } |
| } |
| |
| public void initialize(IPath containerPath, IJavaProject project) throws CoreException { |
| String[] jars = Util.getJavaClassLibs(); |
| IClasspathEntry[] entries = new IClasspathEntry[jars.length]; |
| for (int i = 0; i < jars.length; i++) |
| entries[i] = JavaCore.newLibraryEntry(new Path(jars[i]), null, null); |
| JavaCore.setClasspathContainer( |
| new Path(TEST_CONTAINER_NAME), |
| new IJavaProject[]{ project }, |
| new IClasspathContainer[] { new TestContainer(new Path(TEST_CONTAINER_NAME), entries) }, |
| null); |
| } |
| public boolean allowFailureContainer() { |
| return false; |
| } |
| } |
| |
| protected IJavaProject project; |
| protected IPackageFragmentRoot root; |
| protected String ANNOTATION_LIB; |
| protected final String compliance; |
| protected final String jclLib; |
| |
| protected static final String MY_MAP_CONTENT = |
| "package libs;\n" + |
| "\n" + |
| "public interface MyMap<K,V> {\n" + |
| " V get(Object key);\n" + |
| " V put(K key, V val);\n" + |
| " V remove(Object key);\n" + |
| "}\n"; |
| |
| public ExternalAnnotations18Test(String name) { |
| this(name, "1.8", "JCL18_LIB"); |
| } |
| |
| protected ExternalAnnotations18Test(String name, String compliance, String jclLib) { |
| super(name); |
| this.compliance = compliance; |
| this.jclLib = jclLib; |
| } |
| |
| // Use this static initializer to specify subset for tests |
| // All specified tests which do not belong to the class are skipped... |
| static { |
| // Names of tests to run: can be "testBugXXXX" or "BugXXXX") |
| // TESTS_PREFIX = "testLibsWithTypeParameters"; |
| // TESTS_NAMES = new String[] {"test3"}; |
| // TESTS_NUMBERS = new int[] { 23, 28, 38 }; |
| // TESTS_RANGE = new int[] { 21, 38 }; |
| } |
| public static Test suite() { |
| return buildModelTestSuite(ExternalAnnotations18Test.class, BYTECODE_DECLARATION_ORDER); |
| } |
| |
| public void setUpSuite() throws Exception { |
| super.setUpSuite(); |
| |
| Bundle[] bundles = getAnnotationBundles(); |
| File bundleFile = FileLocator.getBundleFile(bundles[0]); |
| this.ANNOTATION_LIB = bundleFile.isDirectory() ? bundleFile.getPath()+"/bin" : bundleFile.getPath(); |
| |
| // set up class path container bridging to the host JRE: |
| ContainerInitializer.setInitializer(new TestContainerInitializer()); |
| } |
| |
| /** |
| * @deprecated indirectly uses deprecated class PackageAdmin |
| */ |
| protected Bundle[] getAnnotationBundles() { |
| return org.eclipse.jdt.core.tests.Activator.getPackageAdmin().getBundles("org.eclipse.jdt.annotation", "[2.0.0,3.0.0)"); |
| } |
| |
| public void tearDownSuite() throws Exception { |
| super.tearDownSuite(); |
| ContainerInitializer.setInitializer(null); |
| } |
| |
| public String getSourceWorkspacePath() { |
| // we read individual projects from within this folder: |
| return super.getSourceWorkspacePath()+"/ExternalAnnotations18"; |
| } |
| |
| protected String getSourceWorkspacePathBase() { |
| return super.getSourceWorkspacePath(); |
| } |
| |
| void setupJavaProject(String name) throws CoreException, IOException { |
| this.project = setUpJavaProject(name, this.compliance); //$NON-NLS-1$ |
| addLibraryEntry(this.project, this.ANNOTATION_LIB, false); |
| Map options = this.project.getOptions(true); |
| options.put(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); |
| this.project.setOptions(options); |
| |
| IPackageFragmentRoot[] roots = this.project.getAllPackageFragmentRoots(); |
| int count = 0; |
| for (int i = 0, max = roots.length; i < max; i++) { |
| final IPackageFragmentRoot packageFragmentRoot = roots[i]; |
| switch(packageFragmentRoot.getKind()) { |
| case IPackageFragmentRoot.K_SOURCE : |
| count++; |
| if (this.root == null) { |
| this.root = packageFragmentRoot; |
| } |
| } |
| } |
| assertEquals("Wrong value", 1, count); //$NON-NLS-1$ |
| assertNotNull("Should not be null", this.root); //$NON-NLS-1$ |
| } |
| |
| void myCreateJavaProject(String name) throws CoreException { |
| this.project = createJavaProject(name, new String[]{"src"}, new String[]{this.jclLib}, null, null, "bin", null, null, null, this.compliance); |
| addLibraryEntry(this.project, this.ANNOTATION_LIB, false); |
| Map options = this.project.getOptions(true); |
| options.put(JavaCore.COMPILER_ANNOTATION_NULL_ANALYSIS, JavaCore.ENABLED); |
| this.project.setOptions(options); |
| |
| IPackageFragmentRoot[] roots = this.project.getAllPackageFragmentRoots(); |
| int count = 0; |
| for (int i = 0, max = roots.length; i < max; i++) { |
| final IPackageFragmentRoot packageFragmentRoot = roots[i]; |
| switch(packageFragmentRoot.getKind()) { |
| case IPackageFragmentRoot.K_SOURCE : |
| count++; |
| if (this.root == null) { |
| this.root = packageFragmentRoot; |
| } |
| } |
| } |
| assertEquals("Wrong value", 1, count); //$NON-NLS-1$ |
| assertNotNull("Should not be null", this.root); //$NON-NLS-1$ |
| } |
| |
| protected void tearDown() throws Exception { |
| if (this.project != null) |
| this.project.getProject().delete(true, true, null); |
| this.project = null; |
| this.root = null; |
| super.tearDown(); |
| } |
| |
| protected void addLibraryWithExternalAnnotations( |
| IJavaProject javaProject, |
| String jarName, |
| String externalAnnotationPath, |
| String[] pathAndContents, |
| Map options) throws CoreException, IOException |
| { |
| createLibrary(javaProject, jarName, "src.zip", pathAndContents, null, this.compliance, options); |
| String jarPath = '/' + javaProject.getProject().getName() + '/' + jarName; |
| IClasspathAttribute[] extraAttributes = new IClasspathAttribute[] { new ClasspathAttribute(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH, externalAnnotationPath) }; |
| IClasspathEntry entry = JavaCore.newLibraryEntry( |
| new Path(jarPath), |
| null/*src attach*/, |
| null/*src attach root*/, |
| null/*access rules*/, |
| extraAttributes, |
| false/*exported*/); |
| addClasspathEntry(this.project, entry); |
| } |
| |
| protected void createFileInProject(String projectRelativeFolder, String fileName, String content) throws CoreException { |
| String folderPath = this.project.getProject().getName()+'/'+projectRelativeFolder; |
| createFolder(folderPath); |
| createFile(folderPath+'/'+fileName, content); |
| } |
| |
| protected void assertNoMarkers(IMarker[] markers) throws CoreException { |
| for (int i = 0; i < markers.length; i++) |
| System.err.println("Unexpected marker: "+markers[i].getAttributes().entrySet()); |
| assertEquals("Number of markers", 0, markers.length); |
| } |
| |
| protected void assertNoProblems(IProblem[] problems) throws CoreException { |
| for (int i = 0; i < problems.length; i++) |
| System.err.println("Unexpected marker: "+problems[i]); |
| assertEquals("Number of markers", 0, problems.length); |
| } |
| |
| protected void assertProblems(IProblem[] problems, String[] messages, int[] lines) throws CoreException { |
| int nMatch = 0; |
| for (int i = 0; i < problems.length; i++) { |
| for (int j = 0; j < messages.length; j++) { |
| if (messages[j] == null) continue; |
| if (problems[i].toString().equals(messages[j]) |
| && problems[i].getSourceLineNumber() == lines[j]) { |
| messages[j] = null; |
| problems[i] = null; |
| nMatch++; |
| break; |
| } |
| } |
| } |
| for (int i = 0; i < problems.length; i++) { |
| if (problems[i] != null) |
| fail("Unexpected problem "+problems[i]+" at "+problems[i].getSourceLineNumber()); |
| } |
| for (int i = 0; i < messages.length; i++) { |
| if (messages[i] != null) |
| System.err.println("Unmatched problem "+messages[i]); |
| } |
| assertEquals("Number of problems", messages.length, nMatch); |
| } |
| |
| protected boolean hasJRE18() { |
| return ((AbstractCompilerTest.getPossibleComplianceLevels() & AbstractCompilerTest.F_1_8) != 0); |
| } |
| |
| /** Perform full build. */ |
| public void test1FullBuild() throws Exception { |
| setupJavaProject("Test1"); |
| addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots", new String[] { |
| "/UnannotatedLib/libs/MyMap.java", |
| MY_MAP_CONTENT |
| }, null); |
| this.project.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null); |
| IMarker[] markers = this.project.getProject().findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
| assertNoMarkers(markers); |
| } |
| |
| /** Perform full build, annotations are found relative to a variable. */ |
| public void test1FullBuildWithVariable() throws Exception { |
| setupJavaProject("Test1"); |
| JavaCore.setClasspathVariable("MY_PRJ_ROOT", this.project.getProject().getLocation(), null); |
| try { |
| addLibraryWithExternalAnnotations(this.project, "lib1.jar", "MY_PRJ_ROOT/annots", new String[] { |
| "/UnannotatedLib/libs/MyMap.java", |
| MY_MAP_CONTENT |
| }, null); |
| this.project.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null); |
| IMarker[] markers = this.project.getProject().findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
| assertNoMarkers(markers); |
| } finally { |
| JavaCore.removeClasspathVariable("MY_PRJ_ROOT", null); |
| } |
| } |
| |
| /** Reconcile an individual CU. */ |
| public void test1Reconcile() throws Exception { |
| setupJavaProject("Test1"); |
| addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots", new String[] { |
| "/UnannotatedLib/libs/MyMap.java", |
| MY_MAP_CONTENT |
| }, null); |
| IPackageFragment fragment = this.root.getPackageFragment("test1"); |
| ICompilationUnit unit = fragment.getCompilationUnit("Test1.java").getWorkingCopy(new NullProgressMonitor()); |
| CompilationUnit reconciled = unit.reconcile(AST.JLS8, true, null, new NullProgressMonitor()); |
| IProblem[] problems = reconciled.getProblems(); |
| assertNoProblems(problems); |
| } |
| |
| public void testLibs1() throws Exception { |
| myCreateJavaProject("TestLibs"); |
| addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots", new String[] { |
| "/UnannotatedLib/libs/Lib1.java", |
| "package libs;\n" + |
| "\n" + |
| "import java.util.Collection;\n" + |
| "import java.util.Iterator;\n" + |
| "\n" + |
| "public interface Lib1 {\n" + |
| " <T> Iterator<T> unconstrainedTypeArguments1(Collection<T> in);\n" + |
| " Iterator<String> unconstrainedTypeArguments2(Collection<String> in);\n" + |
| " <T> Iterator<? extends T> constrainedWildcards(Collection<? extends T> in);\n" + |
| " <T extends Collection<?>> T constrainedTypeParameter(T in);\n" + |
| "}\n" |
| }, null); |
| // annotations on type variables & class type in various positions: |
| createFileInProject("annots/libs", "Lib1.eea", |
| "class libs/Lib1\n" + |
| "\n" + |
| "unconstrainedTypeArguments1\n" + |
| " <T:Ljava/lang/Object;>(Ljava/util/Collection<TT;>;)Ljava/util/Iterator<TT;>;\n" + |
| " <T:Ljava/lang/Object;>(Ljava/util/Collection<T0T;>;)Ljava/util/Iterator<TT;>;\n" + // position: type argument |
| "\n" + |
| "unconstrainedTypeArguments2\n" + |
| " (Ljava/util/Collection<Ljava/lang/String;>;)Ljava/util/Iterator<Ljava/lang/String;>;\n" + |
| " (Ljava/util/Collection<Ljava/lang/String;>;)Ljava/util/Iterator<L1java/lang/String;>;\n" + // position: type argument bound (class type) |
| "constrainedWildcards\n" + |
| " <T:Ljava/lang/Object;>(Ljava/util/Collection<+TT;>;)Ljava/util/Iterator<+TT;>;\n" + |
| " <T:Ljava/lang/Object;>(Ljava/util/Collection<+T0T;>;)Ljava/util/Iterator<+T1T;>;\n" + // positions: wildcard bound |
| "constrainedTypeParameter\n" + |
| " <T::Ljava/util/Collection<*>;>(TT;)TT;\n" + |
| " <T::Ljava/util/Collection<*>;>(T0T;)T1T;\n"); // position: top-level type |
| IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("tests", true, null); |
| ICompilationUnit unit = fragment.createCompilationUnit("Test1.java", |
| "package tests;\n" + |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "\n" + |
| "import java.util.Collection;\n" + |
| "import java.util.Iterator;\n" + |
| "\n" + |
| "import libs.Lib1;\n" + |
| "\n" + |
| "public class Test1 {\n" + |
| " Iterator<@NonNull String> test1(Lib1 lib, Collection<@Nullable String> coll) {\n" + |
| " return lib.unconstrainedTypeArguments1(coll);\n" + |
| " }\n" + |
| " Iterator<@NonNull String> test2(Lib1 lib, Collection<@Nullable String> coll) {\n" + |
| " return lib.unconstrainedTypeArguments2(coll);\n" + |
| " }\n" + |
| " Iterator<? extends @NonNull String> test3(Lib1 lib, Collection<String> coll) {\n" + |
| " return lib.constrainedWildcards(coll);\n" + |
| " }\n" + |
| " @NonNull Collection<String> test4(Lib1 lib, @Nullable Collection<String> in) {\n" + |
| " return lib.constrainedTypeParameter(in);\n" + |
| " }\n" + |
| "}\n", |
| true, new NullProgressMonitor()).getWorkingCopy(new NullProgressMonitor()); |
| CompilationUnit reconciled = unit.reconcile(AST.JLS8, true, null, new NullProgressMonitor()); |
| IProblem[] problems = reconciled.getProblems(); |
| assertNoProblems(problems); |
| } |
| |
| public void testLibsWithWildcards() throws Exception { |
| myCreateJavaProject("TestLibs"); |
| addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots", new String[] { |
| "/UnannotatedLib/libs/Lib1.java", |
| "package libs;\n" + |
| "\n" + |
| "import java.util.Collection;\n" + |
| "import java.util.Iterator;\n" + |
| "\n" + |
| "public interface Lib1 {\n" + |
| " Iterator<?> unconstrainedWildcard1(Collection<?> in);\n" + |
| " Iterator<?> unconstrainedWildcard2(Collection<?> in);\n" + |
| " Iterator<? extends CharSequence> constrainedWildcard1(Collection<? extends CharSequence> in);\n" + |
| " Iterator<? super CharSequence> constrainedWildcard2(Collection<? super CharSequence> in);\n" + |
| "}\n" |
| }, null); |
| // annotations directly on a wildcard (*, +, -) |
| createFileInProject("annots/libs", "Lib1.eea", |
| "class libs/Lib1\n" + |
| "\n" + |
| "unconstrainedWildcard1\n" + |
| " (Ljava/util/Collection<*>;)Ljava/util/Iterator<*>;\n" + |
| " (Ljava/util/Collection<*>;)Ljava/util/Iterator<*1>;\n" + |
| "\n" + |
| "unconstrainedWildcard2\n" + |
| " (Ljava/util/Collection<*>;)Ljava/util/Iterator<*>;\n" + |
| " (Ljava/util/Collection<*>;)Ljava/util/Iterator<*0>;\n" + |
| "\n" + |
| "constrainedWildcard1\n" + |
| " (Ljava/util/Collection<+Ljava/lang/CharSequence;>;)Ljava/util/Iterator<+Ljava/lang/CharSequence;>;\n" + |
| " (Ljava/util/Collection<+Ljava/lang/CharSequence;>;)Ljava/util/Iterator<+0Ljava/lang/CharSequence;>;\n" + |
| "\n" + |
| "constrainedWildcard2\n" + |
| " (Ljava/util/Collection<-Ljava/lang/CharSequence;>;)Ljava/util/Iterator<-Ljava/lang/CharSequence;>;\n" + |
| " (Ljava/util/Collection<-Ljava/lang/CharSequence;>;)Ljava/util/Iterator<-0Ljava/lang/CharSequence;>;\n" + |
| "\n" + |
| "\n"); |
| IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("tests", true, null); |
| ICompilationUnit unit = fragment.createCompilationUnit("Test1.java", |
| "package tests;\n" + |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "\n" + |
| "import java.util.Collection;\n" + |
| "\n" + |
| "import libs.Lib1;\n" + |
| "\n" + |
| "public class Test1 {\n" + |
| " @NonNull Object test1(Lib1 lib, Collection<@Nullable String> coll) {\n" + |
| " return lib.unconstrainedWildcard1(coll).next();\n" + // OK |
| " }\n" + |
| " @NonNull Object test2(Lib1 lib, Collection<@Nullable String> coll) {\n" + |
| " return lib.unconstrainedWildcard2(coll).next();\n" + // return is nullable -> error |
| " }\n" + |
| " @NonNull CharSequence test3(Lib1 lib, Collection<@Nullable String> coll) {\n" + |
| " return lib.constrainedWildcard1(coll).next();\n" + // '@Nullable ? extends CharSequence' -> error |
| " }\n" + |
| " @NonNull Object test4(Lib1 lib, Collection<@Nullable CharSequence> coll) {\n" + |
| " return lib.constrainedWildcard2(coll).next();\n" + // return is '@Nullable ? super CharSequence' -> error |
| " }\n" + |
| "}\n", |
| true, new NullProgressMonitor()).getWorkingCopy(new NullProgressMonitor()); |
| CompilationUnit reconciled = unit.reconcile(AST.JLS8, true, null, new NullProgressMonitor()); |
| IProblem[] problems = reconciled.getProblems(); |
| assertProblems(problems, new String[] { |
| "Pb(953) Null type mismatch (type annotations): required '@NonNull Object' but this expression has type '@Nullable capture#of ?'", |
| "Pb(953) Null type mismatch (type annotations): required '@NonNull CharSequence' but this expression has type '@Nullable capture#of ? extends CharSequence'", |
| "Pb(953) Null type mismatch (type annotations): required '@NonNull Object' but this expression has type '@Nullable capture#of ? super CharSequence'" |
| }, new int[] { 13, 16, 19 }); |
| } |
| |
| public void testLibsWithArrays() throws Exception { |
| myCreateJavaProject("TestLibs"); |
| addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots", new String[] { |
| "/UnannotatedLib/libs/Lib1.java", |
| "package libs;\n" + |
| "\n" + |
| "import java.util.Collection;\n" + |
| "import java.util.Iterator;\n" + |
| "\n" + |
| "public interface Lib1 {\n" + |
| " String[] constraintArrayTop(String[] in);\n" + |
| " String[] constraintArrayFull(String[] in);\n" + |
| " String[][] constraintDeep(String[][] in);\n" + |
| "}\n" |
| }, null); |
| createFileInProject("annots/libs", "Lib1.eea", |
| "class libs/Lib1\n" + |
| "\n" + |
| "constraintArrayTop\n" + |
| " ([Ljava/lang/String;)[Ljava/lang/String;\n" + |
| " ([0Ljava/lang/String;)[1Ljava/lang/String;\n" + |
| "\n" + |
| "constraintArrayFull\n" + |
| " ([Ljava/lang/String;)[Ljava/lang/String;\n" + |
| " ([0L0java/lang/String;)[1L1java/lang/String;\n" + |
| "\n" + |
| "constraintDeep\n" + |
| " ([[Ljava/lang/String;)[[Ljava/lang/String;\n" + |
| " ([0[1L0java/lang/String;)[1[0L1java/lang/String;\n"); |
| IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("tests", true, null); |
| ICompilationUnit unit = fragment.createCompilationUnit("Test1.java", |
| "package tests;\n" + |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "\n" + |
| "import libs.Lib1;\n" + |
| "\n" + |
| "public class Test1 {\n" + |
| " String @NonNull[] test1(Lib1 lib, String @Nullable[] ok, String[] nok) {\n" + |
| " lib.constraintArrayTop(nok);\n" + |
| " return lib.constraintArrayTop(ok);\n" + |
| " }\n" + |
| " @NonNull String @NonNull[] test2(Lib1 lib, @Nullable String @Nullable[] ok, String[] nok) {\n" + |
| " lib.constraintArrayFull(nok);\n" + |
| " return lib.constraintArrayFull(ok);\n" + |
| " }\n" + |
| " @NonNull String @NonNull[] @Nullable[] test3(Lib1 lib, @Nullable String @Nullable[] @NonNull[] ok, String[][] nok) {\n" + |
| " lib.constraintDeep(nok);\n" + |
| " return lib.constraintDeep(ok);\n" + |
| " }\n" + |
| "}\n", |
| true, new NullProgressMonitor()).getWorkingCopy(new NullProgressMonitor()); |
| CompilationUnit reconciled = unit.reconcile(AST.JLS8, true, null, new NullProgressMonitor()); |
| IProblem[] problems = reconciled.getProblems(); |
| assertProblems(problems, new String[] { |
| "Pb(955) Null type safety (type annotations): The expression of type 'String[]' needs unchecked conversion to conform to '@Nullable String @Nullable[]'", |
| "Pb(955) Null type safety (type annotations): The expression of type 'String[][]' needs unchecked conversion to conform to '@Nullable String @Nullable[] @NonNull[]'", |
| }, new int[] { 12, 16 }); |
| } |
| |
| public void testLibsWithFields() throws Exception { |
| myCreateJavaProject("TestLibs"); |
| addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots", new String[] { |
| "/UnannotatedLib/libs/Lib1.java", |
| "package libs;\n" + |
| "\n" + |
| "public interface Lib1 {\n" + |
| " String one = \"1\";\n" + |
| " String none = null;\n" + |
| "}\n" |
| }, null); |
| createFileInProject("annots/libs", "Lib1.eea", |
| "class libs/Lib1\n" + |
| "\n" + |
| "one\n" + |
| " Ljava/lang/String;\n" + |
| " L1java/lang/String;\n" + |
| "\n" + |
| "none\n" + |
| " Ljava/lang/String;\n" + |
| " L0java/lang/String;\n" + |
| "\n"); |
| IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("tests", true, null); |
| ICompilationUnit unit = fragment.createCompilationUnit("Test1.java", |
| "package tests;\n" + |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "\n" + |
| "import libs.Lib1;\n" + |
| "\n" + |
| "public class Test1 {\n" + |
| " @NonNull String test0() {\n" + |
| " return Lib1.none;\n" + |
| " }\n" + |
| " @NonNull String test1() {\n" + |
| " return Lib1.one;\n" + |
| " }\n" + |
| "}\n", |
| true, new NullProgressMonitor()).getWorkingCopy(new NullProgressMonitor()); |
| CompilationUnit reconciled = unit.reconcile(AST.JLS8, true, null, new NullProgressMonitor()); |
| IProblem[] problems = reconciled.getProblems(); |
| assertProblems(problems, new String[] { |
| "Pb(953) Null type mismatch (type annotations): required '@NonNull String' but this expression has type '@Nullable String'", |
| }, new int[] { 8 }); |
| } |
| |
| public void testLibsWithFieldsZipped() throws Exception { |
| myCreateJavaProject("TestLibs"); |
| addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots.zip", new String[] { |
| "/UnannotatedLib/libs/Lib1.java", |
| "package libs;\n" + |
| "\n" + |
| "public interface Lib1 {\n" + |
| " String one = \"1\";\n" + |
| " String none = null;\n" + |
| "}\n" |
| }, null); |
| Util.createSourceZip( |
| new String[] { |
| "libs/Lib1.eea", |
| "class libs/Lib1\n" + |
| "\n" + |
| "one\n" + |
| " Ljava/lang/String;\n" + |
| " L1java/lang/String;\n" + |
| "\n" + |
| "none\n" + |
| " Ljava/lang/String;\n" + |
| " L0java/lang/String;\n" + |
| "\n" |
| }, |
| this.project.getProject().getLocation().toString()+"/annots.zip"); |
| this.project.getProject().refreshLocal(IResource.DEPTH_INFINITE, null); |
| |
| IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("tests", true, null); |
| ICompilationUnit unit = fragment.createCompilationUnit("Test1.java", |
| "package tests;\n" + |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "\n" + |
| "import libs.Lib1;\n" + |
| "\n" + |
| "public class Test1 {\n" + |
| " @NonNull String test0() {\n" + |
| " return Lib1.none;\n" + |
| " }\n" + |
| " @NonNull String test1() {\n" + |
| " return Lib1.one;\n" + |
| " }\n" + |
| "}\n", |
| true, new NullProgressMonitor()).getWorkingCopy(new NullProgressMonitor()); |
| CompilationUnit reconciled = unit.reconcile(AST.JLS8, true, null, new NullProgressMonitor()); |
| IProblem[] problems = reconciled.getProblems(); |
| assertProblems(problems, new String[] { |
| "Pb(953) Null type mismatch (type annotations): required '@NonNull String' but this expression has type '@Nullable String'", |
| }, new int[] { 8 }); |
| } |
| |
| public void testLibsWithFieldsExternalZipped() throws Exception { |
| myCreateJavaProject("TestLibs"); |
| String zipPath = Util.getOutputDirectory() + '/' + "annots.zip"; |
| addLibraryWithExternalAnnotations(this.project, "lib1.jar", zipPath, new String[] { |
| "/UnannotatedLib/libs/Lib1.java", |
| "package libs;\n" + |
| "\n" + |
| "public interface Lib1 {\n" + |
| " String one = \"1\";\n" + |
| " String none = null;\n" + |
| "}\n" |
| }, null); |
| Util.createSourceZip( |
| new String[] { |
| "libs/Lib1.eea", |
| "class libs/Lib1\n" + |
| "\n" + |
| "one\n" + |
| " Ljava/lang/String;\n" + |
| " L1java/lang/String;\n" + |
| "\n" + |
| "none\n" + |
| " Ljava/lang/String;\n" + |
| " L0java/lang/String;\n" + |
| "\n" |
| }, |
| zipPath); |
| |
| IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("tests", true, null); |
| ICompilationUnit unit = fragment.createCompilationUnit("Test1.java", |
| "package tests;\n" + |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "\n" + |
| "import libs.Lib1;\n" + |
| "\n" + |
| "public class Test1 {\n" + |
| " @NonNull String test0() {\n" + |
| " return Lib1.none;\n" + |
| " }\n" + |
| " @NonNull String test1() {\n" + |
| " return Lib1.one;\n" + |
| " }\n" + |
| "}\n", |
| true, new NullProgressMonitor()).getWorkingCopy(new NullProgressMonitor()); |
| CompilationUnit reconciled = unit.reconcile(AST.JLS8, true, null, new NullProgressMonitor()); |
| IProblem[] problems = reconciled.getProblems(); |
| assertProblems(problems, new String[] { |
| "Pb(953) Null type mismatch (type annotations): required '@NonNull String' but this expression has type '@Nullable String'", |
| }, new int[] { 8 }); |
| } |
| |
| public void testLibsWithTypeParameters() throws Exception { |
| myCreateJavaProject("TestLibs"); |
| addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots", new String[] { |
| "/UnannotatedLib/libs/Lib1.java", |
| "package libs;\n" + |
| "\n" + |
| "public interface Lib1<U,V,W extends U> {\n" + |
| " U getU();\n" + |
| " V getV();\n" + |
| " W getW();\n" + |
| " <X,Y extends CharSequence> Y fun(X x);\n" + |
| "}\n" |
| }, null); |
| createFileInProject("annots/libs", "Lib1.eea", |
| "class libs/Lib1\n" + |
| " <U:Ljava/lang/Object;V:Ljava/lang/Object;W:TU;>\n" + |
| " <0U:Ljava/lang/Object;1V:Ljava/lang/Object;W:T1U;>\n" + |
| "\n" + |
| "fun\n" + |
| " <X:Ljava/lang/Object;Y::Ljava/lang/CharSequence;>(TX;)TY;\n" + |
| " <1X:Ljava/lang/Object;Y::L1java/lang/CharSequence;>(TX;)TY;\n" + |
| "\n"); |
| IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("tests", true, null); |
| ICompilationUnit unit = fragment.createCompilationUnit("Test1.java", |
| "package tests;\n" + |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "\n" + |
| "import libs.Lib1;\n" + |
| "\n" + |
| "public class Test1 {\n" + |
| " @NonNull String test0(Lib1<@Nullable String,@NonNull String,@NonNull String> l) {\n" + |
| " return l.getU();\n" + // mismatch: U is nullable |
| " }\n" + |
| " @NonNull String test1(Lib1<@Nullable String,@NonNull String,@NonNull String> l) {\n" + |
| " return l.getV();\n" + // OK: V is nonnull |
| " }\n" + |
| " @NonNull String test2(Lib1<@Nullable String,@NonNull String,@NonNull String> l) {\n" + |
| " return l.getW();\n" + // OK: V is nonnull |
| " }\n" + |
| " Lib1<@NonNull String, @NonNull String, @NonNull String> f1;\n" + // mismatch at U |
| " Lib1<@Nullable String, String, @NonNull String> f2;\n" + // mismatch at V |
| " Lib1<@Nullable String, @NonNull String, @Nullable String> f3;\n" + // mismatch at W |
| " @Nullable String test3(Lib1<@Nullable String,@NonNull String,@NonNull String> l) {\n" + |
| " return l.<@Nullable String,@Nullable String>fun(\"\");\n" + // mismatches at X and Y |
| " }\n" + |
| "}\n", |
| true, new NullProgressMonitor()).getWorkingCopy(new NullProgressMonitor()); |
| CompilationUnit reconciled = unit.reconcile(AST.JLS8, true, null, new NullProgressMonitor()); |
| IProblem[] problems = reconciled.getProblems(); |
| assertProblems(problems, new String[] { |
| "Pb(953) Null type mismatch (type annotations): required '@NonNull String' but this expression has type '@Nullable String'", |
| "Pb(964) Null constraint mismatch: The type '@NonNull String' is not a valid substitute for the type parameter '@Nullable U extends Object'", |
| "Pb(964) Null constraint mismatch: The type 'String' is not a valid substitute for the type parameter '@NonNull V extends Object'", |
| "Pb(964) Null constraint mismatch: The type '@Nullable String' is not a valid substitute for the type parameter '@NonNull W extends @NonNull U extends Object'", // FIXME(stephan): @NonNull before W is bogus, see https://bugs.eclipse.org/456532 |
| "Pb(964) Null constraint mismatch: The type '@Nullable String' is not a valid substitute for the type parameter '@NonNull X extends Object'", |
| "Pb(964) Null constraint mismatch: The type '@Nullable String' is not a valid substitute for the type parameter '@NonNull Y extends @NonNull CharSequence'", // FIXME(see above) |
| }, new int[] { 8, 16, 17, 18, 20, 20 }); |
| } |
| |
| public void testLibsWithTypeArgOfSuper() throws Exception { |
| myCreateJavaProject("TestLibs"); |
| addLibraryWithExternalAnnotations(this.project, "lib1.jar", "annots", new String[] { |
| "/UnannotatedLib/libs/LibSuper.java", |
| "package libs;\n" + |
| "\n" + |
| "public interface LibSuper<T,U> {\n" + |
| " U apply(T t);\n" + |
| "}\n", |
| "/UnannotatedLib/libs/Lib1.java", |
| "package libs;\n" + |
| "\n" + |
| "public interface Lib1 extends LibSuper<String,Exception> {\n" + |
| "}\n" |
| }, null); |
| createFileInProject("annots/libs", "Lib1.eea", |
| "class libs/Lib1\n" + |
| "super libs/LibSuper\n" + |
| " <Ljava/lang/String;Ljava/lang/Exception;>\n" + |
| " <L1java/lang/String;L0java/lang/Exception;>\n" + |
| "\n"); |
| IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("tests", true, null); |
| ICompilationUnit unit = fragment.createCompilationUnit("Test1.java", |
| "package tests;\n" + |
| "import org.eclipse.jdt.annotation.*;\n" + |
| "\n" + |
| "import libs.Lib1;\n" + |
| "\n" + |
| "public class Test1 {\n" + |
| " @NonNull Exception test0(Lib1 lib, @Nullable String str) {\n" + |
| " return lib\n" + |
| " .apply(str);\n" + |
| " }\n" + |
| "}\n", |
| true, new NullProgressMonitor()).getWorkingCopy(new NullProgressMonitor()); |
| CompilationUnit reconciled = unit.reconcile(AST.JLS8, true, null, new NullProgressMonitor()); |
| IProblem[] problems = reconciled.getProblems(); |
| assertProblems(problems, new String[] { |
| "Pb(953) Null type mismatch (type annotations): required '@NonNull Exception' but this expression has type '@Nullable Exception'", |
| "Pb(953) Null type mismatch (type annotations): required '@NonNull String' but this expression has type '@Nullable String'", |
| }, new int[] { 8, 9 }); |
| } |
| |
| /** Project with real JRE. */ |
| public void test2() throws Exception { |
| // library type used: j.u.Map (no need for JRE8) |
| Hashtable options = JavaCore.getOptions(); |
| try { |
| setupJavaProject("Test2"); |
| this.project.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null); |
| IMarker[] markers = this.project.getProject().findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
| assertNoMarkers(markers); |
| } finally { |
| // project using a full JRE container initializes global options to 1.8 -- must reset now: |
| JavaCore.setOptions(options); |
| } |
| } |
| |
| /** Project with real JRE8. |
| * More interesting work with generics |
| * .classpath uses var TESTWORK for path to external annotations. |
| */ |
| public void test3() throws Exception { |
| if (!hasJRE18()) { |
| System.out.println("Skipping ExternalAnnotations18Test.test3(), needs JRE8"); |
| return; |
| } |
| final String TESTWORK_VAR_NAME = "TESTWORK"; |
| JavaCore.setClasspathVariable(TESTWORK_VAR_NAME, new Path(getSourceWorkspacePath()), null); |
| Hashtable options = JavaCore.getOptions(); |
| try { |
| setupJavaProject("Test3"); |
| this.project.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null); |
| IMarker[] markers = this.project.getProject().findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
| assertNoMarkers(markers); |
| } finally { |
| // project using a full JRE container initializes global options to 1.8 -- must reset now: |
| JavaCore.setOptions(options); |
| JavaCore.removeClasspathVariable(TESTWORK_VAR_NAME, null); |
| } |
| } |
| |
| } |