Skip to content

Commit

Permalink
Add Clinit and validation checks for implicit types
Browse files Browse the repository at this point in the history
- Report an error if implicit class is missing candidate main method
- Report an error when implicit types and its default constructors are
referenced in the same compile session
- Keep the implicit type name same as CU name
- Add Clinit to implicit types
- Fix failing tests and add more tests
  • Loading branch information
jarthana committed Mar 6, 2024
1 parent 21f8969 commit db1c71d
Show file tree
Hide file tree
Showing 34 changed files with 610 additions and 272 deletions.
8 changes: 8 additions & 0 deletions org.eclipse.jdt.core.compiler.batch/.settings/.api_filters
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<component id="org.eclipse.jdt.core.compiler.batch" version="2">
<resource path="META-INF/MANIFEST.MF">
<filter id="924844039">
<message_arguments>
<message_argument value="3.37.50"/>
<message_argument value="3.37.0"/>
</message_arguments>
</filter>
</resource>
<resource path="src/org/eclipse/jdt/core/compiler/CategorizedProblem.java" type="org.eclipse.jdt.core.compiler.CategorizedProblem">
<filter comment="BETA_JAVA22 temporary issue" id="576725006">
<message_arguments>
Expand Down
2 changes: 1 addition & 1 deletion org.eclipse.jdt.core.compiler.batch/grammar/java.g
Original file line number Diff line number Diff line change
Expand Up @@ -449,7 +449,7 @@ ModuleDeclaration ::= ModuleHeader ModuleBody
/:$compliance 9:/
/.$putCase consumeModuleDeclaration(); $break ./

-- JEP 445: unnamed class, this may capture type declarations without unnamed class, this case is fixed/reduced upon completioon of parsing
-- JEP 445: unnamed class, this may capture type declarations without unnamed class, this case is fixed/reduced upon completion of parsing
InternalCompilationUnit ::= UnnamedClassBodyDeclarations
/.$putCase consumeInternalCompilationUnitWithPotentialUnnamedClass(); $break ./
InternalCompilationUnit ::= ImportDeclarations ReduceImports UnnamedClassBodyDeclarations
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2576,6 +2576,10 @@ public interface IProblem {
*/
int IllegalRecordPattern = TypeRelated + 1941;

/**
* @since 3.37
*/
int ImplicitClassMissingMainMethod = PreviewRelated + 1950;

/**
* @since 3.35
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,10 @@ public boolean isStatic() {
return (this.modifiers & ClassFileConstants.AccStatic) != 0;
}

public boolean isCandidateMain() {
return false;
}

/**
* Fill up the method body with statement
*/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2023 Red Hat, Inc. and others.
* Copyright (c) 2023, 2024 Red Hat, Inc. and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -13,18 +13,20 @@
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.MessageFormat;
import java.util.stream.Stream;

import org.eclipse.jdt.internal.compiler.CompilationResult;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.lookup.CompilationUnitScope;

/**
* Represents an unnamed class as defined in JEP 445
* Represents an unnamed class as defined in JEP 463
*/
public class UnnamedClass extends TypeDeclaration {
public class ImplicitTypeDeclaration extends TypeDeclaration {

private static String NAME_TEMPLATE = "<unnamed_class${0}>"; //$NON-NLS-1$
private static String NAME_TEMPLATE = "{0}"; //$NON-NLS-1$

public UnnamedClass(CompilationResult result) {
public ImplicitTypeDeclaration(CompilationResult result) {
super(result);
this.modifiers = ClassFileConstants.AccDefault | ClassFileConstants.AccFinal;

Expand All @@ -40,5 +42,16 @@ public UnnamedClass(CompilationResult result) {
String nameString = MessageFormat.format(NAME_TEMPLATE, classSuffix);
this.name = nameString.toCharArray();
}

@Override
public boolean isImplicitType() {
return true;
}
@Override
public void resolve(CompilationUnitScope upperScope) {
super.resolve(upperScope);
boolean anyMatch = Stream.of(this.methods).anyMatch(m -> m.isCandidateMain());
if (!anyMatch) {
upperScope.problemReporter().implicitClassMissingMainMethod(this);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,13 @@ public boolean isDefaultMethod() {
return (this.modifiers & ExtraCompilerModifiers.AccDefaultMethod) != 0;
}

@Override
public boolean isCandidateMain() {
if (this.binding == null)
return false;
return this.binding.isCandindateMain();
}

@Override
public boolean isMethod() {
return true;
Expand Down Expand Up @@ -293,9 +300,9 @@ public void resolveStatements() {
if (this.typeParameters != null)
this.scope.problemReporter().recordAccessorMethodShouldNotBeGeneric(this);
if (this.binding != null) {
if ((this.binding.modifiers & ClassFileConstants.AccPublic) == 0)
if (!(this.binding.isPublic()))
this.scope.problemReporter().recordAccessorMethodShouldBePublic(this);
if ((this.binding.modifiers & ClassFileConstants.AccStatic) != 0)
if (this.binding.isStatic())
this.scope.problemReporter().recordAccessorMethodShouldNotBeStatic(this);
}
if (this.thrownExceptions != null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1037,7 +1037,9 @@ public final static int kind(int flags) {
public boolean isRecord() {
return (this.modifiers & ExtraCompilerModifiers.AccRecord) != 0;
}

public boolean isImplicitType() {
return false;
}
/*
* Access emulation for a local type
* force to emulation of access to direct enclosing instance.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public enum JavaFeature {
Messages.bind(Messages.unnamed_patterns_and_vars),
new char[][] {},
true),
UNNAMMED_CLASSES_AND_INSTANCE_MAIN_METHODS(ClassFileConstants.JDK22,
IMPLICIT_CLASSES_AND_INSTANCE_MAIN_METHODS(ClassFileConstants.JDK22,
Messages.bind(Messages.unnamed_classes_and_instance_main_methods),
new char[][] {},
true),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,8 @@ private void buildMemberTypes(AccessRestriction accessRestriction) {
ReferenceBinding type = sourceType;
// check that the member does not conflict with an enclosing type
do {
if (this.referenceContext.isImplicitType())
break;
if (CharOperation.equals(type.sourceName, memberContext.name)) {
problemReporter().typeCollidesWithEnclosingType(memberContext);
continue nextMember;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -895,6 +895,25 @@ public final boolean isMain() {
}
return false;
}
public final boolean isCandindateMain() {
if (this.parameters.length > 1) {
return false;
}
if (this.selector.length == 4 && CharOperation.equals(this.selector, TypeConstants.MAIN)
&& ((this.modifiers & ClassFileConstants.AccPrivate) == 0)
&& TypeBinding.VOID == this.returnType) {
if (this.parameters.length == 0) {
return true;
}
if (this.parameters.length == 1) {
TypeBinding paramType = this.parameters[0];
if (paramType.dimensions() == 1 && paramType.leafComponentType().id == TypeIds.T_JavaLangString) {
return true;
}
}
}
return false;
}

/* Answer true if the receiver is a native method
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1813,7 +1813,9 @@ public final boolean isViewedAsDeprecated() {
}
return false;
}

public boolean isImplicitType() {
return false;
}
/**
* Returns the member types of this type sorted by simple name.
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3489,6 +3489,10 @@ final Binding getTypeOrPackage(char[] name, int mask, boolean needResolve) {
insideClassContext = !sourceType.isAnonymousType();
insideTypeAnnotation = false;
if (CharOperation.equals(sourceType.sourceName, name)) {
if (sourceType.isImplicit) {
char[][] compoundName = new char[][] { name };
return new ProblemReferenceBinding(compoundName, null, ProblemReasons.NotFound);
}
if (foundType != null && TypeBinding.notEquals(foundType, sourceType) && foundType.problemId() != ProblemReasons.NotVisible)
return new ProblemReferenceBinding(new char[][]{name}, foundType, ProblemReasons.InheritedNameHidesEnclosingName);
return sourceType;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ public class SourceTypeBinding extends ReferenceBinding {
private SourceTypeBinding nestHost;

private boolean isRecordDeclaration = false;
public boolean isImplicit = false;
public boolean isVarArgs = false; // for record declaration
private FieldBinding[] implicitComponentFields; // cache
private MethodBinding[] recordComponentAccessors = null; // hash maybe an overkill
Expand All @@ -154,6 +155,7 @@ public SourceTypeBinding(char[][] compoundName, PackageBinding fPackage, ClassSc
this.methods = Binding.UNINITIALIZED_METHODS;
this.prototype = this;
this.isRecordDeclaration = scope.referenceContext.isRecord();
this.isImplicit = scope.referenceContext.isImplicitType();
computeId();
}

Expand Down Expand Up @@ -182,6 +184,7 @@ public SourceTypeBinding(SourceTypeBinding prototype) {
this.containerAnnotationType = prototype.containerAnnotationType;
this.tagBits |= TagBits.HasUnresolvedMemberTypes; // see memberTypes()
this.isRecordDeclaration = this.prototype.isRecordDeclaration;
this.isImplicit = this.prototype.isImplicit;
}

private void addDefaultAbstractMethods() {
Expand Down Expand Up @@ -2627,6 +2630,10 @@ public boolean isRecord() {
return this.isRecordDeclaration;
}

@Override
public boolean isImplicitType() {
return this.isImplicit;
}
@Override
public ReferenceBinding containerAnnotationType() {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@
import org.eclipse.jdt.internal.compiler.lookup.MethodScope;
import org.eclipse.jdt.internal.compiler.lookup.TypeConstants;
import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
import org.eclipse.jdt.internal.compiler.parser.Scanner.EmbeddedExpression;
import org.eclipse.jdt.internal.compiler.parser.Scanner.IStringTemplateComponent;
import org.eclipse.jdt.internal.compiler.parser.Scanner.TextFragment;
import org.eclipse.jdt.internal.compiler.parser.Scanner.EmbeddedExpression;
import org.eclipse.jdt.internal.compiler.parser.diagnose.DiagnoseParser;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilation;
import org.eclipse.jdt.internal.compiler.problem.AbortCompilationUnit;
Expand Down Expand Up @@ -4870,21 +4870,22 @@ protected void consumeInternalCompilationUnitWithPotentialUnnamedClass() {
}
}
if (!methods.isEmpty() || !fields.isEmpty()) {
problemReporter().validateJavaFeatureSupport(JavaFeature.UNNAMMED_CLASSES_AND_INSTANCE_MAIN_METHODS, 0, 0);
UnnamedClass unnamedClass = new UnnamedClass(this.compilationUnit.compilationResult);
unnamedClass.methods = methods.toArray(AbstractMethodDeclaration[]::new);
unnamedClass.createDefaultConstructor(false, true);
unnamedClass.fields = fields.toArray(FieldDeclaration[]::new);
unnamedClass.memberTypes = types.toArray(TypeDeclaration[]::new);

unnamedClass.declarationSourceStart = sourceStart;
unnamedClass.declarationSourceEnd = this.scanner.eofPosition - 1;
unnamedClass.bodyStart = sourceStart;
unnamedClass.bodyEnd = this.scanner.eofPosition - 1;
unnamedClass.sourceStart = sourceStart;
unnamedClass.sourceEnd = this.scanner.eofPosition - 1;
types.forEach(type -> type.enclosingType = unnamedClass);
this.compilationUnit.types = new TypeDeclaration[] { unnamedClass };
problemReporter().validateJavaFeatureSupport(JavaFeature.IMPLICIT_CLASSES_AND_INSTANCE_MAIN_METHODS, 0, 0);
ImplicitTypeDeclaration implicitClass = new ImplicitTypeDeclaration(this.compilationUnit.compilationResult);
implicitClass.methods = methods.toArray(AbstractMethodDeclaration[]::new);
implicitClass.createDefaultConstructor(false, true);
implicitClass.fields = fields.toArray(FieldDeclaration[]::new);
implicitClass.memberTypes = types.toArray(TypeDeclaration[]::new);

implicitClass.declarationSourceStart = sourceStart;
implicitClass.declarationSourceEnd = this.scanner.eofPosition - 1;
implicitClass.bodyStart = sourceStart;
implicitClass.bodyEnd = this.scanner.eofPosition - 1;
implicitClass.sourceStart = sourceStart;
implicitClass.sourceEnd = this.scanner.eofPosition - 1;
types.forEach(type -> type.enclosingType = implicitClass);
this.compilationUnit.types = new TypeDeclaration[] { implicitClass };
implicitClass.addClinit();
} else if (types.size() > 0) {
// add types to compilation unit
this.compilationUnit.types = types.toArray(TypeDeclaration[]::new);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12552,6 +12552,13 @@ public void errorReturnInPrologue(Statement stmt) {
stmt.sourceStart,
stmt.sourceEnd);
}
public void implicitClassMissingMainMethod(TypeDeclaration typeDeclaration) {
this.handle(IProblem.ImplicitClassMissingMainMethod,
NoArgument,
NoArgument,
typeDeclaration.sourceStart,
typeDeclaration.sourceStart);
}
public boolean scheduleProblemForContext(Runnable problemComputation) {
if (this.referenceContext != null) {
CompilationResult result = this.referenceContext.compilationResult();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1164,6 +1164,8 @@
1940 = Cannot infer record pattern types for {0}
1941 = Syntax error, record patterns are not allowed here

# Java 22 - Implicitly Declared Classes and Instance main Methods (Second Preview)
1950 = Implicitly declared class must have a candidate main method

1990 = Access to {1}({2}) from the type {0} is emulated by a synthetic accessor method

Expand Down

0 comments on commit db1c71d

Please sign in to comment.