Bug 577119 - Expressions does not work when records defined as inner classes are used

 Eclipse tries to parse the source code with a parser which only supports Java 1.4, so
it fails to detect the 'records' coming from Java 17. This prevents using the 'Expression' view
when the class contains inner record classes, and there are local variables with that type

Change-Id: I38ec4f6f1ffc66c67807fcdc064a9cf8c4353e19
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.debug/+/187494
Tested-by: JDT Bot <jdt-bot@eclipse.org>
Reviewed-by: Sarika Sinha <sarika.sinha@in.ibm.com>
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationSourceGenerator.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationSourceGenerator.java
index 7e1848a..26e65b9 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationSourceGenerator.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/EvaluationSourceGenerator.java
@@ -305,7 +305,7 @@
 	private void createEvaluationSourceFromSource(String source, IType type,
 			int line, boolean createInAStaticMethod, IJavaProject project)
 			throws DebugException {
-		ASTParser parser = ASTParser.newParser(AST.JLS4);
+		ASTParser parser = ASTParser.newParser(AST.getJLSLatest());
 		parser.setSource(source.toCharArray());
 		Map<String, String> options = getCompilerOptions(project);
 		String sourceLevel = project.getOption(JavaCore.COMPILER_SOURCE, true);
diff --git a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/SourceBasedSourceGenerator.java b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/SourceBasedSourceGenerator.java
index 6dae414..d53d0fb 100644
--- a/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/SourceBasedSourceGenerator.java
+++ b/org.eclipse.jdt.debug/eval/org/eclipse/jdt/internal/debug/eval/ast/engine/SourceBasedSourceGenerator.java
@@ -92,6 +92,7 @@
 import org.eclipse.jdt.core.dom.PrimitiveType;
 import org.eclipse.jdt.core.dom.QualifiedName;
 import org.eclipse.jdt.core.dom.QualifiedType;
+import org.eclipse.jdt.core.dom.RecordDeclaration;
 import org.eclipse.jdt.core.dom.ReturnStatement;
 import org.eclipse.jdt.core.dom.SimpleName;
 import org.eclipse.jdt.core.dom.SimpleType;
@@ -454,6 +455,11 @@
 						.equals(fLastTypeName)) {
 					source.append(buildEnumDeclaration(null, enumDeclaration));
 				}
+			} else if (bodyDeclaration instanceof RecordDeclaration) {
+				var recordDeclaration = (RecordDeclaration) bodyDeclaration;
+				if (!recordDeclaration.getName().getIdentifier().equals(fLastTypeName)) {
+					source.append(buildRecordDeclaration(null, recordDeclaration));
+				}
 			}
 		}
 		return source;
@@ -534,16 +540,14 @@
 		appendExtraDimensions(source, methodDeclaration.getExtraDimensions());
 
 		first = true;
-		for (Iterator<Name> iterator = methodDeclaration.thrownExceptions()
-				.iterator(); iterator.hasNext();) {
-			Name name = iterator.next();
+		for (Object exceptionType : methodDeclaration.thrownExceptionTypes()) {
 			if (first) {
 				first = false;
 				source.append(" throws "); //$NON-NLS-1$
 			} else {
 				source.append(',');
 			}
-			source.append(getQualifiedIdentifier(name));
+			source.append(getTypeName((Type) exceptionType));
 		}
 
 		if (Flags.isAbstract(modifiers) || Flags.isNative(modifiers)) {
@@ -610,7 +614,71 @@
 
 		source.append(typeDeclaration.getName().getIdentifier());
 
-		List<TypeParameter> typeParameters = typeDeclaration.typeParameters();
+		buildTypeParameterList(source, typeDeclaration.typeParameters());
+
+		Type superClass = typeDeclaration.getSuperclassType();
+		if (superClass != null) {
+			source.append(" extends "); //$NON-NLS-1$
+			source.append(getTypeName(superClass));
+		}
+
+		buildSuperInterfaceTypeList(source, typeDeclaration.superInterfaceTypes().iterator(), typeDeclaration.isInterface());
+
+		if (buffer != null) {
+			fSnippetStartPosition += source.length();
+		}
+		source.append(buildTypeBody(buffer, typeDeclaration.bodyDeclarations()));
+
+		return source;
+	}
+
+	void buildSuperInterfaceTypeList(StringBuilder source, Iterator<Type> superInterfaceTypes, boolean isTypeInterface) {
+		if (superInterfaceTypes.hasNext()) {
+			if (isTypeInterface) {
+				source.append(" extends "); //$NON-NLS-1$
+			} else {
+				source.append(" implements "); //$NON-NLS-1$
+			}
+			source.append(getTypeName(superInterfaceTypes.next()));
+			while (superInterfaceTypes.hasNext()) {
+				source.append(',');
+				source.append(getTypeName(superInterfaceTypes.next()));
+			}
+		}
+	}
+
+	private StringBuilder buildRecordDeclaration(StringBuilder buffer, RecordDeclaration typeDeclaration) {
+
+		StringBuilder source = new StringBuilder();
+		source.append(Flags.toString(typeDeclaration.getModifiers()));
+		source.append(" record "); //$NON-NLS-1$
+
+		source.append(typeDeclaration.getName().getIdentifier());
+
+		buildTypeParameterList(source, typeDeclaration.typeParameters());
+
+		boolean first = true;
+		source.append('(');
+		for (SingleVariableDeclaration field : (List<SingleVariableDeclaration>) typeDeclaration.recordComponents()) {
+			if (first) {
+				first = false;
+			} else {
+				source.append(',');
+			}
+			source.append(getTypeName(field.getType())).append(' ').append(field.getName());
+		}
+		source.append(')');
+		buildSuperInterfaceTypeList(source, typeDeclaration.superInterfaceTypes().iterator(), false);
+
+		if (buffer != null) {
+			fSnippetStartPosition += source.length();
+		}
+		source.append(buildTypeBody(buffer, typeDeclaration.bodyDeclarations()));
+
+		return source;
+	}
+
+	void buildTypeParameterList(StringBuilder source, List<TypeParameter> typeParameters) {
 		if (!typeParameters.isEmpty() && isSourceLevelGreaterOrEqual(1, 5)) {
 			source.append('<');
 			Iterator<TypeParameter> iter = typeParameters.iterator();
@@ -643,33 +711,6 @@
 			}
 			source.append('>');
 		}
-
-		Type superClass = typeDeclaration.getSuperclassType();
-		if (superClass != null) {
-			source.append(" extends "); //$NON-NLS-1$
-			source.append(getTypeName(superClass));
-		}
-
-		Iterator<Type> iter = typeDeclaration.superInterfaceTypes().iterator();
-		if (iter.hasNext()) {
-			if (typeDeclaration.isInterface()) {
-				source.append(" extends "); //$NON-NLS-1$
-			} else {
-				source.append(" implements "); //$NON-NLS-1$
-			}
-			source.append(getTypeName(iter.next()));
-			while (iter.hasNext()) {
-				source.append(',');
-				source.append(getTypeName(iter.next()));
-			}
-		}
-
-		if (buffer != null) {
-			fSnippetStartPosition += source.length();
-		}
-		source.append(buildTypeBody(buffer, typeDeclaration.bodyDeclarations()));
-
-		return source;
 	}
 
 	private StringBuilder buildCompilationUnit(StringBuilder buffer,
@@ -813,7 +854,7 @@
 			}
 			return name;
 		} else if (type.isArrayType()) {
-			return getTypeName(((ArrayType) type).getComponentType()) + "[]"; //$NON-NLS-1$
+			return getTypeName(((ArrayType) type).getElementType()) + "[]"; //$NON-NLS-1$
 		} else if (type.isPrimitiveType()) {
 			return ((PrimitiveType) type).getPrimitiveTypeCode().toString();
 		} else if (type.isQualifiedType()) {
@@ -1091,7 +1132,6 @@
 	 */
 	@Override
 	public void endVisit(TypeDeclaration node) {
-
 		if (hasError()) {
 			fTypeParameterStack.pop();
 			fTypeParameterTypeStack.pop();