Skip to content

Commit

Permalink
[Sealed Types] Compiler does not handle non-sealed contextual keyword…
Browse files Browse the repository at this point in the history
… correctly (eclipse-jdt#2660)

* Fixes eclipse-jdt#2654
  • Loading branch information
srikanth-sankaran authored Jul 8, 2024
1 parent 07c9984 commit bbb6d6c
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 72 deletions.
3 changes: 2 additions & 1 deletion org.eclipse.jdt.core.compiler.batch/grammar/java.g
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ Goal ::= '@' TypeAnnotations
-- JSR 354 Reconnaissance mission.
Goal ::= '->' YieldStatement
Goal ::= '->' SwitchLabelCaseLhs
-- JSR 360 Restricted
-- JEP 409 Sealed types Reconnaissance mission.
Goal ::= RestrictedIdentifiersealed Modifiersopt
Goal ::= RestrictedIdentifierpermits PermittedSubtypes
-- jsr 427 --
Expand Down Expand Up @@ -3206,3 +3206,4 @@ UNDERSCORE ::= '_'
$end
-- need a carriage return after the $end


Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,7 @@

import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Predicate;

import org.eclipse.jdt.core.compiler.CharOperation;
Expand Down Expand Up @@ -210,9 +208,6 @@ enum ScanContext {
// text block support - 13
protected int textBlockOffset = -1;

//Java 15 - first _ keyword appears
Map<String, Integer> _Keywords = null;

private final CharDeduplication deduplication = CharDeduplication.getThreadLocalInstance();

public Scanner() {
Expand Down Expand Up @@ -3438,10 +3433,28 @@ else if ((data[index] == 'o')
case 3 :
if ((data[++index] == 'e') && (data[++index] == 'w'))
return TokenNamenew;
else {
int token = checkFor_KeyWord(index - 1, length, data);
return token != TokenNameNotAToken ? token : TokenNameIdentifier;
}
else if (data == this.source // not handling unicode as of now in non-sealed
&& (data[index] == 'o')
&& (data[++index] == 'n')
&& (data[++index] == '-')
&& (data[++index] == 's')
&& (data[++index] == 'e')
&& (data[++index] == 'a')
&& (data[++index] == 'l')
&& (data[++index] == 'e')
&& (data[++index] == 'd')
&& !ScannerHelper.isJavaIdentifierPart(data[++index])) {
this.currentPosition += 7;
int t = disambiguatesRestrictedIdentifierWithLookAhead(x-> !isInModuleDeclaration() && this.sourceLevel >= ClassFileConstants.JDK17, TokenNamenon_sealed, Goal.RestrictedIdentifierSealedGoal);
if (t == TokenNamenon_sealed) {
return TokenNamenon_sealed;
} else {
this.currentPosition -= 7;
return TokenNameIdentifier;
}

} else
return TokenNameIdentifier;
case 4 :
if ((data[++index] == 'u') && (data[++index] == 'l') && (data[++index] == 'l'))
return TokenNamenull;
Expand Down Expand Up @@ -3514,7 +3527,9 @@ else if ((data[index] == 'o')
&& (data[++index] == 'i')
&& (data[++index] == 't')
&& (data[++index] == 's')) {
return disambiguatedRestrictedIdentifierpermits(TokenNameRestrictedIdentifierpermits);
return disambiguatesRestrictedIdentifierWithLookAhead(x -> !isInModuleDeclaration() && this.sourceLevel >= ClassFileConstants.JDK17,
TokenNameRestrictedIdentifierpermits,
Goal.RestrictedIdentifierPermitsGoal);
} else
return TokenNameIdentifier;
}
Expand Down Expand Up @@ -3616,7 +3631,9 @@ else if ((data[index] == 'c')
&& (data[++index] == 'l')
&& (data[++index] == 'e')
&& (data[++index] == 'd')) {
return disambiguatedRestrictedIdentifiersealed(TokenNameRestrictedIdentifiersealed);
return disambiguatesRestrictedIdentifierWithLookAhead(x -> !isInModuleDeclaration() && this.sourceLevel >= ClassFileConstants.JDK17,
TokenNameRestrictedIdentifiersealed,
Goal.RestrictedIdentifierSealedGoal);
} else
return TokenNameIdentifier;
case 8 :
Expand Down Expand Up @@ -3799,25 +3816,6 @@ else if ((data[++index] == 'h')
}
}


private int checkFor_KeyWord(int index, int length, char[] data) {
if (this._Keywords == null) {
this._Keywords = new HashMap<>(0);
if (JavaFeature.RECORDS.isSupported(this.complianceLevel, this.previewEnabled)) {
this._Keywords.put("non-sealed", TerminalTokens.TokenNamenon_sealed); //$NON-NLS-1$
}
}
for (String key : this._Keywords.keySet()) {
if (CharOperation.prefixEquals(key.toCharArray(), data, true /* isCaseSensitive */, index)) {
this.currentPosition = this.currentPosition - length + key.length();
if (this.currentPosition < this.eofPosition)
this.currentCharacter = data[this.currentPosition];
return this._Keywords.get(key);
}
}
return TokenNameNotAToken;
}

public int scanNumber(boolean dotPrefix) throws InvalidInputException {

//when entering this method the currentCharacter is the first
Expand Down Expand Up @@ -4624,7 +4622,7 @@ private static class Goal {
static int YieldStatementRule = 0;
static int SwitchLabelCaseLhsRule = 0;
static int[] RestrictedIdentifierSealedRule;
static int[] RestrictedIdentifierPermitsRule;
static int RestrictedIdentifierPermitsRule;
static int[] PatternRules;

static Goal LambdaParameterListGoal;
Expand All @@ -4646,7 +4644,6 @@ private static class Goal {
static {

List<Integer> ridSealed = new ArrayList<>(2);
List<Integer> ridPermits = new ArrayList<>();
List<Integer> patternStates = new ArrayList<>();
for (int i = 1; i <= ParserBasicInformation.NUM_RULES; i++) { // 0 == $acc
// TODO: Change to switch
Expand All @@ -4672,7 +4669,7 @@ private static class Goal {
ridSealed.add(i);
else
if ("PermittedSubtypes".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$
ridPermits.add(i);
RestrictedIdentifierPermitsRule = i;
else
if ("SwitchLabelCaseLhs".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$
SwitchLabelCaseLhsRule = i;
Expand All @@ -4683,14 +4680,10 @@ private static class Goal {
if ("Pattern".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$
patternStates.add(i);
else
if ("ParenthesizedPattern".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$
patternStates.add(i);
else
if ("RecordPattern".equals(Parser.name[Parser.non_terminal_index[Parser.lhs[i]]])) //$NON-NLS-1$
patternStates.add(i);
}
RestrictedIdentifierSealedRule = ridSealed.stream().mapToInt(Integer :: intValue).toArray(); // overkill but future-proof
RestrictedIdentifierPermitsRule = ridPermits.stream().mapToInt(Integer :: intValue).toArray();
PatternRules = patternStates.stream().mapToInt(Integer :: intValue).toArray();

LambdaParameterListGoal = new Goal(TokenNameARROW, new int[] { TokenNameARROW }, LambdaParameterListRule);
Expand Down Expand Up @@ -5049,17 +5042,6 @@ private boolean mayBeAtAnYieldStatement() {
return false;
}
}
private boolean mayBeAtASealedRestricedIdentifier(int restrictedIdentifier) {
if (isInModuleDeclaration())
return false;
switch (restrictedIdentifier) {
case TokenNameRestrictedIdentifiersealed:
break;
case TokenNameRestrictedIdentifierpermits:
break;
}
return true;
}
int disambiguatedRestrictedIdentifierrecord(int restrictedIdentifierToken) {
// and here's the kludge
if (restrictedIdentifierToken != TokenNameRestrictedIdentifierrecord)
Expand Down Expand Up @@ -5191,26 +5173,6 @@ private boolean disambiguateYieldWithLookAhead() {
}
return false; // IIE event;
}
int disambiguatedRestrictedIdentifierpermits(int restrictedIdentifierToken) {
// and here's the kludge
if (restrictedIdentifierToken != TokenNameRestrictedIdentifierpermits)
return restrictedIdentifierToken;
if (!JavaFeature.RECORDS.isSupported(this.complianceLevel, this.previewEnabled))
return TokenNameIdentifier;

return disambiguatesRestrictedIdentifierWithLookAhead(this::mayBeAtASealedRestricedIdentifier,
restrictedIdentifierToken, Goal.RestrictedIdentifierPermitsGoal);
}
int disambiguatedRestrictedIdentifiersealed(int restrictedIdentifierToken) {
// and here's the kludge
if (restrictedIdentifierToken != TokenNameRestrictedIdentifiersealed)
return restrictedIdentifierToken;
if (!JavaFeature.RECORDS.isSupported(this.complianceLevel, this.previewEnabled))
return TokenNameIdentifier;

return disambiguatesRestrictedIdentifierWithLookAhead(this::mayBeAtASealedRestricedIdentifier,
restrictedIdentifierToken, Goal.RestrictedIdentifierSealedGoal);
}
int disambiguatedRestrictedIdentifierWhen(int restrictedIdentifierToken) {
// and here's the kludge
if (restrictedIdentifierToken != TokenNameRestrictedIdentifierWhen)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1099,10 +1099,31 @@ public void testBug563806_034() {
"}\n",
},
"----------\n" +
"1. ERROR in p1\\X.java (at line 2)\n" +
"1. ERROR in p1\\X.java (at line 1)\n" +
" package p1;\n" +
" ^^^^^^^^^^^\n" +
"Syntax error on token(s), misplaced construct(s)\n" +
"----------\n" +
"2. ERROR in p1\\X.java (at line 1)\n" +
" package p1;\n" +
"public non-sealed @interface X {\n" +
" ^^^^^^^^^^^^^^^^^^^^^^^\n" +
"Syntax error on token(s), misplaced construct(s)\n" +
"----------\n" +
"3. ERROR in p1\\X.java (at line 2)\n" +
" public non-sealed @interface X {\n" +
" ^\n" +
"An interface X declared as non-sealed should have a sealed direct superinterface\n" +
" ^^^^^^\n" +
"Syntax error, insert \"Identifier (\" to complete MethodHeaderName\n" +
"----------\n" +
"4. ERROR in p1\\X.java (at line 2)\n" +
" public non-sealed @interface X {\n" +
" ^^^^^^\n" +
"Syntax error, insert \")\" to complete MethodDeclaration\n" +
"----------\n" +
"5. ERROR in p1\\X.java (at line 2)\n" +
" public non-sealed @interface X {\n" +
" ^^^^^^\n" +
"Syntax error, insert \";\" to complete RecordBodyDeclarations\n" +
"----------\n");
}
public void testBug563806_035() {
Expand Down Expand Up @@ -6259,4 +6280,46 @@ class Circle {
"Syntax error on tokens, delete these tokens\n" +
"----------\n");
}

// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2654
// [Sealed Types] Compiler does not handle non-sealed contextual keyword correctly
public void testIssue2654() {
runNegativeTest(
new String[] {
"X.java",
"""
non-sealed public class X {
int foo(int non, int sealed) {
return non-sealed;
}
}
"""
},
"----------\n" +
"1. ERROR in X.java (at line 1)\n" +
" non-sealed public class X {\n" +
" ^\n" +
"A class X declared as non-sealed should have either a sealed direct superclass or a sealed direct superinterface\n" +
"----------\n");
}

// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2654
// [Sealed Types] Compiler does not handle non-sealed contextual keyword correctly
public void testIssue2654_2() {
runConformTest(
new String[] {
"X.java",
"""
public class X {
static int foo(int non, int sealed) {
return non-sealed;
}
public static void main(String [] args) {
System.out.println(foo(142, 100));
}
}
"""
},
"42");
}
}

0 comments on commit bbb6d6c

Please sign in to comment.