Skip to content

Commit

Permalink
Streamline error handling for sealed types. (eclipse-jdt#3123)
Browse files Browse the repository at this point in the history
  • Loading branch information
srikanth-sankaran authored Oct 21, 2024
1 parent 5da56db commit 1511df2
Show file tree
Hide file tree
Showing 9 changed files with 212 additions and 363 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2550,11 +2550,15 @@ public interface IProblem {
int SealedLocalDirectSuperTypeSealed = TypeRelated + 1864;
/** @since 3.28 */
int SealedAnonymousClassCannotExtendSealedType = TypeRelated + 1865;
/** @since 3.28 */
/** @since 3.28
* @deprecated problem no longer generated
*/
int SealedSuperTypeInDifferentPackage = TypeRelated + 1866;
/** @since 3.28 */
/** @since 3.28
* @deprecated problem no longer generated
*/
int SealedSuperTypeDisallowed = TypeRelated + 1867;
/* Java15 errors - end */
/* Java17 Sealed types errors - end */

/**
* @since 3.28
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5842,7 +5842,6 @@ private int generateTypeAnnotationAttributeForTypeDeclaration() {
superInterface.getAllAnnotationContexts(AnnotationTargetTypeConstants.CLASS_EXTENDS, i, allTypeAnnotationContexts);
}
}
// TODO: permittedTypes codegen
TypeParameter[] typeParameters = typeDeclaration.typeParameters;
if (typeParameters != null) {
for (int i = 0, max = typeParameters.length; i < max; i++) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -556,7 +556,7 @@ && isTypeUseDeprecated(type, scope)) {
public boolean isTypeReference() {
return true;
}
public boolean isImplicit() {
public boolean isSynthetic() {
return false;
}
public boolean isWildcard() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ void buildAnonymousTypeBinding(SourceTypeBinding enclosingType, ReferenceBinding
anonymousType.tagBits |= TagBits.HierarchyHasProblems;
anonymousType.setSuperInterfaces(Binding.NO_SUPERINTERFACES);
} if (supertype.isSealed()) {
problemReporter().sealedAnonymousClassCannotExtendSealedType(typeReference, supertype);
problemReporter().anonymousClassCannotExtendSealedType(typeReference, supertype);
anonymousType.tagBits |= TagBits.HierarchyHasProblems;
anonymousType.setSuperInterfaces(Binding.NO_SUPERINTERFACES);
}
Expand Down Expand Up @@ -133,7 +133,7 @@ void buildAnonymousTypeBinding(SourceTypeBinding enclosingType, ReferenceBinding
anonymousType.tagBits |= TagBits.HierarchyHasProblems;
anonymousType.setSuperClass(getJavaLangObject());
} else if (supertype.isSealed()) {
problemReporter().sealedAnonymousClassCannotExtendSealedType(typeReference, supertype);
problemReporter().anonymousClassCannotExtendSealedType(typeReference, supertype);
anonymousType.tagBits |= TagBits.HierarchyHasProblems;
anonymousType.setSuperClass(getJavaLangObject());
}
Expand Down Expand Up @@ -577,7 +577,7 @@ private void checkAndSetModifiers() {
switch (modifiers & (ExtraCompilerModifiers.AccSealed | ExtraCompilerModifiers.AccNonSealed | ClassFileConstants.AccFinal)) {
case ExtraCompilerModifiers.AccSealed, ExtraCompilerModifiers.AccNonSealed, ClassFileConstants.AccFinal, ClassFileConstants.AccDefault : break;
default :
problemReporter().IllegalModifierCombinationForType(sourceType);
problemReporter().illegalModifierCombinationForType(sourceType);
break;
}
if (sourceType.isRecord()) {
Expand Down Expand Up @@ -1190,6 +1190,14 @@ private boolean connectSuperclass() {
} else {
return connectRecordSuperclass();
}
} else if (superclass.isSealed() && sourceType.isLocalType()) {
sourceType.setSuperClass(superclass);
problemReporter().localTypeMayNotBePermittedType(sourceType, superclassRef, superclass);
return false;
} else if (superclass.isSealed() && !(sourceType.isFinal() || sourceType.isSealed() || sourceType.isNonSealed())) {
sourceType.setSuperClass(superclass);
problemReporter().permittedTypeNeedsModifier(sourceType, this.referenceContext, superclass);
return false;
} else if ((superclass.tagBits & TagBits.HierarchyHasProblems) != 0
|| !superclassRef.resolvedType.isValidBinding()) {
sourceType.setSuperClass(superclass);
Expand Down Expand Up @@ -1246,23 +1254,64 @@ private boolean connectEnumSuperclass() {
return !foundCycle;
}

/* Check that the permitted subtype and the sealed type are located in close proximity: either in the same module (if the superclass is in a named module)
* or in the same package (if the superclass is in the unnamed module)
* Return true, if all is well. Report error and return false otherwise,
*/
private boolean checkSealingProximity(ReferenceBinding subType, TypeReference subTypeReference, ReferenceBinding sealedType) {
final PackageBinding sealedTypePackage = sealedType.getPackage();
final ModuleBinding sealedTypeModule = sealedType.module();
if (subType.getPackage() != sealedTypePackage) {
if (sealedTypeModule.isUnnamed())
problemReporter().permittedTypeOutsideOfPackage(subType, sealedType, subTypeReference, sealedTypePackage);
else if (subType.module() != sealedTypeModule)
problemReporter().permittedTypeOutsideOfModule(subType, sealedType, subTypeReference, sealedTypeModule);
}
return true;
}

void connectPermittedTypes() {
SourceTypeBinding sourceType = this.referenceContext.binding;

if (this.referenceContext.permittedTypes != null && (this.referenceContext.permittedTypes.length == 0 || !this.referenceContext.permittedTypes[0].isImplicit())) {
if (this.referenceContext.permittedTypes != null && (this.referenceContext.permittedTypes.length == 0 || !this.referenceContext.permittedTypes[0].isSynthetic())) {
sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES);
try {
sourceType.tagBits |= TagBits.SealingTypeHierarchy;
if (!sourceType.isSealed())
problemReporter().missingSealedModifier(sourceType, this.referenceContext);
int length = this.referenceContext.permittedTypes.length;
ReferenceBinding[] permittedTypeBindings = new ReferenceBinding[length];
int count = 0;
nextPermittedType : for (int i = 0; i < length; i++) {
TypeReference permittedTypeRef = this.referenceContext.permittedTypes[i];
ReferenceBinding permittedType = findPermittedtype(permittedTypeRef);
if (permittedType == null) {
if (permittedType == null || !permittedType.isValidBinding()) {
continue nextPermittedType;
}

if (sourceType.isClass()) {
ReferenceBinding superClass = permittedType.superclass();
superClass = superClass == null ? null : superClass.actualType();
if (!TypeBinding.equalsEquals(sourceType, superClass))
problemReporter().sealedClassNotDirectSuperClassOf(permittedType, permittedTypeRef, sourceType);
} else if (sourceType.isInterface()) {
ReferenceBinding[] superInterfaces = permittedType.superInterfaces();
boolean hierarchyOK = false;
if (superInterfaces != null) {
for (ReferenceBinding superInterface : superInterfaces) {
superInterface = superInterface == null ? null : superInterface.actualType();
if (TypeBinding.equalsEquals(sourceType, superInterface)) {
hierarchyOK = true;
break;
}
}
if (!hierarchyOK)
problemReporter().sealedInterfaceNotDirectSuperInterfaceOf(permittedType, permittedTypeRef, sourceType);
}
}

checkSealingProximity(permittedType, permittedTypeRef, sourceType);

for (int j = 0; j < i; j++) {
if (TypeBinding.equalsEquals(permittedTypeBindings[j], permittedType)) {
problemReporter().duplicatePermittedType(sourceType, permittedTypeRef, permittedType);
Expand All @@ -1287,7 +1336,7 @@ void connectPermittedTypes() {
sourceType.setPermittedTypes(Binding.NO_PERMITTED_TYPES);
if (sourceType.isSealed()) {
if (!sourceType.isLocalType() && !sourceType.isRecord() && !sourceType.isEnum()) // error flagged alread
problemReporter().sealedSealedTypeMissingPermits(sourceType, this.referenceContext);
problemReporter().sealedTypeMissingPermits(sourceType, this.referenceContext);
}
}
}
Expand Down Expand Up @@ -1320,70 +1369,89 @@ private boolean connectRecordSuperclass() {
*/
private boolean connectSuperInterfaces() {
SourceTypeBinding sourceType = this.referenceContext.binding;
sourceType.setSuperInterfaces(Binding.NO_SUPERINTERFACES);
if (this.referenceContext.superInterfaces == null) {
if (sourceType.isAnnotationType() && compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) { // do not connect if source < 1.5 as annotation already got flagged as syntax error) {
ReferenceBinding annotationType = getJavaLangAnnotationAnnotation();
boolean foundCycle = detectHierarchyCycle(sourceType, annotationType, null);
sourceType.setSuperInterfaces(new ReferenceBinding[] { annotationType });
return !foundCycle;
}
return true;
}
if (sourceType.id == TypeIds.T_JavaLangObject) // already handled the case of redefining java.lang.Object
return true;

boolean hasSealedSupertype = sourceType.superclass == null ? false : sourceType.superclass.isSealed();
boolean noProblems = true;
int length = this.referenceContext.superInterfaces.length;
ReferenceBinding[] interfaceBindings = new ReferenceBinding[length];
int count = 0;
nextInterface : for (int i = 0; i < length; i++) {
TypeReference superInterfaceRef = this.referenceContext.superInterfaces[i];
ReferenceBinding superInterface = findSupertype(superInterfaceRef);
if (superInterface == null) { // detected cycle
sourceType.tagBits |= TagBits.HierarchyHasProblems;
noProblems = false;
continue nextInterface;
try {
sourceType.setSuperInterfaces(Binding.NO_SUPERINTERFACES);
if (this.referenceContext.superInterfaces == null) {
if (sourceType.isAnnotationType() && compilerOptions().sourceLevel >= ClassFileConstants.JDK1_5) { // do not connect if source < 1.5 as annotation already got flagged as syntax error) {
ReferenceBinding annotationType = getJavaLangAnnotationAnnotation();
boolean foundCycle = detectHierarchyCycle(sourceType, annotationType, null);
sourceType.setSuperInterfaces(new ReferenceBinding[] { annotationType });
return !foundCycle;
}
return true;
}
if (sourceType.id == TypeIds.T_JavaLangObject) // already handled the case of redefining java.lang.Object
return true;

// check for simple interface collisions
// Check for a duplicate interface once the name is resolved, otherwise we may be confused (i.e. a.b.I and c.d.I)
for (int j = 0; j < i; j++) {
if (TypeBinding.equalsEquals(interfaceBindings[j], superInterface)) {
problemReporter().duplicateSuperinterface(sourceType, superInterfaceRef, superInterface);
int length = this.referenceContext.superInterfaces.length;
ReferenceBinding[] interfaceBindings = new ReferenceBinding[length];
int count = 0;
nextInterface : for (int i = 0; i < length; i++) {
TypeReference superInterfaceRef = this.referenceContext.superInterfaces[i];
ReferenceBinding superInterface = findSupertype(superInterfaceRef);
if (superInterface == null) { // detected cycle
sourceType.tagBits |= TagBits.HierarchyHasProblems;
noProblems = false;
continue nextInterface;
}

if (superInterface.isSealed())
hasSealedSupertype = true;

// check for simple interface collisions
// Check for a duplicate interface once the name is resolved, otherwise we may be confused (i.e. a.b.I and c.d.I)
for (int j = 0; j < i; j++) {
if (TypeBinding.equalsEquals(interfaceBindings[j], superInterface)) {
problemReporter().duplicateSuperinterface(sourceType, superInterfaceRef, superInterface);
sourceType.tagBits |= TagBits.HierarchyHasProblems;
noProblems = false;
continue nextInterface;
}
}
if (!superInterface.isInterface() && (superInterface.tagBits & TagBits.HasMissingType) == 0) {
problemReporter().superinterfaceMustBeAnInterface(sourceType, superInterfaceRef, superInterface);
sourceType.tagBits |= TagBits.HierarchyHasProblems;
noProblems = false;
continue nextInterface;
} else if (superInterface.isAnnotationType()){
problemReporter().annotationTypeUsedAsSuperinterface(sourceType, superInterfaceRef, superInterface);
}
if ((superInterface.tagBits & TagBits.HasDirectWildcard) != 0) {
problemReporter().superTypeCannotUseWildcard(sourceType, superInterfaceRef, superInterface);
sourceType.tagBits |= TagBits.HierarchyHasProblems;
noProblems = false;
continue nextInterface;
}
if ((superInterface.tagBits & TagBits.HierarchyHasProblems) != 0
|| !superInterfaceRef.resolvedType.isValidBinding()) {
sourceType.tagBits |= TagBits.HierarchyHasProblems; // propagate if missing supertype
noProblems &= superInterfaceRef.resolvedType.isValidBinding();
}
if (superInterface.isSealed() && sourceType.isLocalType()) {
problemReporter().localTypeMayNotBePermittedType(sourceType, superInterfaceRef, superInterface);
noProblems = false;
} else if (superInterface.isSealed() && !(sourceType.isFinal() || sourceType.isSealed() || sourceType.isNonSealed())) {
problemReporter().permittedTypeNeedsModifier(sourceType, this.referenceContext, superInterface);
noProblems = false;
}

// only want to reach here when no errors are reported
sourceType.typeBits |= (superInterface.typeBits & TypeIds.InheritableBits);
interfaceBindings[count++] = superInterface;
}
if (!superInterface.isInterface() && (superInterface.tagBits & TagBits.HasMissingType) == 0) {
problemReporter().superinterfaceMustBeAnInterface(sourceType, superInterfaceRef, superInterface);
sourceType.tagBits |= TagBits.HierarchyHasProblems;
noProblems = false;
continue nextInterface;
} else if (superInterface.isAnnotationType()){
problemReporter().annotationTypeUsedAsSuperinterface(sourceType, superInterfaceRef, superInterface);
}
if ((superInterface.tagBits & TagBits.HasDirectWildcard) != 0) {
problemReporter().superTypeCannotUseWildcard(sourceType, superInterfaceRef, superInterface);
sourceType.tagBits |= TagBits.HierarchyHasProblems;
noProblems = false;
continue nextInterface;
// hold onto all correctly resolved superinterfaces
if (count > 0) {
if (count != length)
System.arraycopy(interfaceBindings, 0, interfaceBindings = new ReferenceBinding[count], 0, count);
sourceType.setSuperInterfaces(interfaceBindings);
}
if ((superInterface.tagBits & TagBits.HierarchyHasProblems) != 0
|| !superInterfaceRef.resolvedType.isValidBinding()) {
sourceType.tagBits |= TagBits.HierarchyHasProblems; // propagate if missing supertype
noProblems &= superInterfaceRef.resolvedType.isValidBinding();
} finally {
if (sourceType.isNonSealed() && !hasSealedSupertype) {
if (!sourceType.isRecord() && !sourceType.isLocalType() && !sourceType.isEnum() && !sourceType.isSealed()) // avoid double jeopardy
problemReporter().disallowedNonSealedModifier(sourceType, this.referenceContext);
}
// only want to reach here when no errors are reported
sourceType.typeBits |= (superInterface.typeBits & TypeIds.InheritableBits);
interfaceBindings[count++] = superInterface;
}
// hold onto all correctly resolved superinterfaces
if (count > 0) {
if (count != length)
System.arraycopy(interfaceBindings, 0, interfaceBindings = new ReferenceBinding[count], 0, count);
sourceType.setSuperInterfaces(interfaceBindings);
}
return noProblems;
}
Expand Down
Loading

0 comments on commit 1511df2

Please sign in to comment.