Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJay Arthanareeswaran2020-08-10 14:27:52 +0000
committerJay Arthanareeswaran2020-08-12 09:01:53 +0000
commit0ba0ff5a1b9449f6f9284ceec17a8416913bd368 (patch)
treeb36e1108a4455440c521f508c69eed26cf15ce23
parent582b99fc133bf07e2dc720a11f38d6333b0d7189 (diff)
downloadeclipse.jdt.core-0ba0ff5a1b9449f6f9284ceec17a8416913bd368.tar.gz
eclipse.jdt.core-0ba0ff5a1b9449f6f9284ceec17a8416913bd368.tar.xz
eclipse.jdt.core-0ba0ff5a1b9449f6f9284ceec17a8416913bd368.zip
Bug 563182 - [15] Records - Safevarargs
Change-Id: I445d50d7ea3c98080c5dc065136e57210d42cd94 Signed-off-by: Jay Arthanareeswaran <jarthana@in.ibm.com>
-rw-r--r--org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java185
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java4
-rw-r--r--org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/SourceTypeBinding.java77
3 files changed, 235 insertions, 31 deletions
diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java
index f8391383bc..a09b58fdb6 100644
--- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java
+++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/RecordsRestrictedClassTest.java
@@ -63,9 +63,8 @@ public class RecordsRestrictedClassTest extends AbstractRegressionTest {
runConformTest(testFiles, expectedOutput, getCompilerOptions());
}
- @SuppressWarnings({ "unchecked", "rawtypes" })
@Override
- protected void runConformTest(String[] testFiles, String expectedOutput, Map customOptions) {
+ protected void runConformTest(String[] testFiles, String expectedOutput, Map<String, String> customOptions) {
Runner runner = new Runner();
runner.testFiles = testFiles;
runner.expectedOutputString = expectedOutput;
@@ -7200,4 +7199,186 @@ public void testBug565786_001() throws IOException, ClassFormatException {
" public I$R();\n";
verifyClassFile(expectedOutput, "I$R.class", ClassFileBytesDisassembler.SYSTEM);
}
+// Test that without an explicit canonical constructor, we
+// report the warning on the record type.
+public void testBug563182_01() {
+ Map<String, String> customOptions = getCompilerOptions();
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "class X<T> {\n" +
+ " record Point<T> (T ... args) { // 1\n" +
+ " }\n" +
+ " public static void main(String[] args) {}\n"+
+ "}\n",
+ },
+ "----------\n" +
+ "1. WARNING in X.java (at line 2)\n" +
+ " record Point<T> (T ... args) { // 1\n" +
+ " ^^^^\n" +
+ "Type safety: Potential heap pollution via varargs parameter args\n" +
+ "----------\n",
+ null,
+ true,
+ new String[] {"--enable-preview"},
+ customOptions);
+}
+//Test that in presence of an explicit canonical constructor that is NOT annotated with @SafeVarargs,
+// we don't report the warning on the record type but report on the explicit canonical constructor
+public void testBug563182_02() {
+ Map<String, String> customOptions = getCompilerOptions();
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "class X<T> {\n" +
+ " record Point<T> (T ... args) { // 1\n" +
+ " Point(T ... args) { // 2\n" +
+ " this.args = args;\n" +
+ " }\n" +
+ " }\n" +
+ " public static void main(String[] args) {}\n"+
+ "}\n",
+ },
+ "----------\n" +
+ "1. WARNING in X.java (at line 3)\n" +
+ " Point(T ... args) { // 2\n" +
+ " ^^^^\n" +
+ "Type safety: Potential heap pollution via varargs parameter args\n" +
+ "----------\n",
+ null,
+ true,
+ new String[] {"--enable-preview"},
+ customOptions);
+}
+//Test that in presence of an explicit canonical constructor that IS annotated with @SafeVarargs,
+//we don't report the warning on neither the record type nor the explicit canonical constructor
+public void testBug563182_03() {
+ Map<String, String> customOptions = getCompilerOptions();
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "class X<T> {\n" +
+ " record Point<T> (T ... args) { // 1\n" +
+ " @SafeVarargs\n" +
+ " Point(T ... args) { // 2\n" +
+ " this.args = args;\n" +
+ " }\n" +
+ " }\n" +
+ " public static void main(String[] args) {}\n"+
+ "}\n",
+ },
+ "",
+ null,
+ true,
+ new String[] {"--enable-preview"},
+ customOptions);
+}
+//Test that in presence of a compact canonical constructor that is NOT annotated with @SafeVarargs,
+//we don't report the warning on the compact canonical constructor but report on the record type
+public void testBug563182_04() {
+ Map<String, String> customOptions = getCompilerOptions();
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "class X<T> {\n" +
+ " record Point<T> (T ... args) { // 1\n" +
+ " Point { // 2\n" +
+ " }\n" +
+ " }\n" +
+ " public static void main(String[] args) {}\n"+
+ "}\n",
+ },
+ "----------\n" +
+ "1. WARNING in X.java (at line 2)\n" +
+ " record Point<T> (T ... args) { // 1\n" +
+ " ^^^^\n" +
+ "Type safety: Potential heap pollution via varargs parameter args\n" +
+ "----------\n",
+ null,
+ true,
+ new String[] {"--enable-preview"},
+ customOptions);
+}
+//Test that in presence of a compact canonical constructor that IS annotated with @SafeVarargs,
+//we don't report the warning on neither the record type nor the compact canonical constructor
+public void testBug563182_05() {
+ Map<String, String> customOptions = getCompilerOptions();
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "class X<T> {\n" +
+ " record Point<T> (T ... args) { // 1\n" +
+ " @SafeVarargs\n" +
+ " Point { // 2\n" +
+ " }\n" +
+ " }\n" +
+ " public static void main(String[] args) {}\n"+
+ "}\n",
+ },
+ "",
+ null,
+ true,
+ new String[] {"--enable-preview"},
+ customOptions);
+}
+//Test that in presence of a non-canonical constructor that is annotated with @SafeVarargs,
+//we don't report the warning on the non-canonical constructor but report on the record type
+public void testBug563182_06() {
+ Map<String, String> customOptions = getCompilerOptions();
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "class X<T> {\n" +
+ " record Point<T> (T ... args) { // 1\n" +
+ " @SafeVarargs\n" +
+ " Point (String s, T ... t) {\n" +
+ " this(t);\n" +
+ " }\n" +
+ " }\n" +
+ " public static void main(String[] args) {}\n"+
+ "}\n",
+ },
+ "----------\n" +
+ "1. WARNING in X.java (at line 2)\n" +
+ " record Point<T> (T ... args) { // 1\n" +
+ " ^^^^\n" +
+ "Type safety: Potential heap pollution via varargs parameter args\n" +
+ "----------\n",
+ null,
+ true,
+ new String[] {"--enable-preview"},
+ customOptions);
+}
+//Test that in presence of a non-canonical constructor that is NOT annotated with @SafeVarargs,
+//we don't report the warning on the non-canonical constructor but report on the record type
+public void testBug563182_07() {
+ Map<String, String> customOptions = getCompilerOptions();
+ this.runNegativeTest(
+ new String[] {
+ "X.java",
+ "class X<T> {\n" +
+ " record Point<T> (T ... args) { // 1\n" +
+ " Point (String s, T ... t) {\n" +
+ " this(t);\n" +
+ " }\n" +
+ " }\n" +
+ " public static void main(String[] args) {}\n"+
+ "}\n",
+ },
+ "----------\n" +
+ "1. WARNING in X.java (at line 2)\n" +
+ " record Point<T> (T ... args) { // 1\n" +
+ " ^^^^\n" +
+ "Type safety: Potential heap pollution via varargs parameter args\n" +
+ "----------\n" +
+ "2. WARNING in X.java (at line 3)\n" +
+ " Point (String s, T ... t) {\n" +
+ " ^\n" +
+ "Type safety: Potential heap pollution via varargs parameter t\n" +
+ "----------\n",
+ null,
+ true,
+ new String[] {"--enable-preview"},
+ customOptions);
+}
}
diff --git a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
index 6f86e09694..561aa4f141 100644
--- a/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
+++ b/org.eclipse.jdt.core/compiler/org/eclipse/jdt/internal/compiler/lookup/MethodBinding.java
@@ -8,6 +8,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann - Contributions for
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 ad852be591..4ba0466251 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
@@ -8,6 +8,10 @@
*
* SPDX-License-Identifier: EPL-2.0
*
+ * This is an implementation of an early-draft specification developed under the Java
+ * Community Process (JCP) and is made available for testing and evaluation purposes
+ * only. The code is not compatible with any specification of the JCP.
+ *
* Contributors:
* IBM Corporation - initial API and implementation
* Stephan Herrmann <stephan@cs.tu-berlin.de> - Contributions for
@@ -2069,32 +2073,38 @@ private int getImplicitCanonicalConstructor() {
}
return -1;
}
-private MethodBinding[] checkAndGetExplicitCanonicalConstructors() {
- List<MethodBinding> ec = new ArrayList<>();
- if (!this.isRecordDeclaration)
- return ec.toArray(new MethodBinding[0]);
-
+private void checkAndGetExplicitCanonicalConstructors() {
RecordComponentBinding[] recComps = this.components;
int nRecordComponents = recComps.length;
- if (this.methods != null && this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK14) {
- for (MethodBinding method : this.methods) {
- if (!method.isConstructor() || (method.tagBits & TagBits.isImplicit) != 0
- || method.parameters.length != nRecordComponents)
- continue;
- boolean isEC = true;
- for (int j = 0; j < nRecordComponents; ++j) {
- if (TypeBinding.notEquals(method.parameters[j], recComps[j].type)) {
- isEC = false;
- break;
- }
- }
- if (isEC) {
- ec.add(method);
- checkRecordCanonicalConstructor(method);
+ MethodBinding implicitCanConstr = null;
+ MethodBinding explictCanConstr = null;
+ for (MethodBinding method : this.methods) {
+ if (!method.isConstructor())
+ continue;
+ if ((method.tagBits & TagBits.isImplicit) != 0) {
+ implicitCanConstr = method;
+ continue;
+ }
+ if (method.parameters.length != nRecordComponents)
+ continue;
+ boolean isEC = true;
+ for (int j = 0; j < nRecordComponents; ++j) {
+ if (TypeBinding.notEquals(method.parameters[j], recComps[j].type)) {
+ isEC = false;
+ break;
}
}
+ if (isEC) {
+ checkRecordCanonicalConstructor(method);
+ // Just exit after sighting the first explicit canonical constructor,
+ // because there can only be one.
+ explictCanConstr = method;
+ break;
+ }
+ }
+ if (explictCanConstr == null && implicitCanConstr != null) {
+ checkAndFlagHeapPollution(implicitCanConstr, implicitCanConstr.sourceMethod());
}
- return ec.toArray(new MethodBinding[0]);
}
private int getImplicitMethod(char[] name) {
if (this.methods != null && this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK14) {
@@ -2158,9 +2168,12 @@ public MethodBinding[] methods() {
// find & report collision cases
boolean complyTo15OrAbove = this.scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5;
boolean compliance16 = this.scope.compilerOptions().complianceLevel == ClassFileConstants.JDK1_6;
- int recordCanonIndex = getImplicitCanonicalConstructor();
- computeRecordComponents();
- checkAndGetExplicitCanonicalConstructors();
+ int recordCanonIndex = -1;
+ if (this.isRecordDeclaration) {
+ recordCanonIndex = getImplicitCanonicalConstructor();
+ computeRecordComponents();
+ checkAndGetExplicitCanonicalConstructors();
+ }
int recordEqualsIndex = getImplicitMethod(TypeConstants.EQUALS);
for (int i = 0, length = this.methods.length; i < length; i++) {
@@ -2728,10 +2741,8 @@ private MethodBinding resolveTypesWithSuspendedTempErrorHandlingPolicy(MethodBin
&& !(sourceLevel >= ClassFileConstants.JDK9 && method.isPrivate())) {
methodDecl.scope.problemReporter().safeVarargsOnNonFinalInstanceMethod(method);
}
- } else if (method.parameters != null && method.parameters.length > 0 && method.isVarargs()) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=337795
- if (!method.parameters[method.parameters.length - 1].isReifiable()) {
- methodDecl.scope.problemReporter().possibleHeapPollutionFromVararg(methodDecl.arguments[methodDecl.arguments.length - 1]);
- }
+ } else if ((method.tagBits & TagBits.IsCanonicalConstructor) == 0) {
+ checkAndFlagHeapPollution(method, methodDecl);
}
}
@@ -2827,6 +2838,14 @@ private MethodBinding resolveTypesWithSuspendedTempErrorHandlingPolicy(MethodBin
}
return method;
}
+
+private void checkAndFlagHeapPollution(MethodBinding method, AbstractMethodDeclaration methodDecl) {
+ if (method.parameters != null && method.parameters.length > 0 && method.isVarargs()) { // https://bugs.eclipse.org/bugs/show_bug.cgi?id=337795
+ if (!method.parameters[method.parameters.length - 1].isReifiable()) {
+ methodDecl.scope.problemReporter().possibleHeapPollutionFromVararg(methodDecl.arguments[methodDecl.arguments.length - 1]);
+ }
+ }
+}
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=391108
private static void rejectTypeAnnotatedVoidMethod(AbstractMethodDeclaration methodDecl) {
Annotation[] annotations = methodDecl.annotations;
@@ -3455,7 +3474,7 @@ public MethodBinding getRecordComponentAccessor(char[] name) {
return accessor;
}
public void computeRecordComponents() {
- if (!this.isRecordDeclaration || this.implicitComponentFields != null)
+ if (this.implicitComponentFields != null)
return;
RecordComponent[] recComps = this.scope.referenceContext.recordComponents;
List<FieldBinding> list = new ArrayList<>();

Back to the top