diff options
author | Stephan Herrmann | 2021-02-09 12:01:47 +0000 |
---|---|---|
committer | Stephan Herrmann | 2021-02-09 13:00:20 +0000 |
commit | 4c1496dfbd11b3ad81e483bf468882e56f00e870 (patch) | |
tree | 1b96271752e433b436a2692727f85da0b70378ba | |
parent | 7744b1ebb9582f7f915951b6ebfc4a79830e785e (diff) | |
download | eclipse.jdt.core-4c1496dfbd11b3ad81e483bf468882e56f00e870.tar.gz eclipse.jdt.core-4c1496dfbd11b3ad81e483bf468882e56f00e870.tar.xz eclipse.jdt.core-4c1496dfbd11b3ad81e483bf468882e56f00e870.zip |
Bug 571055 - [null][batch] EEA for sources also during batch compilationI20210210-1800I20210210-0910I20210209-1800
Change-Id: Ie232aebb4d764746a211d2213976aee954b7ea06
8 files changed, 141 insertions, 7 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationBatchCompilerTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationBatchCompilerTest.java index f1aa48c907..2a4158b899 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationBatchCompilerTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NullAnnotationBatchCompilerTest.java @@ -1022,4 +1022,98 @@ public class NullAnnotationBatchCompilerTest extends AbstractBatchCompilerTest { "3 problems (3 warnings)\n", true); } + public void testBug571055_explicit() throws IOException { + runTestBug571055(false, false); + } + public void testBug571055_inherit() throws IOException { + runTestBug571055(true, false); + } + public void testBug571055_dedicatedAnnotationPath() throws IOException { + runTestBug571055(false, true); + } + private void runTestBug571055(boolean inheritAnnotations, boolean dedicatedAnnotationPath) throws IOException { + String annots_dir = Util.getOutputDirectory() + File.separator + "annots"; + String annots_api = annots_dir + File.separator + "api"; + new File(annots_api).mkdirs(); + Util.createFile( + annots_api + File.separator + "Foo.eea", + "class api/Foo\n" + + "m\n" + + " (Ljava/lang/String;)Ljava/lang/String;\n" + + " (L1java/lang/String;)L0java/lang/String;\n"); + if (!inheritAnnotations) { + // 'manually' establish consistency: + String annots_impl = annots_dir + File.separator + "impl"; + new File(annots_impl).mkdirs(); + Util.createFile( + annots_impl + File.separator + "FooImpl.eea", + "class impl/FooImpl\n" + + "m\n" + + " (Ljava/lang/String;)Ljava/lang/String;\n" + + " (L1java/lang/String;)L0java/lang/String;\n"); + } + + String[] testFiles = new String[] { + "java/lang/annotation/ElementType.java", + ELEMENT_TYPE_18_CONTENT, + "org/eclipse/jdt/annotation/NonNull.java", + NONNULL_ANNOTATION_18_CONTENT, + "org/eclipse/jdt/annotation/Nullable.java", + NULLABLE_ANNOTATION_18_CONTENT, + "org/eclipse/jdt/annotation/DefaultLocation.java", + DEFAULT_LOCATION_CONTENT, + "api/Foo.java", + "package api;\n" + + "public interface Foo {\n" + + " String m(String a);\n" + + "}\n", + "impl/FooImpl.java", + "package impl;\n" + + "import api.Foo;\n" + + "public class FooImpl implements Foo {\n" + + " public String m(String a) { return null; }\n" + // ensure Foo & FooImpl are seen with consistent nullness + "}\n", + "test1/Test1.java", + "package test1;\n" + + "\n" + + "import api.Foo;\n" + + "\n" + + "public class Test1 {\n" + + " void test(Foo api) {\n" + + " String result = api.m(null);\n" + + " System.out.println(result.toUpperCase());\n" + + " }\n" + + "}\n" + }; + + String commandLine; + if (dedicatedAnnotationPath) { + commandLine = "-annotationpath \"" + annots_dir + "\" " + + " -1.8 -proc:none -err:+nullAnnot -warn:+null " + + (inheritAnnotations ? " -warn:+inheritNullAnnot ": "") + + " \"" + OUTPUT_DIR + "\""; + } else { + commandLine = "-annotationpath CLASSPATH " + + " -1.8 -proc:none -err:+nullAnnot -warn:+null " + + (inheritAnnotations ? " -warn:+inheritNullAnnot ": "") + + " -classpath \"" + annots_dir + "\" " + + " \"" + OUTPUT_DIR + "\""; + } + + // expect eea-motivated problems in Test1 but no in FooImpl: + String expectedCompilerMessage = + "----------\n" + + "1. ERROR in " + OUTPUT_DIR + File.separator + "test1" + File.separator + "Test1.java (at line 7)\n" + + " String result = api.m(null);\n" + + " ^^^^\n" + + "Null type mismatch: required '@NonNull String' but the provided value is null\n" + + "----------\n" + + "2. WARNING in " + OUTPUT_DIR + File.separator + "test1" + File.separator + "Test1.java (at line 8)\n" + + " System.out.println(result.toUpperCase());\n" + + " ^^^^^^\n" + + "Potential null pointer access: The variable result may be null at this location\n" + + "----------\n" + + "2 problems (1 error, 1 warning)\n"; + this.runNegativeTest(testFiles, commandLine, "", expectedCompilerMessage, 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 1fcce4beec..f04669c6cf 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 @@ -156,6 +156,8 @@ public NameEnvironmentAnswer findClass(char[] typeName, String qualifiedPackageN } @Override public boolean hasAnnotationFileFor(String qualifiedTypeName) { + if (this.zipFile == null) + return false; return this.zipFile.getEntry(qualifiedTypeName+ExternalAnnotationProvider.ANNOTATION_FILE_SUFFIX) != null; } @Override diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/CompilationUnit.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/CompilationUnit.java index 80d9d1d20f..aa9c1d100e 100644 --- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/CompilationUnit.java +++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/CompilationUnit.java @@ -15,6 +15,7 @@ package org.eclipse.jdt.internal.compiler.batch; import java.io.File; import java.io.IOException; +import java.util.function.Function; import org.eclipse.jdt.core.compiler.CharOperation; import org.eclipse.jdt.internal.compiler.env.ICompilationUnit; @@ -39,6 +40,11 @@ public class CompilationUnit implements ICompilationUnit { // be written. private boolean ignoreOptionalProblems; private ModuleBinding moduleBinding; + /** + * annotation path can only be retrieved once the qualified type name is known. + * This is the provided function for computing the annotation path from that type name. + */ + private Function<String,String> annotationPathProvider; public CompilationUnit(char[] contents, String fileName, String encoding) { this(contents, fileName, encoding, null); @@ -49,6 +55,12 @@ public CompilationUnit(char[] contents, String fileName, String encoding, } public CompilationUnit(char[] contents, String fileName, String encoding, String destinationPath, boolean ignoreOptionalProblems, String modName) { + this(contents, fileName, encoding, destinationPath, ignoreOptionalProblems, modName, null); +} +public CompilationUnit(char[] contents, String fileName, String encoding, String destinationPath, + boolean ignoreOptionalProblems, String modName, Function<String,String> annotationPathProvider) +{ + this.annotationPathProvider = annotationPathProvider; this.contents = contents; if (modName != null) this.module = modName.toCharArray(); @@ -130,4 +142,10 @@ public ModuleBinding module(LookupEnvironment rootEnvironment) { public String getDestinationPath() { return this.destinationPath; } +@Override +public String getExternalAnnotationPath(String qualifiedTypeName) { + if (this.annotationPathProvider != null) + return this.annotationPathProvider.apply(qualifiedTypeName); + return null; +} } diff --git a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java index 8ea28566b4..b88205fa84 100644 --- a/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java +++ b/org.eclipse.jdt.core/batch/org/eclipse/jdt/internal/compiler/batch/Main.java @@ -72,6 +72,7 @@ import java.util.Properties; import java.util.ResourceBundle; import java.util.Set; import java.util.StringTokenizer; +import java.util.function.Function; import org.eclipse.jdt.core.compiler.CategorizedProblem; import org.eclipse.jdt.core.compiler.CharOperation; @@ -91,6 +92,7 @@ import org.eclipse.jdt.internal.compiler.batch.ModuleFinder.AddExport; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.classfmt.ClassFileReader; import org.eclipse.jdt.internal.compiler.classfmt.ClassFormatException; +import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider; import org.eclipse.jdt.internal.compiler.env.AccessRestriction; import org.eclipse.jdt.internal.compiler.env.AccessRule; import org.eclipse.jdt.internal.compiler.env.AccessRuleSet; @@ -3439,9 +3441,28 @@ public CompilationUnit[] getCompilationUnits() { // if we got exception during canonicalization, fall back to the name that was specified fileName = this.filenames[i]; } + Function<String,String> annotationPathProvider = null; + if (this.annotationsFromClasspath) { + annotationPathProvider = (String qualifiedTypeName) -> { + for (Classpath classpathEntry : this.checkedClasspaths) { + if (classpathEntry.hasAnnotationFileFor(qualifiedTypeName.replace('.', '/'))) + return classpathEntry.getPath(); + } + return null; + }; + } else if (this.annotationPaths != null) { + annotationPathProvider = (String qualifiedTypeName) -> { + String eeaFileName = '/'+qualifiedTypeName.replace('.', '/')+ExternalAnnotationProvider.ANNOTATION_FILE_SUFFIX; + for (String annotationPath : this.annotationPaths) { + if (new File(annotationPath+eeaFileName).exists()) + return annotationPath; + } + return null; + }; + } units[i] = new CompilationUnit(null, fileName, encoding, this.destinationPaths[i], shouldIgnoreOptionalProblems(this.ignoreOptionalProblemsFromFolders, fileName.toCharArray()), - this.modNames[i]); + this.modNames[i], annotationPathProvider); } } } 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 5e2cdfa662..7d846729cf 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 @@ -78,6 +78,6 @@ default String getDestinationPath() { * Answers a path for external annotations that has been configured for * the providing classpath entry, or <code>null</code>. */ -default String getExternalAnnotationPath() { return null; } +default String getExternalAnnotationPath(String qualifiedTypeName) { 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 a2affaf198..fee27d3e8f 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 @@ -525,7 +525,7 @@ public class ClassScope extends Scope { environment().setAccessRestriction(sourceType, accessRestriction); ICompilationUnit compilationUnit = this.referenceContext.compilationResult.getCompilationUnit(); if (compilationUnit != null && compilerOptions().isAnnotationBasedNullAnalysisEnabled) { - String externalAnnotationPath = compilationUnit.getExternalAnnotationPath(); + String externalAnnotationPath = compilationUnit.getExternalAnnotationPath(CharOperation.toString(sourceType.compoundName)); if (externalAnnotationPath != null) { ExternalAnnotationSuperimposer.apply(sourceType, externalAnnotationPath); } 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 7a1b542b22..d2b9fcc2e9 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 @@ -2834,15 +2834,14 @@ private MethodBinding resolveTypesWithSuspendedTempErrorHandlingPolicy(MethodBin } } } + if (this.externalAnnotationProvider != null) + ExternalAnnotationSuperimposer.annotateMethodBinding(method, arguments, this.externalAnnotationProvider, this.environment); if (compilerOptions.storeAnnotations) createArgumentBindings(method, compilerOptions); // need annotations resolved already at this point if (foundReturnTypeProblem) 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, arguments, this.externalAnnotationProvider, this.environment); - } return method; } 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 891a4ec581..289d2f5d50 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 @@ -118,7 +118,7 @@ public boolean ignoreOptionalProblems() { return this.sourceLocation.ignoreOptionalProblems; } @Override -public String getExternalAnnotationPath() { +public String getExternalAnnotationPath(String qualifiedTypeName) { return this.sourceLocation.externalAnnotationPath; } String typeLocator() { |