From c362ae3b4eb530797ee3282cf7ccd2f415cefaed Mon Sep 17 00:00:00 2001 From: David Thompson Date: Fri, 3 Nov 2023 13:58:46 -0400 Subject: [PATCH] Fix error messages related to `_` Signed-off-by: David Thompson --- .../eclipse/jdt/core/compiler/IProblem.java | 6 +++++ .../ast/AbstractVariableDeclaration.java | 12 +++++++++ .../jdt/internal/compiler/ast/Argument.java | 16 ++++++------ .../compiler/ast/FieldDeclaration.java | 3 +++ .../compiler/ast/LocalDeclaration.java | 2 +- .../internal/compiler/ast/TypePattern.java | 2 +- .../internal/compiler/impl/JavaFeature.java | 2 +- .../internal/compiler/lookup/BlockScope.java | 3 ++- .../jdt/internal/compiler/messages.properties | 2 +- .../jdt/internal/compiler/parser/Parser.java | 25 +++++++++++++------ .../compiler/problem/ProblemReporter.java | 8 ++++++ .../compiler/problem/messages.properties | 3 +++ .../jdt/internal/compiler/util/Messages.java | 2 +- .../regression/CompilerInvocationTests.java | 2 ++ .../NegativeLambdaExpressionsTest.java | 8 ++++-- 15 files changed, 74 insertions(+), 22 deletions(-) diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java index 003cdb30a64..fc5960097fe 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/core/compiler/IProblem.java @@ -2564,4 +2564,10 @@ public interface IProblem { * @since 3.35 */ int SyntheticAccessorNotEnclosingMethod = MethodRelated + 1990; + + /** + * @since 3.36 + * @noreference preview feature + */ + int FieldNameCannotBeUnderscore = PreviewRelated + 2000; } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AbstractVariableDeclaration.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AbstractVariableDeclaration.java index e85cdfffec4..b105e4634d8 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AbstractVariableDeclaration.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/AbstractVariableDeclaration.java @@ -16,6 +16,7 @@ *******************************************************************************/ package org.eclipse.jdt.internal.compiler.ast; +import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants; import org.eclipse.jdt.internal.compiler.flow.FlowContext; import org.eclipse.jdt.internal.compiler.flow.FlowInfo; import org.eclipse.jdt.internal.compiler.lookup.BlockScope; @@ -144,4 +145,15 @@ public void setDepth(int depth) { public void setFieldIndex(int depth) { // do nothing by default } + + /** + * Returns true if this variable is an unnamed variable (_) and false otherwise. + * + * @param scope used to determine source level + */ + public boolean isUnnamed(BlockScope scope) { + return this.name.length == 1 && this.name[0] == '_' + && scope.compilerOptions().sourceLevel >= ClassFileConstants.JDK21 + && scope.compilerOptions().enablePreviewFeatures; + } } diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Argument.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Argument.java index 2d19f0957a9..43052d2eaca 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Argument.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/Argument.java @@ -123,12 +123,14 @@ public TypeBinding bind(MethodScope scope, TypeBinding typeBinding, boolean used if (existingVariable != null && existingVariable.isValidBinding()){ final boolean localExists = existingVariable instanceof LocalVariableBinding; if (localExists && this.hiddenVariableDepth == 0) { - if ((this.bits & ASTNode.ShadowsOuterLocal) != 0 && scope.isLambdaSubscope()) { - scope.problemReporter().lambdaRedeclaresArgument(this); - } else if (scope.referenceContext instanceof CompactConstructorDeclaration) { - // skip error reporting - hidden params - already reported in record components - } else { - scope.problemReporter().redefineArgument(this); + if (!this.isUnnamed(scope)) { + if ((this.bits & ASTNode.ShadowsOuterLocal) != 0 && scope.isLambdaSubscope()) { + scope.problemReporter().lambdaRedeclaresArgument(this); + } else if (scope.referenceContext instanceof CompactConstructorDeclaration) { + // skip error reporting - hidden params - already reported in record components + } else { + scope.problemReporter().redefineArgument(this); + } } } else { boolean isSpecialArgument = false; @@ -234,7 +236,7 @@ public TypeBinding resolveForCatch(BlockScope scope) { } } Binding existingVariable = scope.getBinding(this.name, Binding.VARIABLE, this, false /*do not resolve hidden field*/); - if (existingVariable != null && existingVariable.isValidBinding()){ + if (existingVariable != null && existingVariable.isValidBinding() && !isUnnamed(scope)) { if (existingVariable instanceof LocalVariableBinding && this.hiddenVariableDepth == 0) { scope.problemReporter().redefineArgument(this); } else { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java index 714ef30c957..19a732f4c58 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/FieldDeclaration.java @@ -70,6 +70,9 @@ public FieldDeclaration( char[] name, int sourceStart, int sourceEnd) { } public FlowInfo analyseCode(MethodScope initializationScope, FlowContext flowContext, FlowInfo flowInfo) { + if (this.isUnnamed(initializationScope)) { + initializationScope.problemReporter().fieldNameCannotBeUnderscore(this); + } if (this.binding != null && !this.binding.isUsed() && this.binding.isOrEnclosedByPrivateType()) { if (!initializationScope.referenceCompilationUnit().compilationResult.hasSyntaxError) { if (!this.isARecordComponent) // record component used by implicit methods diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java index 5f6f34fe969..fe1f57cd852 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/LocalDeclaration.java @@ -299,7 +299,7 @@ public void resolve(BlockScope scope, boolean isPatternVariable) { } Binding existingVariable = scope.getBinding(this.name, Binding.VARIABLE, this, false /*do not resolve hidden field*/); - if (existingVariable != null && existingVariable.isValidBinding()){ + if (existingVariable != null && existingVariable.isValidBinding() && !this.isUnnamed(scope)) { boolean localExists = existingVariable instanceof LocalVariableBinding; if (localExists && (this.bits & ASTNode.ShadowsOuterLocal) != 0 && scope.isLambdaSubscope() && this.hiddenVariableDepth == 0) { scope.problemReporter().lambdaRedeclaresLocal(this); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java index f24a4e10804..e2880d140bd 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/ast/TypePattern.java @@ -52,7 +52,7 @@ public void collectPatternVariablesToScope(LocalVariableBinding[] variables, Blo if (this.resolvedType == null) { this.resolveType(scope); } - if (this.local != null && this.local.binding != null) { + if (this.local != null && this.local.binding != null && !this.local.isUnnamed(scope)) { LocalVariableBinding binding = this.local.binding; if (variables != null) { for (LocalVariableBinding variable : variables) { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/impl/JavaFeature.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/impl/JavaFeature.java index 8f3b0f7af1c..2815b5cd1a4 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/impl/JavaFeature.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/impl/JavaFeature.java @@ -66,7 +66,7 @@ public enum JavaFeature { new char[][] {}, false), UNNAMMED_PATTERNS_AND_VARS(ClassFileConstants.JDK21, - Messages.bind(Messages.unnammed_patterns_and_vars), + Messages.bind(Messages.unnamed_patterns_and_vars), new char[][] {}, true), UNNAMMED_CLASSES_AND_INSTANCE_MAIN_METHODS(ClassFileConstants.JDK21, diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java index 45e1260308f..0e42bc525b0 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/lookup/BlockScope.java @@ -289,7 +289,8 @@ void computeLocalVariablePositions(int ilocal, int initOffset, CodeStream codeSt // do not report fake used variable if (local.useFlag == LocalVariableBinding.UNUSED && (local.declaration != null) // unused (and non secret) local - && ((local.declaration.bits & ASTNode.IsLocalDeclarationReachable) != 0)) { // declaration is reachable + && ((local.declaration.bits & ASTNode.IsLocalDeclarationReachable) != 0) // declaration is reachable + && !local.declaration.isUnnamed(local.declaringScope)) { if (local.isCatchParameter()) { problemReporter().unusedExceptionParameter(local.declaration); // report unused catch arguments diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/messages.properties b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/messages.properties index c0167ee88e8..365531f03fe 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/messages.properties +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/messages.properties @@ -74,5 +74,5 @@ records = Records sealed_types = Sealed Types pattern_matching_switch = Pattern Matching in Switch record_patterns = Record Pattern -unnammed_patterns_and_vars = Unnammed Patterns and Variables +unnamed_patterns_and_vars = Unnamed Patterns and Variables unnamed_classes_and_instance_main_methods = Unnamed Classes and Instance Main Methods \ No newline at end of file diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java index 94f106d7a0e..9c646cfb633 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/parser/Parser.java @@ -986,6 +986,7 @@ protected int actFromTokenOrSynthetic(int previousAct) { protected boolean parsingJava15Plus; protected boolean parsingJava17Plus; protected boolean parsingJava18Plus; +protected boolean parsingJava21Plus; protected boolean previewEnabled; protected boolean parsingJava11Plus; protected int unstackedAct = ERROR_ACTION; @@ -1017,6 +1018,7 @@ public Parser(ProblemReporter problemReporter, boolean optimizeStringLiterals) { this.parsingJava15Plus = this.options.sourceLevel >= ClassFileConstants.JDK15; this.parsingJava17Plus = this.options.sourceLevel >= ClassFileConstants.JDK17; this.parsingJava18Plus = this.options.sourceLevel >= ClassFileConstants.JDK18; + this.parsingJava21Plus = this.options.sourceLevel >= ClassFileConstants.JDK21; this.previewEnabled = this.options.sourceLevel == ClassFileConstants.getLatestJDKLevel() && this.options.enablePreviewFeatures; this.astLengthStack = new int[50]; this.expressionLengthStack = new int[30]; @@ -7485,9 +7487,9 @@ protected void consumeRule(int act) { consumePatternListopt(); break; - case 369 : if (DEBUG) { System.out.println("PushLeftBrace ::="); } //$NON-NLS-1$ - consumePushLeftBrace(); - break; + case 368 : if (DEBUG) { System.out.println("ComponentPatternList ::= ComponentPatternList COMMA..."); } //$NON-NLS-1$ + consumePatternList(); + break; case 370 : if (DEBUG) { System.out.println("TypePattern ::= Modifiersopt Type UNDERSCORE"); } //$NON-NLS-1$ consumeTypePattern(); @@ -9115,8 +9117,13 @@ protected void consumeLambdaHeader() { if (argument.isReceiver()) { problemReporter().illegalThis(argument); } - if (argument.name.length == 1 && argument.name[0] == '_') - problemReporter().illegalUseOfUnderscoreAsAnIdentifier(argument.sourceStart, argument.sourceEnd, true); // true == lambdaParameter + if (this.parsingJava8Plus && !(this.parsingJava21Plus && this.previewEnabled) && argument.name.length == 1 && argument.name[0] == '_') { + if (this.parsingJava21Plus) { + problemReporter().validateJavaFeatureSupport(JavaFeature.UNNAMMED_PATTERNS_AND_VARS, argument.sourceStart, argument.sourceEnd); + } else { + problemReporter().illegalUseOfUnderscoreAsAnIdentifier(argument.sourceStart, argument.sourceEnd, true); // true == lambdaParameter + } + } } LambdaExpression lexp = (LambdaExpression) this.astStack[this.astPtr]; lexp.setArguments(arguments); @@ -13944,8 +13951,12 @@ protected void pushIdentifier(char [] identifier, long position) { stackLength); } this.identifierLengthStack[this.identifierLengthPtr] = 1; - if (this.parsingJava8Plus && identifier.length == 1 && identifier[0] == '_' && !this.processingLambdaParameterList) { - problemReporter().illegalUseOfUnderscoreAsAnIdentifier((int) (position >>> 32), (int) position, this.parsingJava9Plus); + if (this.parsingJava8Plus && !(this.parsingJava21Plus && this.previewEnabled) && identifier.length == 1 && identifier[0] == '_' && !this.processingLambdaParameterList) { + if (this.parsingJava21Plus) { + problemReporter().validateJavaFeatureSupport(JavaFeature.UNNAMMED_PATTERNS_AND_VARS, (int) (position >>> 32), (int) position); + } else { + problemReporter().illegalUseOfUnderscoreAsAnIdentifier((int) (position >>> 32), (int) position, this.parsingJava9Plus); + } } } protected void pushIdentifier() { diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java index e7f89c084c6..146b24011e1 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/ProblemReporter.java @@ -12466,6 +12466,14 @@ public void falseLiteralInGuard(Expression exp) { exp.sourceStart, exp.sourceEnd); } +public void fieldNameCannotBeUnderscore(FieldDeclaration fieldDeclaration) { + this.handle( + IProblem.FieldNameCannotBeUnderscore, + NoArgument, + NoArgument, + fieldDeclaration.sourceStart, + fieldDeclaration.sourceEnd); +} public boolean scheduleProblemForContext(Runnable problemComputation) { if (this.referenceContext != null) { CompilationResult result = this.referenceContext.compilationResult(); diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties index fbb7c33afa3..49c29f76146 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/problem/messages.properties @@ -1158,6 +1158,9 @@ 1990 = Access to {1}({2}) from the type {0} is emulated by a synthetic accessor method +# Unnamed variables and patterns (preview in Java 21, set for release in Java 22) +2000 = Field name cannot be '_' + ### ELABORATIONS ## Access restrictions 78592 = The type ''{1}'' is not API (restriction on classpath entry ''{0}'') diff --git a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/util/Messages.java b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/util/Messages.java index 525fb152c8a..9ddc0e5279c 100644 --- a/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/util/Messages.java +++ b/org.eclipse.jdt.core.compiler.batch/src/org/eclipse/jdt/internal/compiler/util/Messages.java @@ -124,7 +124,7 @@ private Messages() { public static String sealed_types; public static String pattern_matching_switch; public static String record_patterns; - public static String unnammed_patterns_and_vars; + public static String unnamed_patterns_and_vars; public static String unnamed_classes_and_instance_main_methods; static { diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java index 362d466096d..ea731232ca4 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/CompilerInvocationTests.java @@ -1328,6 +1328,7 @@ class ProblemAttributes { expectedProblemAttributes.put("ClassExtendFinalRecord", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); expectedProblemAttributes.put("RecordErasureIncompatibilityInCanonicalConstructor", new ProblemAttributes(CategorizedProblem.CAT_TYPE)); expectedProblemAttributes.put("JavadocInvalidModule", new ProblemAttributes(CategorizedProblem.CAT_INTERNAL)); + expectedProblemAttributes.put("FieldNameCannotBeUnderscore", new ProblemAttributes(CategorizedProblem.CAT_PREVIEW_RELATED)); StringBuffer failures = new StringBuffer(); StringBuffer correctResult = new StringBuffer(70000); Field[] fields = (iProblemClass = IProblem.class).getFields(); @@ -2429,6 +2430,7 @@ class ProblemAttributes { expectedProblemAttributes.put("ClassExtendFinalRecord", SKIP); expectedProblemAttributes.put("RecordErasureIncompatibilityInCanonicalConstructor", SKIP); expectedProblemAttributes.put("JavadocInvalidModule", SKIP); + expectedProblemAttributes.put("FieldNameCannotBeUnderscore", SKIP); Map constantNamesIndex = new HashMap(); Field[] fields = JavaCore.class.getFields(); diff --git a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java index a4cdf0fc934..9823cc9b763 100644 --- a/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java +++ b/org.eclipse.jdt.core.tests.compiler/src/org/eclipse/jdt/core/tests/compiler/regression/NegativeLambdaExpressionsTest.java @@ -7371,6 +7371,10 @@ public void testIntersectionCast() { public void testUnderScoreParameter() { String level = this.complianceLevel >= ClassFileConstants.JDK9 ? "ERROR" : "WARNING"; String errorMessage = this.complianceLevel >= ClassFileConstants.JDK9 ? "\'_\' is a keyword from source level 9 onwards, cannot be used as identifier\n" : "\'_\' should not be used as an identifier, since it is a reserved keyword from source level 1.8 on\n"; + if (this.complianceLevel >= ClassFileConstants.JDK21) { + errorMessage = "Unnamed Patterns and Variables is a preview feature and disabled by default. Use --enable-preview to enable\n"; + } + String otherErrorMessage = this.complianceLevel >= ClassFileConstants.JDK21 ? errorMessage : "\'_\' is a keyword from source level 9 onwards, cannot be used as identifier\n"; this.runNegativeTest( new String[] { "X.java", @@ -7389,7 +7393,7 @@ public void testUnderScoreParameter() { "1. ERROR in X.java (at line 6)\n" + " F f = (int _) -> {\n" + " ^\n" + - "\'_\' is a keyword from source level 9 onwards, cannot be used as identifier\n" + + otherErrorMessage + "----------\n" + "2. "+ level +" in X.java (at line 8)\n" + " F f2 = _ -> {};\n" + @@ -7399,7 +7403,7 @@ public void testUnderScoreParameter() { "3. ERROR in X.java (at line 8)\n" + " F f2 = _ -> {};\n" + " ^\n" + - "\'_\' is a keyword from source level 9 onwards, cannot be used as identifier\n" + + otherErrorMessage + "----------\n" ); }