Skip to content

Commit

Permalink
JEP-445: allow parsing unnamed classes
Browse files Browse the repository at this point in the history
Adds a compiler AST UnnamedClass type and grammar rules to parse unnamed
classes.
Adds some tests.

Still missing:
* codegen for UnnamedClass
* checks for Java version compatibility
* More analyseCode to ensure we don't allow more than specified (after
parsing)
* Proper check of existence of a main() method
* ...
  • Loading branch information
mickaelistria committed Oct 17, 2023
1 parent 398ba0b commit d56b9af
Show file tree
Hide file tree
Showing 37 changed files with 2,153 additions and 1,775 deletions.
29 changes: 22 additions & 7 deletions org.eclipse.jdt.core.compiler.batch/grammar/java.g
Original file line number Diff line number Diff line change
@@ -1,7 +1,15 @@
--main options
%options ACTION, AN=JavaAction.java, GP=java,
%options FILE-PREFIX=java, ESCAPE=$, PREFIX=TokenName, OUTPUT-SIZE=125 ,
%options NOGOTO-DEFAULT, SINGLE-PRODUCTIONS, LALR=1 , TABLE,
%options ACTION
%options AN=JavaAction.java
%options GP=java,
%options FILE-PREFIX=java
%options ESCAPE=$
%options PREFIX=TokenName
%options OUTPUT-SIZE=125
%options NOGOTO-DEFAULT
%options SINGLE-PRODUCTIONS
%options LALR=1
%options TABLE

--error recovering options.....
%options ERROR_MAPS
Expand Down Expand Up @@ -432,10 +440,6 @@ InternalCompilationUnit ::= PackageDeclaration TypeDeclarations
/.$putCase consumeInternalCompilationUnitWithTypes(); $break ./
InternalCompilationUnit ::= ImportDeclarations ReduceImports
/.$putCase consumeInternalCompilationUnit(); $break ./
InternalCompilationUnit ::= TypeDeclarations
/.$putCase consumeInternalCompilationUnitWithTypes(); $break ./
InternalCompilationUnit ::= ImportDeclarations ReduceImports TypeDeclarations
/.$putCase consumeInternalCompilationUnitWithTypes(); $break ./
InternalCompilationUnit ::= $empty
/.$putCase consumeEmptyInternalCompilationUnit(); $break ./
/:$readableName CompilationUnit:/
Expand All @@ -451,6 +455,12 @@ 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
InternalCompilationUnit ::= UnnamedClassBodyDeclarations
/.$putCase consumeInternalCompilationUnitWithPotentialUnnamedClass(); $break ./
InternalCompilationUnit ::= ImportDeclarations ReduceImports UnnamedClassBodyDeclarations
/.$putCase consumeInternalCompilationUnitWithPotentialUnnamedClass(); $break ./

-- to work around shift/reduce conflicts, we allow Modifiersopt in order to support annotations
-- in a module declaration, and then report errors if any modifiers other than annotations are
-- encountered
Expand Down Expand Up @@ -744,6 +754,11 @@ ClassBodyDeclarations ::= ClassBodyDeclarations ClassBodyDeclaration
ClassBodyDeclaration -> ClassMemberDeclaration
ClassBodyDeclaration -> StaticInitializer
ClassBodyDeclaration -> ConstructorDeclaration

UnnamedClassBodyDeclarations -> ClassMemberDeclaration
UnnamedClassBodyDeclarations -> ClassMemberDeclaration UnnamedClassBodyDeclarations
/.$putCase consumeUnnamedClassBodyDeclarations(); $break ./
/:$readableName UnnamedClassBodyDeclarations:/
--1.1 feature
ClassBodyDeclaration ::= Diet NestedMethod CreateInitializer Block
/.$putCase consumeClassBodyDeclaration(); $break ./
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2553,6 +2553,9 @@ public interface IProblem {
* @noreference preview feature
*/
int CannotInferRecordPatternTypes = PreviewRelated + 1940;

int unnamedIsMissingMainMethod = PreviewRelated + 1942;
int unnamedRequiresJava21 = PreviewRelated + 1943;

/**
* @since 3.36
Expand All @@ -2564,4 +2567,5 @@ public interface IProblem {
* @since 3.35
*/
int SyntheticAccessorNotEnclosingMethod = MethodRelated + 1990;

}
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ public int compare(Object o1, Object o2) {
public TypeDeclaration[] types;
public ModuleDeclaration moduleDeclaration;
public int[][] comments;
public UnnamedClass unnamedClass;

public boolean ignoreFurtherInvestigation = false; // once pointless to investigate due to errors
public boolean ignoreMethodBodies = false;
Expand Down Expand Up @@ -132,6 +133,9 @@ public void analyseCode() {
if (this.ignoreFurtherInvestigation)
return;
try {
if (this.unnamedClass != null) {
this.unnamedClass.analyseCode(this.scope);
}
if (this.types != null) {
for (int i = 0, count = this.types.length; i < count; i++) {
this.types[i].analyseCode(this.scope);
Expand Down Expand Up @@ -449,7 +453,7 @@ public char[] getMainTypeName() {
}

public boolean isEmpty() {
return (this.currentPackage == null) && (this.imports == null) && (this.types == null);
return (this.currentPackage == null) && (this.imports == null) && (this.types == null) && this.unnamedClass == null;
}

public boolean isPackageInfo() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -443,4 +443,18 @@ public void traverse(
public TypeParameter[] typeParameters() {
return this.typeParameters;
}

public boolean isMainMethodCandidate() {
if (!"main".equals(new String(this.selector))) { //$NON-NLS-1$
return false;
}
if (this.arguments == null || this.arguments.length == 0) {
return true;
}
if (this.arguments.length == 1) {
TypeBinding resolvedType = this.arguments[0].type.getTypeBinding(this.scope);
// TODO check String... or String[]
}
return false;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*******************************************************************************
* Copyright (c) 2023 Red Hat, Inc. and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.jdt.internal.compiler.ast;

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

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

public UnnamedClass(CompilationResult result) {
super(result);
this.modifiers = ClassFileConstants.AccDefault | ClassFileConstants.AccFinal;
this.name = "<unnamed class>".toCharArray(); //$NON-NLS-1$
}

@Override
public void analyseCode(CompilationUnitScope unitScope) {
if (this.ignoreFurtherInvestigation)
return;
try {
boolean foundMain = false;
for (AbstractMethodDeclaration method : this.methods) {
if (method instanceof MethodDeclaration methodDeclaration) {
foundMain |= methodDeclaration.isMainMethodCandidate();
} else {
//unitScope.problemReporter().specialMethodsNotAllowedInUnnamedClass(method);
}
}
if (!foundMain) {
unitScope.problemReporter().unnamedClassMustHaveMainMethod();
}
// TODO flowAnalyseCode(null, FlowInfo.initial(Integer.MAX_VALUE));
} catch (AbortType e) {
this.ignoreFurtherInvestigation = true;
}
super.analyseCode(unitScope);
}

// @Override
// public void generateCode(CompilationUnitScope unitScope) {
// SourceTypeBinding unnamedBinding = this.binding;
// this.binding = toNamedBinding(unitScope);
// super.generateCode(unitScope);
// this.binding = unnamedBinding;
// }
//
// private SourceTypeBinding toNamedBinding(CompilationUnitScope scope) {
// SourceTypeBinding res = new SourceTypeBinding(this.binding) {
// @Override
// public boolean isPrototype() {
// return true;
// }
// };
// res.compoundName = new char[][] { scope.referenceContext.getMainTypeName() };
// return res;
// }

}
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
import org.eclipse.jdt.internal.compiler.ast.TypeDeclaration;
import org.eclipse.jdt.internal.compiler.ast.TypeParameter;
import org.eclipse.jdt.internal.compiler.ast.TypeReference;
import org.eclipse.jdt.internal.compiler.ast.UnnamedClass;
import org.eclipse.jdt.internal.compiler.ast.TypeReference.AnnotationPosition;
import org.eclipse.jdt.internal.compiler.classfmt.ClassFileConstants;
import org.eclipse.jdt.internal.compiler.classfmt.ExternalAnnotationProvider;
Expand Down Expand Up @@ -140,7 +141,9 @@ public class SourceTypeBinding extends ReferenceBinding {
private MethodBinding[] recordComponentAccessors = null; // hash maybe an overkill

public SourceTypeBinding(char[][] compoundName, PackageBinding fPackage, ClassScope scope) {
this.compoundName = compoundName;
this.compoundName = scope.referenceContext instanceof UnnamedClass unnamed
? new char[][] { unnamed.getCompilationUnitDeclaration().getMainTypeName() }
: compoundName;
this.fPackage = fPackage;
this.fileName = scope.referenceCompilationUnit().getFileName();
this.modifiers = scope.referenceContext.modifiers;
Expand Down
Loading

0 comments on commit d56b9af

Please sign in to comment.