Skip to content

Commit

Permalink
[Patterns] Unconditional pattern in instanceof shouldn't compile at c…
Browse files Browse the repository at this point in the history
…ompliance level of 17 (#3547)

* Fixes #3478
  • Loading branch information
srikanth-sankaran authored Jan 10, 2025
1 parent 9dee5f4 commit 7ddead3
Show file tree
Hide file tree
Showing 9 changed files with 97 additions and 78 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2526,9 +2526,8 @@ public interface IProblem {
*/
int PatternVariableRedefined = Internal + 1781;
/** @since 3.26
* @deprecated
*/
int PatternSubtypeOfExpression = Internal + 1782;
int PatternSubtypeOfExpression = Internal + 1782; // ass backwards naming of API constant :-( It should read ExpressionSubtypeOfPattern
/** @since 3.26
*/
int IllegalModifierForPatternVariable = Internal + 1783;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -277,15 +277,16 @@ public TypeBinding resolveType(BlockScope scope) {
if (expressionType == null || checkedType == null)
return null;

CompilerOptions options = scope.compilerOptions();
if (this.pattern != null) {
if (this.pattern.isApplicable(expressionType, scope, this)) {
if (this.pattern.isApplicable(expressionType, scope, this))
checkForPrimitives(scope, checkedType, expressionType);
}
if (options.complianceLevel < ClassFileConstants.JDK21 && expressionType.isSubtypeOf(checkedType, false))
scope.problemReporter().expressionTypeCannotBeSubtypeOfPatternType(this.expression);
return this.resolvedType = TypeBinding.BOOLEAN;
}

if (!checkedType.isReifiable()) {
CompilerOptions options = scope.compilerOptions();
// Report same as before for older compliances
if (options.complianceLevel < ClassFileConstants.JDK16) {
scope.problemReporter().illegalInstanceOfGenericType(checkedType, this);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1012,7 +1012,8 @@ public void resolve(BlockScope upperScope) {
// special scope for secret locals optimization.
this.scope = new BlockScope(upperScope);

if (enclosingSwitchExpression(upperScope) instanceof SwitchExpression swich) {
SwitchExpression swich;
if ((swich = enclosingSwitchExpression(upperScope)) != null) {
swich.jvmStackVolatile = true; // ought to prepare for any raised exception blowing up the the operand stack to smithereens
}
BlockScope finallyScope = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -728,7 +728,8 @@ public boolean isRecord() {
}

public boolean isRecordWithComponents() { // do records without components make sense ??!
return isRecord() && components() instanceof RecordComponentBinding [] components && components.length > 0;
RecordComponentBinding [] components;
return isRecord() && (components = components()) != null && components.length > 0;
}

/* Answer true if the receiver type can be assigned to the argument type (right)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2166,6 +2166,14 @@ public void illegalRedeclarationOfPatternVar(LocalVariableBinding local, ASTNode
nodeSourceStart(local, location),
nodeSourceEnd(local, location));
}
public void expressionTypeCannotBeSubtypeOfPatternType(ASTNode location) {
this.handle(
IProblem.PatternSubtypeOfExpression, // ass backwards naming of API constant :-(
NoArgument,
NoArgument,
location.sourceStart,
location.sourceEnd);
}
public void duplicateMethodInType(AbstractMethodDeclaration methodDecl, boolean equalParameters, int severity) {
MethodBinding method = methodDecl.binding;
if (equalParameters) {
Expand Down Expand Up @@ -6412,7 +6420,8 @@ public void nullAnnotationUnsupportedLocation(TypeReference type) {
}
private char[][] missingAnalysisAnnotationName(AnnotationBinding[] annotations, LookupEnvironment environment) {
for (AnnotationBinding annotationBinding : annotations) {
if (annotationBinding.getAnnotationType() instanceof ReferenceBinding type
ReferenceBinding type;
if ((type = annotationBinding.getAnnotationType()) != null
&& environment.checkForMissingAnalysisAnnotation(type) != 0)
return type.compoundName;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -202,12 +202,26 @@ public void test009() {
" }\n" +
"}\n",
},
this.complianceLevel < ClassFileConstants.JDK21 ?

"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
"1. ERROR in X.java (at line 3)\n" +
" if (s instanceof Object o) {\n" +
" ^\n" +
"Expression type cannot be a subtype of the Pattern type\n" +
"----------\n" +
"2. ERROR in X.java (at line 4)\n" +
" System.out.println(s1);\n" +
" ^^\n" +
"s1 cannot be resolved to a variable\n" +
"----------\n");
"----------\n" :

"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" System.out.println(s1);\n" +
" ^^\n" +
"s1 cannot be resolved to a variable\n" +
"----------\n");
}
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1076
// ECJ accepts invalid Java code instanceof final Type
Expand Down Expand Up @@ -652,6 +666,8 @@ private void foo(String x) {
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3222
// [Patterns][Ternary] Pattern binding variable not recognized in poly conditional operator expression
public void testIssue3222() {
if (this.complianceLevel < ClassFileConstants.JDK21)
return;
runConformTest(
new String[] {
"X.java",
Expand Down Expand Up @@ -679,6 +695,8 @@ public static void main(String[] args) {
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3222
// [Patterns][Ternary] Pattern binding variable not recognized in poly conditional operator expression
public void testIssue3222_2() {
if (this.complianceLevel < ClassFileConstants.JDK21)
return;
runConformTest(
new String[] {
"X.java",
Expand Down Expand Up @@ -706,6 +724,8 @@ public static void main(String[] args) {
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/3222
// [Patterns][Ternary] Pattern binding variable not recognized in poly conditional operator expression
public void testIssue3222_3() {
if (this.complianceLevel < ClassFileConstants.JDK21)
return;
runConformTest(
new String[] {
"X.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1038,7 +1038,7 @@ public void test019b() {
options);
}
/* Test that we report subtypes of pattern variables used in the same stmt
* As of Java 19, we no longer report error for the above
* As of Java 21, we no longer report error for the above
*/
public void test020() {
Map<String, String> options = getCompilerOptions(true);
Expand All @@ -1055,12 +1055,24 @@ public void test020() {
" }\n" +
"}\n",
},
"----------\n" +
"1. ERROR in X20.java (at line 7)\n" +
" System.out.print(b1);\n" +
" ^^\n" +
"b1 cannot be resolved to a variable\n" +
"----------\n",
this.complianceLevel < ClassFileConstants.JDK21 ?
"----------\n" +
"1. ERROR in X20.java (at line 6)\n" +
" boolean b = (o instanceof String[] s) && s instanceof CharSequence[] s2;\n" +
" ^\n" +
"Expression type cannot be a subtype of the Pattern type\n" +
"----------\n" +
"2. ERROR in X20.java (at line 7)\n" +
" System.out.print(b1);\n" +
" ^^\n" +
"b1 cannot be resolved to a variable\n" +
"----------\n" :
"----------\n" +
"1. ERROR in X20.java (at line 7)\n" +
" System.out.print(b1);\n" +
" ^^\n" +
"b1 cannot be resolved to a variable\n" +
"----------\n",
"",
null,
true,
Expand Down Expand Up @@ -2431,12 +2443,24 @@ public void testBug562392d() {
" }\n" +
"}\n",
},
"----------\n" +
"1. ERROR in X.java (at line 10)\n" +
" System.out.println(abc);\n" +
" ^^^\n" +
"abc cannot be resolved to a variable\n" +
"----------\n",
this.complianceLevel < ClassFileConstants.JDK21 ?
"----------\n" +
"1. ERROR in X.java (at line 4)\n" +
" if (null instanceof T t) {\n" +
" ^^^^\n" +
"Expression type cannot be a subtype of the Pattern type\n" +
"----------\n" +
"2. ERROR in X.java (at line 10)\n" +
" System.out.println(abc);\n" +
" ^^^\n" +
"abc cannot be resolved to a variable\n" +
"----------\n" :
"----------\n" +
"1. ERROR in X.java (at line 10)\n" +
" System.out.println(abc);\n" +
" ^^^\n" +
"abc cannot be resolved to a variable\n" +
"----------\n",
"",
null,
true,
Expand Down Expand Up @@ -4327,7 +4351,8 @@ public static void main(String [] args) {
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/1485
// ECJ hangs when pattern matching code is used in a nested conditional expression.
public void testGHI1485() {

if (this.complianceLevel < ClassFileConstants.JDK21)
return;
runConformTest(
new String[] {
"X.java",
Expand Down Expand Up @@ -4823,6 +4848,8 @@ public static void main(String [] args) {
// https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2104
// [Patterns] Missing boxing conversion after instanceof leads to verify error
public void testBoxing() {
if (this.complianceLevel < ClassFileConstants.JDK21)
return;
runConformTest(
new String[] {
"X.java",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,54 +20,9 @@
import java.util.OptionalInt;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.jdt.core.Flags;
import org.eclipse.jdt.core.IField;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaModelStatusConstants;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.ILocalVariable;
import org.eclipse.jdt.core.IMethod;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IParent;
import org.eclipse.jdt.core.ISourceRange;
import org.eclipse.jdt.core.ISourceReference;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.jdt.core.WorkingCopyOwner;
import org.eclipse.jdt.core.dom.ASTNode;
import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
import org.eclipse.jdt.core.dom.ClassInstanceCreation;
import org.eclipse.jdt.core.dom.Comment;
import org.eclipse.jdt.core.dom.ConstructorInvocation;
import org.eclipse.jdt.core.dom.ExpressionMethodReference;
import org.eclipse.jdt.core.dom.FieldAccess;
import org.eclipse.jdt.core.dom.IBinding;
import org.eclipse.jdt.core.dom.IMethodBinding;
import org.eclipse.jdt.core.dom.IPackageBinding;
import org.eclipse.jdt.core.dom.ITypeBinding;
import org.eclipse.jdt.core.dom.IVariableBinding;
import org.eclipse.jdt.core.dom.Javadoc;
import org.eclipse.jdt.core.dom.LambdaExpression;
import org.eclipse.jdt.core.dom.MethodDeclaration;
import org.eclipse.jdt.core.dom.MethodInvocation;
import org.eclipse.jdt.core.dom.MethodReference;
import org.eclipse.jdt.core.dom.Name;
import org.eclipse.jdt.core.dom.NodeFinder;
import org.eclipse.jdt.core.dom.ParameterizedType;
import org.eclipse.jdt.core.dom.QualifiedName;
import org.eclipse.jdt.core.dom.QualifiedType;
import org.eclipse.jdt.core.dom.SimpleName;
import org.eclipse.jdt.core.dom.SimpleType;
import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
import org.eclipse.jdt.core.dom.SuperConstructorInvocation;
import org.eclipse.jdt.core.dom.SuperMethodInvocation;
import org.eclipse.jdt.core.dom.TagElement;
import org.eclipse.jdt.core.dom.Type;
import org.eclipse.jdt.core.dom.TypeMethodReference;
import org.eclipse.jdt.core.dom.VariableDeclaration;
import org.eclipse.jdt.core.dom.VariableDeclarationFragment;
import org.eclipse.jdt.core.*;
import org.eclipse.jdt.core.dom.*;
import org.eclipse.jdt.core.search.IJavaSearchConstants;
import org.eclipse.jdt.core.search.IJavaSearchScope;
import org.eclipse.jdt.core.search.SearchEngine;
Expand Down Expand Up @@ -245,10 +200,11 @@ public IJavaElement[] codeSelect(int offset, int length) throws JavaModelExcepti
} else if (findTypeDeclaration(node) == null) {
IBinding binding = resolveBinding(node);
if (binding != null && !binding.isRecovered()) {
ITypeBinding declaringClass;
if (node instanceof SuperMethodInvocation && // on `super`
binding instanceof IMethodBinding methodBinding &&
methodBinding.getDeclaringClass() instanceof ITypeBinding typeBinding &&
typeBinding.getJavaElement() instanceof IType type) {
(declaringClass = methodBinding.getDeclaringClass()) != null &&
declaringClass.getJavaElement() instanceof IType type) {
return new IJavaElement[] { type };
}
if (binding instanceof IPackageBinding packageBinding
Expand All @@ -262,11 +218,13 @@ public IJavaElement[] codeSelect(int offset, int length) throws JavaModelExcepti
}
}
// workaround https://github.com/eclipse-jdt/eclipse.jdt.core/issues/2177
IMethodBinding declaringMethod;
ITypeBinding recordBinding;
if (binding instanceof IVariableBinding variableBinding &&
variableBinding.getDeclaringMethod() instanceof IMethodBinding declaringMethod &&
(declaringMethod = variableBinding.getDeclaringMethod()) != null &&
declaringMethod.isCompactConstructor() &&
Arrays.stream(declaringMethod.getParameterNames()).anyMatch(variableBinding.getName()::equals) &&
declaringMethod.getDeclaringClass() instanceof ITypeBinding recordBinding &&
(recordBinding = declaringMethod.getDeclaringClass()) != null &&
recordBinding.isRecord() &&
recordBinding.getJavaElement() instanceof IType recordType &&
recordType.getField(variableBinding.getName()) instanceof SourceField field) {
Expand Down Expand Up @@ -315,10 +273,11 @@ public IJavaElement[] codeSelect(int offset, int length) throws JavaModelExcepti
}
}
}
IField field;
if (binding instanceof IMethodBinding methodBinding &&
methodBinding.isSyntheticRecordMethod() &&
methodBinding.getDeclaringClass().getJavaElement() instanceof IType recordType &&
recordType.getField(methodBinding.getName()) instanceof IField field) {
(field = recordType.getField(methodBinding.getName())) != null) {
return new IJavaElement[] { field };
}
ASTNode bindingNode = currentAST.findDeclaringNode(binding);
Expand Down Expand Up @@ -367,14 +326,15 @@ public IJavaElement[] codeSelect(int offset, int length) throws JavaModelExcepti
return new IJavaElement[] { currentElement };
}
if (insideComment) {
IType type;
String toSearch = trimmedText.isBlank() ? findWord(offset) : trimmedText;
String resolved = ((List<org.eclipse.jdt.core.dom.ImportDeclaration>)currentAST.imports()).stream()
.map(org.eclipse.jdt.core.dom.ImportDeclaration::getName)
.map(Name::toString)
.filter(importedPackage -> importedPackage.endsWith(toSearch))
.findAny()
.orElse(toSearch);
if (this.unit.getJavaProject().findType(resolved) instanceof IType type) {
if ((type = this.unit.getJavaProject().findType(resolved)) != null) {
return new IJavaElement[] { type };
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1249,7 +1249,8 @@ private boolean popBlockContaining(ASTNode soughtStatement) {
RecoveredElement elem = this.currentElement;
while (elem instanceof RecoveredBlock block) {
for (int i=0; i<block.statementCount; i++) {
if (block.statements[i] instanceof RecoveredStatement stmt) {
RecoveredStatement stmt;
if ((stmt = block.statements[i]) != null) {
if (stmt.statement == soughtStatement) {
this.currentElement = block.parent;
// also remove block from the new currentElement:
Expand Down

0 comments on commit 7ddead3

Please sign in to comment.