diff options
| author | Stephan Herrmann | 2016-02-25 23:22:21 +0000 |
|---|---|---|
| committer | Stephan Herrmann | 2016-02-28 16:24:55 +0000 |
| commit | c96202cbcb2ef06c10430e9d0f011fe30e627a82 (patch) | |
| tree | cf91dd0d069e7c3777d45b63fe687e874a359d67 | |
| parent | a620430067d64a8c379ef9bfad63016ee0485c9e (diff) | |
| download | eclipse.jdt.core-c96202cbcb2ef06c10430e9d0f011fe30e627a82.tar.gz eclipse.jdt.core-c96202cbcb2ef06c10430e9d0f011fe30e627a82.tar.xz eclipse.jdt.core-c96202cbcb2ef06c10430e9d0f011fe30e627a82.zip | |
Bug 488494: [null] external annotations should apply to project
dependencies, too
Change-Id: I130fd707e7eaf20779518d118702e9fd3873ae09
31 files changed, 650 insertions, 63 deletions
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java index 19c83637fe..0b4fd0544e 100644 --- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java +++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/model/ExternalAnnotations18Test.java @@ -249,6 +249,22 @@ public class ExternalAnnotations18Test extends ModifyingResourceTests { addClasspathEntry(this.project, entry); } + protected void addProjectDependencyWithExternalAnnotations( + IJavaProject javaProject, + String referencedProjectName, + String externalAnnotationPath, + Map options) throws CoreException, IOException + { + IClasspathAttribute[] extraAttributes = new IClasspathAttribute[] { new ClasspathAttribute(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH, externalAnnotationPath) }; + IClasspathEntry entry = JavaCore.newProjectEntry( + new Path(referencedProjectName), + null/*access rules*/, + false/*combine 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); @@ -1419,4 +1435,121 @@ public class ExternalAnnotations18Test extends ModifyingResourceTests { Platform.removeLogListener(listener); } } + + /** Lib exists as workspace project. Perform full build. */ + public void testProjectDependencyFullBuild() throws Exception { + try { + setupJavaProject("Lib"); + this.project.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null); + + setupJavaProject("Test1"); + addProjectDependencyWithExternalAnnotations(this.project, "/Lib", "annots", 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 { + deleteProject("Lib"); + } + } + + /** Lib exists as workspace project. Reconcile an individual CU. */ + public void testProjectDependencyReconcile1() throws Exception { + try { + setupJavaProject("Lib"); + this.project.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null); + this.root = null; // prepare to get the root from project Test1 + + setupJavaProject("Test1"); + addProjectDependencyWithExternalAnnotations(this.project, "/Lib", "annots", 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); + } finally { + deleteProject("Lib"); + } + } + + /** Lib exists as workspace project. Type-Annotations in zip file. Reconcile an individual CU. */ + public void testProjectDependencyReconcile2() throws Exception { + try { + setupJavaProject("Lib"); + this.project.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null); + this.root = null; // prepare to get the root from project Test1 + + setupJavaProject("Test3b"); + Util.createSourceZip( + new String[] { + "libs/MyFunction.eea", + "class libs/MyFunction\n" + + " <T:R:>\n" + + "\n" + + "compose\n" + + " <V:Ljava/lang/Object;>(Llibs/MyFunction<-TV;+TT;>;)Llibs/MyFunction<TV;TR;>;\n" + + " <V:Ljava/lang/Object;>(Llibs/MyFunction<-TV;+T0T;>;)Llibs/MyFunction<TV;TR;>;\n" + + "\n", + "libs/Arrays.eea", + "class libs/Arrays\n" + + "\n" + + "array\n" + + " [Ljava/lang/String;\n" + + " [1L0java/lang/String;\n" + + "\n" + + "getArray\n" + + " ()[[Ljava/lang/String;\n" + + " ()[0[1L0java/lang/String;\n" + }, + this.project.getProject().getLocation().toString()+"/annots.zip"); + this.project.getProject().refreshLocal(1, new NullProgressMonitor()); + + addProjectDependencyWithExternalAnnotations(this.project, "/Lib", "annots.zip", null); + IPackageFragment fragment = this.root.getPackageFragment("test1"); + ICompilationUnit unit = fragment.getCompilationUnit("Reconcile2.java").getWorkingCopy(new NullProgressMonitor()); + CompilationUnit reconciled = unit.reconcile(AST.JLS8, true, null, new NullProgressMonitor()); + IProblem[] problems = reconciled.getProblems(); + assertNoProblems(problems); + } finally { + deleteProject("Lib"); + } + } + + /** Lib exists as workspace project. Invocations conflict with type parameter constraints. Reconcile an individual CU. */ + public void testProjectDependencyReconcile3() throws Exception { + try { + setupJavaProject("Lib"); + this.project.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null); + this.root = null; // prepare to get the root from project Test1 + + setupJavaProject("Test3b"); + Util.createSourceZip( + new String[] { + "libs/MyFunction.eea", + "class libs/MyFunction\n" + + " <T:R:>\n" + + " <T:1R:>\n" + + "\n" + + "compose\n" + + " <V:Ljava/lang/Object;>(Llibs/MyFunction<-TV;+TT;>;)Llibs/MyFunction<TV;TR;>;\n" + + " <1V:Ljava/lang/Object;>(Llibs/MyFunction<-TV;+TT;>;)Llibs/MyFunction<TV;TR;>;\n" + + "\n", + }, + this.project.getProject().getLocation().toString()+"/annots.zip"); + this.project.getProject().refreshLocal(1, new NullProgressMonitor()); + + addProjectDependencyWithExternalAnnotations(this.project, "/Lib", "annots.zip", null); + IPackageFragment fragment = this.root.getPackageFragment("test1"); + ICompilationUnit unit = fragment.getCompilationUnit("Reconcile3.java").getWorkingCopy(new NullProgressMonitor()); + CompilationUnit reconciled = unit.reconcile(AST.JLS8, true, null, new NullProgressMonitor()); + for (IProblem iProblem : reconciled.getProblems()) { + System.out.println(iProblem); + } + assertProblems(reconciled.getProblems(), new String[] { + "Pb(964) Null constraint mismatch: The type '@Nullable B' is not a valid substitute for the type parameter '@NonNull R'", + "Pb(964) Null constraint mismatch: The type '@Nullable String' is not a valid substitute for the type parameter '@NonNull V'", + }, new int[] { 12, 17 }); + } finally { + deleteProject("Lib"); + } + } } diff --git a/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Lib/.classpath b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Lib/.classpath new file mode 100644 index 0000000000..f8a6de7d3b --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Lib/.classpath @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="var" path="JCL18_LIB"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Lib/.project b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Lib/.project new file mode 100644 index 0000000000..b68234a6fe --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Lib/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>Lib</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Lib/src/libs/Arrays.java b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Lib/src/libs/Arrays.java new file mode 100644 index 0000000000..29b7e159b8 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Lib/src/libs/Arrays.java @@ -0,0 +1,6 @@ +package libs; + +public class Arrays { + public String[][] getArray() { return null; } + public static final String[] array = new String[1] { null } +}
\ No newline at end of file diff --git a/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Lib/src/libs/MyFunction.java b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Lib/src/libs/MyFunction.java new file mode 100644 index 0000000000..6f7bff6437 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Lib/src/libs/MyFunction.java @@ -0,0 +1,10 @@ +package libs; + +public interface MyFunction<T,R> { + + R apply(T t); + + default <V> MyFunction<V, R> compose(MyFunction<? super V, ? extends T> before) { + return (V v) -> apply(before.apply(v)); + } +}
\ No newline at end of file diff --git a/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Lib/src/libs/MyMap.java b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Lib/src/libs/MyMap.java new file mode 100644 index 0000000000..ad51f8d971 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Lib/src/libs/MyMap.java @@ -0,0 +1,6 @@ +package libs; +public interface MyMap<K,V> { + V get(Object key); + V put(K key, V val); + V remove(Object key); +} diff --git a/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Test3b/.classpath b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Test3b/.classpath new file mode 100644 index 0000000000..1574d24895 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Test3b/.classpath @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.core.tests.model.TEST_CONTAINER"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Test3b/.project b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Test3b/.project new file mode 100644 index 0000000000..ea7fde1523 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Test3b/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>Test3</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Test3b/src/test1/Reconcile2.java b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Test3b/src/test1/Reconcile2.java new file mode 100644 index 0000000000..385d7cb4c9 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Test3b/src/test1/Reconcile2.java @@ -0,0 +1,24 @@ +package test1; + +import org.eclipse.jdt.annotation.*; +import libs.*; + + +class A {} +class B {} +class C {} + +@NonNullByDefault +public class Reconcile2 { + C test(MyFunction<A,@Nullable B> f1, MyFunction<B,C> f2, A a) { + return f2.compose(f1).apply(a); // actually incompatible, but we tweak compose to pretend it's compatible + } + + void test2(Arrays lib) { + @Nullable String[]@NonNull[] arr = lib.getArray(); + if (arr == null) + throw new NullPointerException(); // not dead code + @Nullable String @NonNull[] arr2 = Arrays.array; + Arrays.array[1] = null; + } +}
\ No newline at end of file diff --git a/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Test3b/src/test1/Reconcile3.java b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Test3b/src/test1/Reconcile3.java new file mode 100644 index 0000000000..7a777c0d85 --- /dev/null +++ b/org.eclipse.jdt.core.tests.model/workspace/ExternalAnnotations18/Test3b/src/test1/Reconcile3.java @@ -0,0 +1,19 @@ +package test1; + +import org.eclipse.jdt.annotation.*; +import libs.*; + +class A {} +class B {} +class C {} + +@NonNullByDefault +public class Reconcile3 { + void test1(MyFunction<A,@Nullable B> f1) { + // nothing + } + + void test2(MyFunction<A,@NonNull B> f2a, MyFunction<@Nullable String,@NonNull A> f2b) { + f2a.<@Nullable String>compose(f2b); + } +}
\ No newline at end of file diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathDirectory.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathDirectory.java index e94ec90857..c44d210567 100644 --- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathDirectory.java +++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathDirectory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation 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 @@ -160,7 +160,7 @@ public NameEnvironmentAnswer findSecondaryInClass(char[] typeName, String qualif public boolean hasAnnotationFileFor(String qualifiedTypeName) { int pos = qualifiedTypeName.lastIndexOf('/'); if (pos != -1 && (pos + 1 < qualifiedTypeName.length())) { - String fileName = qualifiedTypeName.substring(pos + 1) + '.' + ExternalAnnotationProvider.ANNOTION_FILE_EXTENSION; + String fileName = qualifiedTypeName.substring(pos + 1) + ExternalAnnotationProvider.ANNOTATION_FILE_SUFFIX; return doesFileExist(fileName, qualifiedTypeName.substring(0, pos)); } return false; diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJar.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJar.java index 8d56c0a91b..77ad9037c3 100644 --- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJar.java +++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/ClasspathJar.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation 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 @@ -127,7 +127,7 @@ public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageN } @Override public boolean hasAnnotationFileFor(String qualifiedTypeName) { - return this.zipFile.getEntry(qualifiedTypeName+'.'+ExternalAnnotationProvider.ANNOTION_FILE_EXTENSION) != null; + return this.zipFile.getEntry(qualifiedTypeName+ExternalAnnotationProvider.ANNOTATION_FILE_SUFFIX) != null; } public char[][][] findTypeNames(String qualifiedPackageName) { if (!isPackage(qualifiedPackageName)) diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java index cbb2bc1cab..f58c49c5c4 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/ExternalAnnotationProvider.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014, 2015 GK Software AG. + * Copyright (c) 2014, 2016 GK Software AG. * 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 @@ -30,7 +30,7 @@ import org.eclipse.jdt.internal.compiler.util.Util; public class ExternalAnnotationProvider { - public static final String ANNOTION_FILE_EXTENSION= "eea"; //$NON-NLS-1$ + public static final String ANNOTATION_FILE_EXTENSION= "eea"; //$NON-NLS-1$ public static final String CLASS_PREFIX = "class "; //$NON-NLS-1$ public static final String SUPER_PREFIX = "super "; //$NON-NLS-1$ @@ -46,7 +46,7 @@ public class ExternalAnnotationProvider { */ public static final char NO_ANNOTATION = '@'; - static final String ANNOTATION_FILE_SUFFIX = ".eea"; //$NON-NLS-1$ + public static final String ANNOTATION_FILE_SUFFIX = ".eea"; //$NON-NLS-1$ private static final String TYPE_PARAMETER_PREFIX = " <"; //$NON-NLS-1$ @@ -69,8 +69,7 @@ public class ExternalAnnotationProvider { } private void initialize(InputStream input) throws IOException { - LineNumberReader reader = new LineNumberReader(new InputStreamReader(input)); - try { + try (LineNumberReader reader = new LineNumberReader(new InputStreamReader(input))) { assertClassHeader(reader.readLine(), this.typeName); String line; @@ -137,8 +136,6 @@ public class ExternalAnnotationProvider { this.fieldAnnotationSources.put(selector+':'+rawSig, annotSig); } } while (((line = pendingLine) != null) || (line = reader.readLine()) != null); - } finally { - reader.close(); } } @@ -402,7 +399,7 @@ public class ExternalAnnotationProvider { } } } - return null; + return NO_ANNOTATIONS; } } @@ -441,6 +438,19 @@ public class ExternalAnnotationProvider { if ((depth == 0) && (i +1 < length) && (this.source[i+1] != Util.C_COLON)) pendingVariable = true; break; + case Util.C_COLON : + if (depth == 0) + pendingVariable = true; // end of variable name + // skip optional bound ReferenceTypeSignature + i++; // peek next + while (i < length && this.source[i] == Util.C_ARRAY) + i++; + if (i < length && this.source[i] == Util.C_RESOLVED) { + while (i < length && this.source[i] != Util.C_NAME_END) + i++; + } + i--; // unget + break; default: if (pendingVariable) { pendingVariable = false; diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/NonNullDefaultAwareTypeAnnotationWalker.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/NonNullDefaultAwareTypeAnnotationWalker.java index 9fa7cbf580..f6092ac9cd 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/NonNullDefaultAwareTypeAnnotationWalker.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/classfmt/NonNullDefaultAwareTypeAnnotationWalker.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2014 GK Software AG. + * Copyright (c) 2014, 2016 GK Software AG. * 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 @@ -176,7 +176,7 @@ public class NonNullDefaultAwareTypeAnnotationWalker extends TypeAnnotationWalke @Override public IBinaryAnnotation[] getAnnotationsAtCursor(int currentTypeId) { - IBinaryAnnotation[] normalAnnotations = this.isEmpty ? null : super.getAnnotationsAtCursor(currentTypeId); + IBinaryAnnotation[] normalAnnotations = this.isEmpty ? NO_ANNOTATIONS : super.getAnnotationsAtCursor(currentTypeId); if (this.atDefaultLocation && !(currentTypeId == -1) && // never apply default on type variable use or wildcard !(this.atTypeBound && currentTypeId == TypeIds.T_JavaLangObject)) // for CLIMB-to-top consider a j.l.Object type bound as no explicit type bound diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/NameEnvironmentAnswer.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/NameEnvironmentAnswer.java index f43f037784..bc79c2f51f 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/NameEnvironmentAnswer.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/NameEnvironmentAnswer.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation 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 @@ -17,6 +17,7 @@ public class NameEnvironmentAnswer { ICompilationUnit compilationUnit; ISourceType[] sourceTypes; AccessRestriction accessRestriction; + String externalAnnotationPath; // should be an absolute file system path public NameEnvironmentAnswer(IBinaryType binaryType, AccessRestriction accessRestriction) { this.binaryType = binaryType; @@ -28,9 +29,10 @@ public class NameEnvironmentAnswer { this.accessRestriction = accessRestriction; } - public NameEnvironmentAnswer(ISourceType[] sourceTypes, AccessRestriction accessRestriction) { + public NameEnvironmentAnswer(ISourceType[] sourceTypes, AccessRestriction accessRestriction, String externalAnnotationPath) { this.sourceTypes = sourceTypes; this.accessRestriction = accessRestriction; + this.externalAnnotationPath = externalAnnotationPath; } /** * Returns the associated access restriction, or null if none. @@ -54,6 +56,10 @@ public class NameEnvironmentAnswer { return this.compilationUnit; } + public String getExternalAnnotationPath() { + return this.externalAnnotationPath; + } + /** * Answer the unresolved source forms for the type or null if the * receiver represents a compilation unit or binary type. diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ExternalAnnotationSuperimposer.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ExternalAnnotationSuperimposer.java new file mode 100644 index 0000000000..781a451a89 --- /dev/null +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ExternalAnnotationSuperimposer.java @@ -0,0 +1,269 @@ +/******************************************************************************* + * Copyright (c) 2016 GK Software AG. + * 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.internal.compiler.lookup; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipFile; + +import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider; +import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; +import org.eclipse.jdt.internal.compiler.env.ITypeAnnotationWalker; +import org.eclipse.jdt.internal.compiler.util.Messages; + +/** + * Used for superimposing external annotations (served by an {@link ITypeAnnotationWalker}) + * over signatures of a {@link SourceTypeBinding}. + */ +class ExternalAnnotationSuperimposer extends TypeBindingVisitor { + + public static void apply(SourceTypeBinding typeBinding, String externalAnnotationPath) { + ZipFile zipFile = null; + try { + File annotationBase = new File(externalAnnotationPath); + if (annotationBase.exists()) { + String binaryTypeName = String.valueOf(typeBinding.constantPoolName()); + String relativeFileName = binaryTypeName.replace('.', '/')+ExternalAnnotationProvider.ANNOTATION_FILE_SUFFIX; + + InputStream input; + if (annotationBase.isDirectory()) { + input = new FileInputStream(externalAnnotationPath+'/'+relativeFileName); + } else { + zipFile = new ZipFile(externalAnnotationPath); + ZipEntry zipEntry = zipFile.getEntry(relativeFileName); + if (zipEntry == null) + return; + input = zipFile.getInputStream(zipEntry); + } + annotateType(typeBinding, new ExternalAnnotationProvider(input, binaryTypeName), typeBinding.environment); + } + } catch (FileNotFoundException e) { + // file not found is expected + } catch (IOException e) { + typeBinding.scope.problemReporter().abortDueToInternalError(Messages.bind(Messages.abort_externaAnnotationFile, + new String[] {String.valueOf(typeBinding.readableName()), externalAnnotationPath, e.getMessage()})); + } finally { + if (zipFile != null) + try { + zipFile.close(); + } catch (IOException e) { + // nothing + } + } + } + + static void annotateType(SourceTypeBinding binding, ExternalAnnotationProvider provider, LookupEnvironment environment) { + ITypeAnnotationWalker typeWalker = provider.forTypeHeader(environment); + if (typeWalker != null && typeWalker != ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER) { + ExternalAnnotationSuperimposer visitor = new ExternalAnnotationSuperimposer(environment); + TypeVariableBinding[] typeParameters = binding.typeVariables(); + for (int i = 0; i < typeParameters.length; i++) { + if (visitor.go(typeWalker.toTypeParameter(true, i))) + typeParameters[i] = visitor.superimpose(typeParameters[i], TypeVariableBinding.class); + } + } + binding.externalAnnotationProvider = provider; // for superimposing method signatures + } + + public static void annotateFieldBinding(FieldBinding field, ExternalAnnotationProvider provider, LookupEnvironment environment) { + char[] fieldSignature = field.genericSignature(); + if (fieldSignature == null && field.type != null) + fieldSignature = field.type.signature(); + ITypeAnnotationWalker walker = provider.forField(field.name, fieldSignature, environment); + ExternalAnnotationSuperimposer visitor = new ExternalAnnotationSuperimposer(environment); + if (visitor.go(walker)) + field.type = visitor.superimpose(field.type, TypeBinding.class); + } + + public static void annotateMethodBinding(MethodBinding method, ExternalAnnotationProvider provider, LookupEnvironment environment) { + char[] methodSignature = method.genericSignature(); + if (methodSignature == null) + methodSignature = method.signature(); + ITypeAnnotationWalker walker = provider.forMethod(method.selector, methodSignature, environment); + if (walker != null && walker != ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER) { + ExternalAnnotationSuperimposer visitor = new ExternalAnnotationSuperimposer(environment); + TypeVariableBinding[] typeParams = method.typeVariables; + for (short i = 0; i < typeParams.length; i++) { + if (visitor.go(walker.toTypeParameter(false, i))) + typeParams[i] = visitor.superimpose(typeParams[i], TypeVariableBinding.class); + } + if (!method.isConstructor()) { + if (visitor.go(walker.toMethodReturn())) + method.returnType = visitor.superimpose(method.returnType, TypeBinding.class); + } + TypeBinding[] parameters = method.parameters; + for (short i = 0; i < parameters.length; i++) { + if (visitor.go(walker.toMethodParameter(i))) + parameters[i] = visitor.superimpose(parameters[i], TypeBinding.class); + } + } + } + + private ITypeAnnotationWalker currentWalker; + private TypeBinding typeReplacement; + private LookupEnvironment environment; + private boolean isReplacing; + + ExternalAnnotationSuperimposer(LookupEnvironment environment) { + this.environment = environment; + } + + /** for constructing a memento of the superimposer's current state. */ + private ExternalAnnotationSuperimposer(TypeBinding typeReplacement, boolean isReplacing, ITypeAnnotationWalker walker) { + this.typeReplacement = typeReplacement; + this.isReplacing = isReplacing; + this.currentWalker = walker; + } + private ExternalAnnotationSuperimposer snapshot() { + ExternalAnnotationSuperimposer memento = new ExternalAnnotationSuperimposer(this.typeReplacement, this.isReplacing, this.currentWalker); + // soft reset: + this.typeReplacement = null; + this.isReplacing = false; + return memento; + } + private void restore(ExternalAnnotationSuperimposer memento) { + this.isReplacing = memento.isReplacing; + this.currentWalker = memento.currentWalker; + } + + boolean go(ITypeAnnotationWalker walker) { + // hard reset: + reset(); + this.typeReplacement = null; + this.isReplacing = false; + // and start anew: + this.currentWalker = walker; + return walker != ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER; + } + + <T extends TypeBinding> T superimpose(T type, Class<? extends T> cl) { + TypeBindingVisitor.visit(this, type); + if (cl.isInstance(this.typeReplacement)) + return cl.cast(this.typeReplacement); + return type; + } + + private TypeBinding goAndSuperimpose(ITypeAnnotationWalker walker, TypeBinding type) { + // no reset here + if (walker == ITypeAnnotationWalker.EMPTY_ANNOTATION_WALKER) + return type; + this.currentWalker = walker; + + TypeBindingVisitor.visit(this, type); + + if (this.typeReplacement == null) + return type; + this.isReplacing = true; + TypeBinding answer = this.typeReplacement; + this.typeReplacement = null; + return answer; + } + + @Override + public boolean visit(ArrayBinding arrayBinding) { + ExternalAnnotationSuperimposer memento = snapshot(); + try { + int dims = arrayBinding.dimensions; + AnnotationBinding[][] annotsOnDims = new AnnotationBinding[dims][]; + ITypeAnnotationWalker walker = this.currentWalker; + for (int i = 0; i < dims; i++) { + IBinaryAnnotation[] binaryAnnotations = walker.getAnnotationsAtCursor(arrayBinding.id); + if (binaryAnnotations != ITypeAnnotationWalker.NO_ANNOTATIONS) { + annotsOnDims[i] = BinaryTypeBinding.createAnnotations(binaryAnnotations, this.environment, null); + this.isReplacing = true; + } else { + annotsOnDims[i] = Binding.NO_ANNOTATIONS; + } + walker = walker.toNextArrayDimension(); + } + TypeBinding leafComponentType = goAndSuperimpose(walker, arrayBinding.leafComponentType()); + if (this.isReplacing) { + this.typeReplacement = this.environment.createArrayType(leafComponentType, dims, AnnotatableTypeSystem.flattenedAnnotations(annotsOnDims)); + } + } finally { + restore(memento); + } + return false; + } + @Override + public boolean visit(BaseTypeBinding baseTypeBinding) { + return false; // no null annotations + } + @Override + public boolean visit(IntersectionTypeBinding18 intersectionTypeBinding18) { + return false; // shouldn't occur in declarations + } + @Override + public boolean visit(ParameterizedTypeBinding parameterizedTypeBinding) { + ExternalAnnotationSuperimposer memento = snapshot(); + try { + IBinaryAnnotation[] binaryAnnotations = this.currentWalker.getAnnotationsAtCursor(parameterizedTypeBinding.id); + AnnotationBinding[] annotations = Binding.NO_ANNOTATIONS; + if (binaryAnnotations != ITypeAnnotationWalker.NO_ANNOTATIONS) { + annotations = BinaryTypeBinding.createAnnotations(binaryAnnotations, this.environment, null); + this.isReplacing = true; + } + + TypeBinding[] typeArguments = parameterizedTypeBinding.typeArguments(); + TypeBinding[] newArguments = new TypeBinding[typeArguments.length]; + for (int i = 0; i < typeArguments.length; i++) { + newArguments[i] = goAndSuperimpose(memento.currentWalker.toTypeArgument(i), typeArguments[i]); + } + if (this.isReplacing) + this.typeReplacement = this.environment.createParameterizedType(parameterizedTypeBinding.genericType(), newArguments, parameterizedTypeBinding.enclosingType(), annotations); + return false; + } finally { + restore(memento); + } + } + @Override + public boolean visit(RawTypeBinding rawTypeBinding) { + return visit((ReferenceBinding)rawTypeBinding); + } + @Override + public boolean visit(ReferenceBinding referenceBinding) { + IBinaryAnnotation[] binaryAnnotations = this.currentWalker.getAnnotationsAtCursor(referenceBinding.id); + if (binaryAnnotations != ITypeAnnotationWalker.NO_ANNOTATIONS) + this.typeReplacement = this.environment.createAnnotatedType(referenceBinding, BinaryTypeBinding.createAnnotations(binaryAnnotations, this.environment, null)); + return false; + } + @Override + public boolean visit(TypeVariableBinding typeVariable) { + return visit((ReferenceBinding) typeVariable); + } + @Override + public boolean visit(WildcardBinding wildcardBinding) { + TypeBinding bound = wildcardBinding.bound; + ExternalAnnotationSuperimposer memento = snapshot(); + try { + if (bound != null) { + bound = goAndSuperimpose(memento.currentWalker.toWildcardBound(), bound); + } + IBinaryAnnotation[] binaryAnnotations = memento.currentWalker.getAnnotationsAtCursor(-1); + if (this.isReplacing || binaryAnnotations != ITypeAnnotationWalker.NO_ANNOTATIONS) { + TypeBinding[] otherBounds = wildcardBinding.otherBounds; + if (binaryAnnotations != ITypeAnnotationWalker.NO_ANNOTATIONS) { + AnnotationBinding[] annotations = BinaryTypeBinding.createAnnotations(binaryAnnotations, this.environment, null); + this.typeReplacement = this.environment.createWildcard(wildcardBinding.genericType, wildcardBinding.rank, bound, otherBounds, wildcardBinding.boundKind, annotations); + } else { + this.typeReplacement = this.environment.createWildcard(wildcardBinding.genericType, wildcardBinding.rank, bound, otherBounds, wildcardBinding.boundKind); + } + } + } finally { + restore(memento); + } + return false; + } +} diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java index 15c5aac3d6..b233099fc5 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/LookupEnvironment.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation 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 @@ -190,6 +190,12 @@ ReferenceBinding askForType(PackageBinding packageBinding, char[] name) { } else if (answer.isSourceType()) { // the type was found as a source model this.typeRequestor.accept(answer.getSourceTypes(), packageBinding, answer.getAccessRestriction()); + ReferenceBinding binding = packageBinding.getType0(name); + String externalAnnotationPath = answer.getExternalAnnotationPath(); + if (externalAnnotationPath != null && this.globalOptions.isAnnotationBasedNullAnalysisEnabled && binding instanceof SourceTypeBinding) { + ExternalAnnotationSuperimposer.apply((SourceTypeBinding) binding, externalAnnotationPath); + } + return binding; } return packageBinding.getType0(name); } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java index c4a5e723fe..7ae4663504 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java @@ -65,6 +65,7 @@ import org.eclipse.jdt.internal.compiler.ast.TypeParameter; import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.TypeReference.AnnotationPosition;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
+import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider;
import org.eclipse.jdt.internal.compiler.impl.CompilerOptions;
import org.eclipse.jdt.internal.compiler.impl.Constant;
import org.eclipse.jdt.internal.compiler.problem.ProblemSeverities;
@@ -101,6 +102,8 @@ public class SourceTypeBinding extends ReferenceBinding { private int nullnessDefaultInitialized = 0; // 0: nothing; 1: type; 2: package
private int lambdaOrdinal = 0;
private ReferenceBinding containerAnnotationType = null;
+
+ public ExternalAnnotationProvider externalAnnotationProvider;
public SourceTypeBinding(char[][] compoundName, PackageBinding fPackage, ClassScope scope) {
this.compoundName = compoundName;
@@ -1773,6 +1776,9 @@ public FieldBinding resolveTypeFor(FieldBinding field) { } finally {
initializationScope.initializedField = previousField;
}
+ if (this.externalAnnotationProvider != null) {
+ ExternalAnnotationSuperimposer.annotateFieldBinding(field, this.externalAnnotationProvider, this.environment);
+ }
return field;
}
return null; // should never reach this point
@@ -1995,6 +2001,9 @@ public MethodBinding resolveTypesFor(MethodBinding method) { return method; // but its still unresolved with a null return type & is still connected to its method declaration
method.modifiers &= ~ExtraCompilerModifiers.AccUnresolved;
+ if (this.externalAnnotationProvider != null) {
+ ExternalAnnotationSuperimposer.annotateMethodBinding(method, this.externalAnnotationProvider, this.environment);
+ }
return method;
}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=391108
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBindingVisitor.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBindingVisitor.java index a77994e178..ad71838602 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBindingVisitor.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/TypeBindingVisitor.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2013 IBM Corporation and others. + * Copyright (c) 2013, 2016 IBM Corporation 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 @@ -18,6 +18,10 @@ public class TypeBindingVisitor { private SimpleLookupTable visitedCache; + public void reset() { + this.visitedCache = null; + } + public boolean visit(BaseTypeBinding baseTypeBinding) { return true; // continue traversal. } diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/messages.properties b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/messages.properties index 614d5acbce..1d1a5bbb80 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/messages.properties +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/messages.properties @@ -1,5 +1,5 @@ ############################################################################### -# Copyright (c) 2000, 2009 IBM Corporation and others. +# Copyright (c) 2000, 2016 IBM Corporation 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 @@ -39,6 +39,7 @@ abort_invalidExceptionAttribute = SANITY CHECK: Invalid attribute for exception abort_missingCode = Missing code implementation in the compiler abort_againstSourceModel = Cannot compile against source model {0} issued from {1} abort_invalidOpcode = SANITY CHECK: Invalid opcode {0} at pc {1} for stackmap table attribute for method {2} +abort_externaAnnotationFile = Failed to read external annotations for {0} from {1} due to an exception: {2} ### accept accept_cannot = Cannot accept the compilation unit: diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/Messages.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/Messages.java index 5eded19ddc..317d830ece 100644 --- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/Messages.java +++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/util/Messages.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2013 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation 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 @@ -100,6 +100,7 @@ public final class Messages { public static String abort_invalidOpcode; public static String abort_missingCode; public static String abort_againstSourceModel; + public static String abort_externaAnnotationFile; public static String accept_cannot; public static String parser_incorrectPath; public static String parser_moveFiles; diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java index 2ecd344e75..6d15fb2556 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/core/util/ExternalAnnotationUtil.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 GK Software AG. + * Copyright (c) 2015, 2016 GK Software AG. * 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 @@ -185,7 +185,7 @@ public final class ExternalAnnotationUtil { return null; } - annotationPath = annotationPath.append(binaryTypeName).addFileExtension(ExternalAnnotationProvider.ANNOTION_FILE_EXTENSION); + annotationPath = annotationPath.append(binaryTypeName).addFileExtension(ExternalAnnotationProvider.ANNOTATION_FILE_EXTENSION); return workspaceRoot.getFile(annotationPath); } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ExternalAnnotationTracker.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ExternalAnnotationTracker.java index 19c4747ef0..cc5d4f1880 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ExternalAnnotationTracker.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/ExternalAnnotationTracker.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2015 GK Software AG. + * Copyright (c) 2015, 2016 GK Software AG. * 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 @@ -149,7 +149,7 @@ public class ExternalAnnotationTracker implements IResourceChangeListener { if (baseDepth == 0) { Util.log(new IllegalArgumentException("annotationBase cannot be empty")); //$NON-NLS-1$ } else { - relativeAnnotationPath = relativeAnnotationPath.addFileExtension(ExternalAnnotationProvider.ANNOTION_FILE_EXTENSION); + relativeAnnotationPath = relativeAnnotationPath.addFileExtension(ExternalAnnotationProvider.ANNOTATION_FILE_EXTENSION); DirectoryNode base = singleton.getAnnotationBase(singleton.tree, annotationBase, baseDepth, 1); base.registerClassFile(relativeAnnotationPath, classFile); } @@ -166,7 +166,7 @@ public class ExternalAnnotationTracker implements IResourceChangeListener { if (baseDepth == 0) { Util.log(new IllegalArgumentException("annotationBase cannot be empty")); //$NON-NLS-1$ } else { - relativeAnnotationPath = relativeAnnotationPath.addFileExtension(ExternalAnnotationProvider.ANNOTION_FILE_EXTENSION); + relativeAnnotationPath = relativeAnnotationPath.addFileExtension(ExternalAnnotationProvider.ANNOTATION_FILE_EXTENSION); DirectoryNode base = singleton.getAnnotationBase(singleton.tree, annotationBase, baseDepth, 1); base.unregisterClassFile(relativeAnnotationPath); } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NameLookup.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NameLookup.java index 517b19bc18..58ea8fe8d7 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NameLookup.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/NameLookup.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation 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 @@ -61,9 +61,11 @@ public class NameLookup implements SuffixConstants { public static class Answer { public IType type; AccessRestriction restriction; - Answer(IType type, AccessRestriction restriction) { + IClasspathEntry entry; + Answer(IType type, AccessRestriction restriction, IClasspathEntry entry) { this.type = type; this.restriction = restriction; + this.entry = entry; } public boolean ignoreIfBetter() { return this.restriction != null && this.restriction.ignoreIfBetter(); @@ -663,10 +665,14 @@ public class NameLookup implements SuffixConstants { type = findType(typeName, packages[i], partialMatch, acceptFlags, waitForIndexes, considerSecondaryTypes); if (type != null) { AccessRestriction accessRestriction = null; - if (checkRestrictions) { - accessRestriction = getViolatedRestriction(typeName, packageName, type, accessRestriction); + PackageFragmentRoot root = (PackageFragmentRoot) type.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); + ClasspathEntry entry = (ClasspathEntry) this.rootToResolvedEntries.get(root); + if (entry != null) { // reverse map always contains resolved CP entry + if (checkRestrictions) { + accessRestriction = getViolatedRestriction(typeName, packageName, entry, accessRestriction); + } } - Answer answer = new Answer(type, accessRestriction); + Answer answer = new Answer(type, accessRestriction, entry); if (!answer.ignoreIfBetter()) { if (answer.isBetter(suggestedAnswer)) return answer; @@ -722,20 +728,16 @@ public class NameLookup implements SuffixConstants { if (!typeFound) type = null; } } - return type == null ? null : new Answer(type, null); + return type == null ? null : new Answer(type, null, null); } - private AccessRestriction getViolatedRestriction(String typeName, String packageName, IType type, AccessRestriction accessRestriction) { - PackageFragmentRoot root = (PackageFragmentRoot) type.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT); - ClasspathEntry entry = (ClasspathEntry) this.rootToResolvedEntries.get(root); - if (entry != null) { // reverse map always contains resolved CP entry - AccessRuleSet accessRuleSet = entry.getAccessRuleSet(); - if (accessRuleSet != null) { - // TODO (philippe) improve char[] <-> String conversions to avoid performing them on the fly - char[][] packageChars = CharOperation.splitOn('.', packageName.toCharArray()); - char[] typeChars = typeName.toCharArray(); - accessRestriction = accessRuleSet.getViolatedRestriction(CharOperation.concatWith(packageChars, typeChars, '/')); - } + private AccessRestriction getViolatedRestriction(String typeName, String packageName, ClasspathEntry entry, AccessRestriction accessRestriction) { + AccessRuleSet accessRuleSet = entry.getAccessRuleSet(); + if (accessRuleSet != null) { + // TODO (philippe) improve char[] <-> String conversions to avoid performing them on the fly + char[][] packageChars = CharOperation.splitOn('.', packageName.toCharArray()); + char[] typeChars = typeName.toCharArray(); + accessRestriction = accessRuleSet.getViolatedRestriction(CharOperation.concatWith(packageChars, typeChars, '/')); } return accessRestriction; } diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SearchableEnvironment.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SearchableEnvironment.java index 8dc74b2600..c44cba3422 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SearchableEnvironment.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/SearchableEnvironment.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation 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 @@ -11,6 +11,7 @@ *******************************************************************************/ package org.eclipse.jdt.internal.core; +import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.jdt.core.*; @@ -136,7 +137,7 @@ public class SearchableEnvironment if (!otherType.equals(topLevelType) && index < length) // check that the index is in bounds (see https://bugs.eclipse.org/bugs/show_bug.cgi?id=62861) sourceTypes[index++] = otherType; } - return new NameEnvironmentAnswer(sourceTypes, answer.restriction); + return new NameEnvironmentAnswer(sourceTypes, answer.restriction, getExternalAnnotationPath(answer.entry)); } catch (JavaModelException jme) { if (jme.isDoesNotExist() && String.valueOf(TypeConstants.PACKAGE_INFO_NAME).equals(typeName)) { // in case of package-info.java the type doesn't exist in the model, @@ -150,6 +151,15 @@ public class SearchableEnvironment return null; } + private String getExternalAnnotationPath(IClasspathEntry entry) { + if (entry == null) + return null; + IPath path = ClasspathEntry.getExternalAnnotationPath(entry, this.project.getProject(), true); + if (path == null) + return null; + return path.toOSString(); + } + /** * Find the packages that start with the given prefix. * A valid prefix is a qualified name separated by periods diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathDirectory.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathDirectory.java index 63c8de7c5e..72917d5716 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathDirectory.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathDirectory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2009 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation 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 @@ -11,6 +11,7 @@ package org.eclipse.jdt.internal.core.builder; import java.io.IOException; +import java.util.zip.ZipFile; import org.eclipse.core.resources.*; import org.eclipse.core.runtime.*; @@ -30,15 +31,26 @@ boolean isOutputFolder; SimpleLookupTable directoryCache; String[] missingPackageHolder = new String[1]; AccessRuleSet accessRuleSet; +ZipFile annotationZipFile; +String externalAnnotationPath; -ClasspathDirectory(IContainer binaryFolder, boolean isOutputFolder, AccessRuleSet accessRuleSet) { +ClasspathDirectory(IContainer binaryFolder, boolean isOutputFolder, AccessRuleSet accessRuleSet, IPath externalAnnotationPath) { this.binaryFolder = binaryFolder; this.isOutputFolder = isOutputFolder || binaryFolder.getProjectRelativePath().isEmpty(); // if binaryFolder == project, then treat it as an outputFolder this.directoryCache = new SimpleLookupTable(5); this.accessRuleSet = accessRuleSet; + if (externalAnnotationPath != null) + this.externalAnnotationPath = externalAnnotationPath.toOSString(); } public void cleanup() { + if (this.annotationZipFile != null) { + try { + this.annotationZipFile.close(); + } catch(IOException e) { // ignore it + } + this.annotationZipFile = null; + } this.directoryCache = null; } @@ -106,9 +118,16 @@ public NameEnvironmentAnswer findClass(String binaryFileName, String qualifiedPa return null; } if (reader != null) { + String fileNameWithoutExtension = qualifiedBinaryFileName.substring(0, qualifiedBinaryFileName.length() - SuffixConstants.SUFFIX_CLASS.length); + if (this.externalAnnotationPath != null) { + try { + this.annotationZipFile = reader.setExternalAnnotationProvider(this.externalAnnotationPath, fileNameWithoutExtension, this.annotationZipFile, null); + } catch (IOException e) { + // don't let error on annotations fail class reading + } + } if (this.accessRuleSet == null) return new NameEnvironmentAnswer(reader, null); - String fileNameWithoutExtension = qualifiedBinaryFileName.substring(0, qualifiedBinaryFileName.length() - SuffixConstants.SUFFIX_CLASS.length); return new NameEnvironmentAnswer(reader, this.accessRuleSet.getViolatedRestriction(fileNameWithoutExtension.toCharArray())); } return null; diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathLocation.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathLocation.java index 343abc086f..8758a86628 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathLocation.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathLocation.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation 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 @@ -24,8 +24,8 @@ static ClasspathLocation forSourceFolder(IContainer sourceFolder, IContainer out return new ClasspathMultiDirectory(sourceFolder, outputFolder, inclusionPatterns, exclusionPatterns, ignoreOptionalProblems); } -public static ClasspathLocation forBinaryFolder(IContainer binaryFolder, boolean isOutputFolder, AccessRuleSet accessRuleSet) { - return new ClasspathDirectory(binaryFolder, isOutputFolder, accessRuleSet); +public static ClasspathLocation forBinaryFolder(IContainer binaryFolder, boolean isOutputFolder, AccessRuleSet accessRuleSet, IPath externalAnnotationPath) { + return new ClasspathDirectory(binaryFolder, isOutputFolder, accessRuleSet, externalAnnotationPath); } static ClasspathLocation forLibrary(String libraryPathname, long lastModified, AccessRuleSet accessRuleSet, IPath annotationsPath) { diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathMultiDirectory.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathMultiDirectory.java index 3d41e087a9..991e98ec68 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathMultiDirectory.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/ClasspathMultiDirectory.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2012 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation 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 @@ -24,7 +24,7 @@ boolean hasIndependentOutputFolder; // if output folder is not equal to any of t public boolean ignoreOptionalProblems; ClasspathMultiDirectory(IContainer sourceFolder, IContainer binaryFolder, char[][] inclusionPatterns, char[][] exclusionPatterns, boolean ignoreOptionalProblems) { - super(binaryFolder, true, null); + super(binaryFolder, true, null, null); this.sourceFolder = sourceFolder; this.inclusionPatterns = inclusionPatterns; diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java index 060bb205a9..c1509e1804 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/NameEnvironment.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation 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 @@ -146,7 +146,7 @@ private void computeClasspathLocations( : (IContainer) root.getFolder(prereqOutputPath); if (binaryFolder.exists() && !seen.contains(binaryFolder)) { seen.add(binaryFolder); - ClasspathLocation bLocation = ClasspathLocation.forBinaryFolder(binaryFolder, true, entry.getAccessRuleSet()); + ClasspathLocation bLocation = ClasspathLocation.forBinaryFolder(binaryFolder, true, entry.getAccessRuleSet(), externalAnnotationPath); bLocations.add(bLocation); if (binaryLocationsPerProject != null) { // normal builder mode ClasspathLocation[] existingLocations = (ClasspathLocation[]) binaryLocationsPerProject.get(prereqProject); @@ -181,7 +181,7 @@ private void computeClasspathLocations( && JavaCore.IGNORE.equals(javaProject.getOption(JavaCore.COMPILER_PB_DISCOURAGED_REFERENCE, true))) ? null : entry.getAccessRuleSet(); - bLocation = ClasspathLocation.forBinaryFolder((IContainer) target, false, accessRuleSet); // is library folder not output folder + bLocation = ClasspathLocation.forBinaryFolder((IContainer) target, false, accessRuleSet, externalAnnotationPath); // is library folder not output folder } bLocations.add(bLocation); if (binaryLocationsPerProject != null) { // normal builder mode diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java index 72f0d233cc..c28e5c3de3 100644 --- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java +++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/State.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2014 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation 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 @@ -47,7 +47,7 @@ private long previousStructuralBuildTime; private StringSet structurallyChangedTypes; public static int MaxStructurallyChangedTypes = 100; // keep track of ? structurally changed types, otherwise consider all to be changed -public static final byte VERSION = 0x001C; +public static final byte VERSION = 0x001D; static final byte SOURCE_FOLDER = 1; static final byte BINARY_FOLDER = 2; @@ -268,7 +268,7 @@ static State read(IProject project, DataInputStream in) throws IOException { IContainer outputFolder = path.segmentCount() == 1 ? (IContainer) root.getProject(path.toString()) : (IContainer) root.getFolder(path); - newState.binaryLocations[i] = ClasspathLocation.forBinaryFolder(outputFolder, in.readBoolean(), readRestriction(in)); + newState.binaryLocations[i] = ClasspathLocation.forBinaryFolder(outputFolder, in.readBoolean(), readRestriction(in), new Path(in.readUTF())); break; case EXTERNAL_JAR : newState.binaryLocations[i] = ClasspathLocation.forLibrary(in.readUTF(), in.readLong(), readRestriction(in), new Path(in.readUTF())); @@ -454,6 +454,7 @@ void write(DataOutputStream out) throws IOException { out.writeUTF(cd.binaryFolder.getFullPath().toString()); out.writeBoolean(cd.isOutputFolder); writeRestriction(cd.accessRuleSet, out); + out.writeUTF(cd.externalAnnotationPath != null ? cd.externalAnnotationPath : ""); //$NON-NLS-1$ } else { ClasspathJar jar = (ClasspathJar) c; if (jar.resource == null) { diff --git a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java index 2126a1ce01..636863b000 100644 --- a/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java +++ b/org.eclipse.jdt.core/search/org/eclipse/jdt/internal/core/search/matching/JavaSearchNameEnvironment.java @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2000, 2015 IBM Corporation and others. + * Copyright (c) 2000, 2016 IBM Corporation 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 @@ -104,10 +104,15 @@ private ClasspathLocation mapToClassPathLocation( JavaModelManager manager, Pack cp = new ClasspathJar(manager.getZipFile(path), rawClasspathEntry.getAccessRuleSet(), ClasspathEntry.getExternalAnnotationPath(rawClasspathEntry, ((IJavaProject)root.getParent()).getProject(), true)); } else { Object target = JavaModel.getTarget(path, true); - if (target != null) - cp = root.getKind() == IPackageFragmentRoot.K_SOURCE ? - new ClasspathSourceDirectory((IContainer)target, root.fullExclusionPatternChars(), root.fullInclusionPatternChars()) : - ClasspathLocation.forBinaryFolder((IContainer) target, false, ((ClasspathEntry) root.getRawClasspathEntry()).getAccessRuleSet()); + if (target != null) { + if (root.getKind() == IPackageFragmentRoot.K_SOURCE) { + cp = new ClasspathSourceDirectory((IContainer)target, root.fullExclusionPatternChars(), root.fullInclusionPatternChars()); + } else { + ClasspathEntry rawClasspathEntry = (ClasspathEntry) root.getRawClasspathEntry(); + cp = ClasspathLocation.forBinaryFolder((IContainer) target, false, rawClasspathEntry.getAccessRuleSet(), + ClasspathEntry.getExternalAnnotationPath(rawClasspathEntry, ((IJavaProject)root.getParent()).getProject(), true)); + } + } } } catch (CoreException e1) { // problem opening zip file or getting root kind |
