Bug 509397: [null] Enable EEA for generated sources
Change-Id: Ibe1ad400bde67b14b1c55d10788048ea1a83fa15
Also-by: Frank Benoit <frank.rene.benoit@gmail.com>
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 06ff023..5024a9e 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
@@ -315,6 +315,13 @@
this.project.setRawClasspath(rawClasspath, new NullProgressMonitor());
}
+ protected void addSourceFolderWithExternalAnnotations(IJavaProject javaProject, String sourceFolder, String outputFolder, String externalAnnotationPath) throws JavaModelException {
+ IClasspathAttribute[] extraAttributes = new IClasspathAttribute[] { new ClasspathAttribute(IClasspathAttribute.EXTERNAL_ANNOTATION_PATH, externalAnnotationPath) };
+ IClasspathEntry entry = JavaCore.newSourceEntry(new Path(sourceFolder), null, null,
+ outputFolder != null ? new Path(outputFolder) : null, extraAttributes);
+ addClasspathEntry(javaProject, entry);
+ }
+
protected void createFileInProject(String projectRelativeFolder, String fileName, String content) throws CoreException {
String folderPath = this.project.getProject().getName()+'/'+projectRelativeFolder;
createFolder(folderPath);
@@ -2666,4 +2673,154 @@
deleteProject("Bug500024");
}
}
+
+ // reconcile client of a "generated" source+eea
+ @SuppressWarnings("deprecation")
+ public void testSourceFolder1() throws CoreException {
+ myCreateJavaProject("Bug509397");
+ addSourceFolderWithExternalAnnotations(this.project, "/Bug509397/src-gen", "/Bug509397/bin-gen", "/Bug509397/annot-gen");
+
+ createFileInProject("annot-gen/pgen", "CGen.eea",
+ "class pgen/CGen\n" +
+ "\n" +
+ "get\n" +
+ " (Ljava/lang/String;)Ljava/lang/String;\n" +
+ " (L1java/lang/String;)L1java/lang/String;\n");
+
+ createFileInProject("src-gen/pgen", "CGen.java",
+ "package pgen;\n" +
+ "public class CGen {\n" +
+ " public String get(String in) { return in; }\n" +
+ "}\n");
+
+ IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("p", true, null);
+ ICompilationUnit unit = fragment.createCompilationUnit("Use.java",
+ "package p;\n" +
+ "import pgen.CGen;\n" +
+ "import org.eclipse.jdt.annotation.NonNull;\n" +
+ "public class Use {\n" +
+ " public @NonNull String test(CGen c) {\n" +
+ " String s = c.get(null);\n" + // problem here (6)
+ " return s;\n" + // no problem here
+ " }\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(910) Null type mismatch: required '@NonNull String' but the provided value is null"
+ }, new int[] { 6 });
+ }
+
+ // reconcile client of a "generated" source+eea
+ // single merged output folder
+ @SuppressWarnings("deprecation")
+ public void testSourceFolder1a() throws CoreException {
+ myCreateJavaProject("Bug509397");
+ addSourceFolderWithExternalAnnotations(this.project, "/Bug509397/src-gen", null, "/Bug509397/annot-gen");
+
+ createFileInProject("annot-gen/pgen", "CGen.eea",
+ "class pgen/CGen\n" +
+ "\n" +
+ "get\n" +
+ " (Ljava/lang/String;)Ljava/lang/String;\n" +
+ " (L1java/lang/String;)L1java/lang/String;\n");
+
+ createFileInProject("src-gen/pgen", "CGen.java",
+ "package pgen;\n" +
+ "public class CGen {\n" +
+ " public String get(String in) { return in; }\n" +
+ "}\n");
+ this.project.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null);
+
+ IPackageFragment fragment = this.project.getPackageFragmentRoots()[0].createPackageFragment("p", true, null);
+ ICompilationUnit unit = fragment.createCompilationUnit("Use.java",
+ "package p;\n" +
+ "import pgen.CGen;\n" +
+ "import org.eclipse.jdt.annotation.NonNull;\n" +
+ "public class Use {\n" +
+ " public @NonNull String test(CGen c) {\n" +
+ " String s = c.get(null);\n" + // problem here (6)
+ " return s;\n" + // no problem here
+ " }\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(910) Null type mismatch: required '@NonNull String' but the provided value is null"
+ }, new int[] { 6 });
+ }
+
+ // full build of a project with src-gen & annot-gen
+ public void testSourceFolder2() throws CoreException {
+ myCreateJavaProject("Bug509397");
+ addSourceFolderWithExternalAnnotations(this.project, "/Bug509397/src-gen", "/Bug509397/bin-gen", "/Bug509397/annot-gen");
+
+ createFileInProject("annot-gen/pgen", "CGen.eea",
+ "class pgen/CGen\n" +
+ "\n" +
+ "get\n" +
+ " (Ljava/lang/String;)Ljava/lang/String;\n" +
+ " (L1java/lang/String;)L1java/lang/String;\n");
+
+ createFileInProject("src-gen/pgen", "CGen.java",
+ "package pgen;\n" +
+ "public class CGen {\n" +
+ " public String get(String in) { return in; }\n" +
+ "}\n");
+
+ createFileInProject("src/p", "Use.java",
+ "package p;\n" +
+ "import pgen.CGen;\n" +
+ "import org.eclipse.jdt.annotation.NonNull;\n" +
+ "public class Use {\n" +
+ " public @NonNull String test(CGen c) {\n" +
+ " String s = c.get(null);\n" + // problem here (6)
+ " return s;\n" + // no problem here
+ " }\n" +
+ "}\n");
+ this.project.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null);
+ IMarker[] markers = this.project.getProject().findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
+ assertMarkers("Unexpected markers",
+ "Null type mismatch: required '@NonNull String' but the provided value is null",
+ markers);
+ }
+
+ // full build of a project with src-gen & annot-gen
+ // single merged output folder
+ public void testSourceFolder2a() throws CoreException {
+ myCreateJavaProject("Bug509397");
+ addSourceFolderWithExternalAnnotations(this.project, "/Bug509397/src-gen", null, "/Bug509397/annot-gen");
+
+ createFileInProject("annot-gen/pgen", "CGen.eea",
+ "class pgen/CGen\n" +
+ "\n" +
+ "get\n" +
+ " (Ljava/lang/String;)Ljava/lang/String;\n" +
+ " (L1java/lang/String;)L1java/lang/String;\n");
+
+ createFileInProject("src-gen/pgen", "CGen.java",
+ "package pgen;\n" +
+ "public class CGen {\n" +
+ " public String get(String in) { return in; }\n" +
+ "}\n");
+
+ createFileInProject("src/p", "Use.java",
+ "package p;\n" +
+ "import pgen.CGen;\n" +
+ "import org.eclipse.jdt.annotation.NonNull;\n" +
+ "public class Use {\n" +
+ " public @NonNull String test(CGen c) {\n" +
+ " String s = c.get(null);\n" + // problem here (6)
+ " return s;\n" + // no problem here
+ " }\n" +
+ "}\n");
+ this.project.getProject().build(IncrementalProjectBuilder.FULL_BUILD, null);
+ IMarker[] markers = this.project.getProject().findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
+ assertMarkers("Unexpected markers",
+ "Null type mismatch: required '@NonNull String' but the provided value is null",
+ markers);
+ }
+
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/ICompilationUnit.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/ICompilationUnit.java
index 24bd52d..5e2cdfa 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/ICompilationUnit.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/env/ICompilationUnit.java
@@ -74,4 +74,10 @@
default String getDestinationPath() {
return null;
}
+/**
+ * Answers a path for external annotations that has been configured for
+ * the providing classpath entry, or <code>null</code>.
+ */
+default String getExternalAnnotationPath() { return null; }
+
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
index fafd05d..a2affaf 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/ClassScope.java
@@ -52,6 +52,7 @@
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.env.AccessRestriction;
+import org.eclipse.jdt.internal.compiler.env.ICompilationUnit;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.compiler.problem.ProblemReporter;
import org.eclipse.jdt.internal.compiler.util.HashtableOfObject;
@@ -522,6 +523,13 @@
SourceTypeBinding sourceType = this.referenceContext.binding;
sourceType.module = module();
environment().setAccessRestriction(sourceType, accessRestriction);
+ ICompilationUnit compilationUnit = this.referenceContext.compilationResult.getCompilationUnit();
+ if (compilationUnit != null && compilerOptions().isAnnotationBasedNullAnalysisEnabled) {
+ String externalAnnotationPath = compilationUnit.getExternalAnnotationPath();
+ if (externalAnnotationPath != null) {
+ ExternalAnnotationSuperimposer.apply(sourceType, externalAnnotationPath);
+ }
+ }
TypeParameter[] typeParameters = this.referenceContext.typeParameters;
sourceType.typeVariables = typeParameters == null || typeParameters.length == 0 ? Binding.NO_TYPE_VARIABLES : null;
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
index 9fd2eba..ee946d3 100644
--- 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
@@ -21,6 +21,7 @@
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
+import org.eclipse.jdt.internal.compiler.ast.Argument;
import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider;
import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation;
import org.eclipse.jdt.internal.compiler.env.ITypeAnnotationWalker;
@@ -101,7 +102,7 @@
field.type = visitor.superimpose(field.type, TypeBinding.class);
}
- public static void annotateMethodBinding(MethodBinding method, ExternalAnnotationProvider provider, LookupEnvironment environment) {
+ public static void annotateMethodBinding(MethodBinding method, Argument[] arguments, ExternalAnnotationProvider provider, LookupEnvironment environment) {
char[] methodSignature = method.genericSignature();
if (methodSignature == null)
methodSignature = method.signature();
@@ -119,8 +120,11 @@
}
TypeBinding[] parameters = method.parameters;
for (short i = 0; i < parameters.length; i++) {
- if (visitor.go(walker.toMethodParameter(i)))
+ if (visitor.go(walker.toMethodParameter(i))) {
parameters[i] = visitor.superimpose(parameters[i], TypeBinding.class);
+ if (arguments != null && i < arguments.length)
+ arguments[i].binding.type = parameters[i];
+ }
}
}
}
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 4a475f5..7a1b542 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
@@ -2841,7 +2841,7 @@
method.modifiers &= ~ExtraCompilerModifiers.AccUnresolved;
if (this.externalAnnotationProvider != null) {
- ExternalAnnotationSuperimposer.annotateMethodBinding(method, this.externalAnnotationProvider, this.environment);
+ ExternalAnnotationSuperimposer.annotateMethodBinding(method, arguments, this.externalAnnotationProvider, this.environment);
}
return method;
}
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 4cdb930..44fc370 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
@@ -121,9 +121,8 @@
return true;
}
static ClasspathLocation forSourceFolder(IContainer sourceFolder, IContainer outputFolder,
- char[][] inclusionPatterns, char[][] exclusionPatterns, boolean ignoreOptionalProblems) {
- return new ClasspathMultiDirectory(sourceFolder, outputFolder, inclusionPatterns, exclusionPatterns,
- ignoreOptionalProblems);
+ char[][] inclusionPatterns, char[][] exclusionPatterns, boolean ignoreOptionalProblems, IPath externalAnnotationPath) {
+ return new ClasspathMultiDirectory(sourceFolder, outputFolder, inclusionPatterns, exclusionPatterns, ignoreOptionalProblems, externalAnnotationPath);
}
public static ClasspathLocation forBinaryFolder(IContainer binaryFolder, boolean isOutputFolder, AccessRuleSet accessRuleSet, IPath externalAnnotationPath, boolean autoModule) {
return new ClasspathDirectory(binaryFolder, isOutputFolder, accessRuleSet, externalAnnotationPath, autoModule);
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 96b26f2..e127fb7 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
@@ -15,6 +15,7 @@
import org.eclipse.core.resources.*;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
import org.eclipse.jdt.core.compiler.CharOperation;
import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.ast.CompilationUnitDeclaration;
@@ -32,8 +33,9 @@
boolean hasIndependentOutputFolder; // if output folder is not equal to any of the source folders
public boolean ignoreOptionalProblems;
-ClasspathMultiDirectory(IContainer sourceFolder, IContainer binaryFolder, char[][] inclusionPatterns, char[][] exclusionPatterns, boolean ignoreOptionalProblems) {
- super(binaryFolder, true, null, null, false /* source never an automatic module*/);
+ClasspathMultiDirectory(IContainer sourceFolder, IContainer binaryFolder, char[][] inclusionPatterns, char[][] exclusionPatterns,
+ boolean ignoreOptionalProblems, IPath externalAnnotationPath) {
+ super(binaryFolder, true, null, externalAnnotationPath, false /* source never an automatic module*/);
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 577b3ee..837ebc1 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
@@ -181,7 +181,8 @@
outputFolder,
entry.fullInclusionPatternChars(),
entry.fullExclusionPatternChars(),
- entry.ignoreOptionalProblems());
+ entry.ignoreOptionalProblems(),
+ externalAnnotationPath);
if (patchedModule != null) {
ModuleEntryProcessor.combinePatchIntoModuleEntry(sourceLocation, patchedModule, moduleEntries);
}
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/SourceFile.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/SourceFile.java
index 8425b8a..891a4ec 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/SourceFile.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/builder/SourceFile.java
@@ -117,6 +117,10 @@
public boolean ignoreOptionalProblems() {
return this.sourceLocation.ignoreOptionalProblems;
}
+@Override
+public String getExternalAnnotationPath() {
+ return this.sourceLocation.externalAnnotationPath;
+}
String typeLocator() {
return this.resource.getProjectRelativePath().toString();
}
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 eb188d4..e855d95 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
@@ -76,7 +76,7 @@
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 = 0x0023;
+public static final byte VERSION = 0x0024;
static final byte SOURCE_FOLDER = 1;
static final byte BINARY_FOLDER = 2;
@@ -338,7 +338,7 @@
if ((folderName = in.readUTF()).length() > 0) sourceFolder = project.getFolder(folderName);
if ((folderName = in.readUTF()).length() > 0) outputFolder = project.getFolder(folderName);
ClasspathMultiDirectory md =
- (ClasspathMultiDirectory) ClasspathLocation.forSourceFolder(sourceFolder, outputFolder, readNames(in), readNames(in), in.readBoolean());
+ (ClasspathMultiDirectory) ClasspathLocation.forSourceFolder(sourceFolder, outputFolder, readNames(in), readNames(in), in.readBoolean(), readNullablePath(in));
if (in.readBoolean())
md.hasIndependentOutputFolder = true;
sourceLocations[i] = md;
@@ -429,6 +429,13 @@
return names;
}
+private static IPath readNullablePath(DataInputStream in) throws IOException {
+ String path = in.readUTF();
+ if (!path.isEmpty())
+ return new Path(path);
+ return null;
+}
+
private static AccessRuleSet readRestriction(DataInputStream in) throws IOException {
int length = in.readInt();
if (length == 0) return null; // no restriction specified
@@ -711,6 +718,7 @@
writeNames(md.inclusionPatterns, out);
writeNames(md.exclusionPatterns, out);
out.writeBoolean(md.ignoreOptionalProblems);
+ writeNullablePath(md.externalAnnotationPath, out);
out.writeBoolean(md.hasIndependentOutputFolder);
}
}
@@ -741,7 +749,7 @@
out.writeUTF(cd.binaryFolder.getFullPath().toString());
out.writeBoolean(cd.isOutputFolder);
writeRestriction(cd.accessRuleSet, out);
- out.writeUTF(cd.externalAnnotationPath != null ? cd.externalAnnotationPath : ""); //$NON-NLS-1$
+ writeNullablePath(cd.externalAnnotationPath, out);
out.writeBoolean(cd.isOnModulePath);
} else if (c instanceof ClasspathJar) {
ClasspathJar jar = (ClasspathJar) c;
@@ -754,7 +762,7 @@
out.writeUTF(jar.resource.getFullPath().toString());
}
writeRestriction(jar.accessRuleSet, out);
- out.writeUTF(jar.externalAnnotationPath != null ? jar.externalAnnotationPath : ""); //$NON-NLS-1$
+ writeNullablePath(jar.externalAnnotationPath, out);
out.writeBoolean(jar.isOnModulePath);
out.writeUTF(jar.compliance == null ? "" : jar.compliance); //$NON-NLS-1$
@@ -763,7 +771,7 @@
out.writeByte(EXTERNAL_JAR);
out.writeUTF(jrt.zipFilename);
writeRestriction(jrt.accessRuleSet, out);
- out.writeUTF(jrt.externalAnnotationPath != null ? jrt.externalAnnotationPath : ""); //$NON-NLS-1$
+ writeNullablePath(jrt.externalAnnotationPath, out);
if (jrt instanceof ClasspathJrtWithReleaseOption)
out.writeUTF(((ClasspathJrtWithReleaseOption) jrt).release);
else
@@ -835,6 +843,10 @@
writeName(names[i], out);
}
+private void writeNullablePath(String path, DataOutputStream out) throws IOException {
+ out.writeUTF(path != null ? path : ""); //$NON-NLS-1$
+}
+
private void writeRestriction(AccessRuleSet accessRuleSet, DataOutputStream out) throws IOException {
if (accessRuleSet == null) {
out.writeInt(0);