Bug 562736 - [14][dom ast] RecordComponentBinding and node changes
record component details testing and IVariable method with
isRecordComponent new method

Change-Id: Id97b1aa297f257ea185268d2b26758a317b86f53
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter14Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter14Test.java
index 18a70ac..02406b3 100644
--- a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter14Test.java
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/dom/ASTConverter14Test.java
@@ -23,14 +23,19 @@
 import org.eclipse.jdt.core.dom.AST;
 import org.eclipse.jdt.core.dom.ASTNode;
 import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.AbstractTypeDeclaration;
 import org.eclipse.jdt.core.dom.Block;
 import org.eclipse.jdt.core.dom.BreakStatement;
 import org.eclipse.jdt.core.dom.CompilationUnit;
 import org.eclipse.jdt.core.dom.Expression;
 import org.eclipse.jdt.core.dom.IBinding;
 import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.MarkerAnnotation;
 import org.eclipse.jdt.core.dom.MethodDeclaration;
+import org.eclipse.jdt.core.dom.RecordDeclaration;
 import org.eclipse.jdt.core.dom.SimpleName;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
 import org.eclipse.jdt.core.dom.Statement;
 import org.eclipse.jdt.core.dom.SwitchCase;
 import org.eclipse.jdt.core.dom.SwitchExpression;
@@ -781,4 +786,103 @@
 			javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, old);
 		}
 	}
+
+	public void testRecord005() throws CoreException {
+		if (!isJRE14) {
+			System.err.println("Test "+getName()+" requires a JRE 14");
+			return;
+		}
+		String contents = "public class X {\n" +
+				  "	public static void main(String[] args) {\n" +
+		          "		record R(int x,String y){}\n" +
+				  "		R r = new R(100, \"Point\");\n" +
+		          "		System.out.println(r.x());\n" +
+				  "	}\n" +
+		          "}";
+		this.workingCopy = getWorkingCopy("/Converter14/src/X.java", true/*resolve*/);
+		IJavaProject javaProject = this.workingCopy.getJavaProject();
+		String old = javaProject.getOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, true);
+		try {
+			javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED);
+			javaProject.setOption(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.IGNORE);
+			ASTNode node = buildAST(
+				contents,
+				this.workingCopy);
+			assertEquals("Not a compilation unit", ASTNode.COMPILATION_UNIT, node.getNodeType());
+			CompilationUnit compilationUnit = (CompilationUnit) node;
+			assertProblemsSize(compilationUnit, 0);
+			node = getASTNode(compilationUnit, 0, 0);
+			assertEquals("Not a method declaration", ASTNode.METHOD_DECLARATION, node.getNodeType());
+			MethodDeclaration methodDeclaration = (MethodDeclaration) node;
+			List<ASTNode> statements = methodDeclaration.getBody().statements();
+			node = statements.get(0);
+			assertEquals("Not a TypDeclaration statement", ASTNode.TYPE_DECLARATION_STATEMENT, node.getNodeType());
+			TypeDeclarationStatement tdStmt = (TypeDeclarationStatement) node;
+			node = tdStmt.getDeclaration();
+			assertEquals("Not a RecordDeclaration", ASTNode.RECORD_DECLARATION, node.getNodeType());
+			RecordDeclaration record = (RecordDeclaration)node;
+			List<SingleVariableDeclaration> recordComponents = record.recordComponents();
+			assertEquals("There should be 2 record components", 2, recordComponents.size());
+			SingleVariableDeclaration recordComponent = recordComponents.get(0);
+			assertEquals("First record component name should be x","x" , recordComponent.getName().toString());
+			assertEquals("First record component type is int" , "int", recordComponent.getType().toString());
+			IVariableBinding resolveBinding = recordComponent.resolveBinding();
+			assertEquals("First record component binding" , true, resolveBinding.isRecordComponent());
+			recordComponent = recordComponents.get(1);
+			assertEquals("Second record component name should be y","y" , recordComponent.getName().toString());
+			assertEquals("Second record component type is String" , "String", recordComponent.getType().toString());
+			resolveBinding = recordComponent.resolveBinding();
+			assertEquals("Second record component binding" , true, resolveBinding.isRecordComponent());
+		} finally {
+			javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, old);
+		}
+	}
+
+	public void testRecord006() throws CoreException {
+		if (!isJRE14) {
+			System.err.println("Test "+getName()+" requires a JRE 14");
+			return;
+		}
+		String contents = "import java.lang.annotation.ElementType;\n" +
+				"import java.lang.annotation.Target;\n" +
+				"record X(@MyAnnot int lo) {\n" +
+				"	public int lo() {\n" +
+				"		return this.lo;\n" +
+				"	}\n" +
+				"\n" +
+				"}\n" +
+				"@Target({ElementType.FIELD})\n" +
+				"@interface MyAnnot {}";
+		this.workingCopy = getWorkingCopy("/Converter14/src/X.java", true/*resolve*/);
+		IJavaProject javaProject = this.workingCopy.getJavaProject();
+		String old = javaProject.getOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, true);
+		try {
+			javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED);
+			javaProject.setOption(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.IGNORE);
+			ASTNode node = buildAST(
+				contents,
+				this.workingCopy);
+			assertEquals("Not a compilation unit", ASTNode.COMPILATION_UNIT, node.getNodeType());
+			CompilationUnit compilationUnit = (CompilationUnit) node;
+			assertProblemsSize(compilationUnit, 0);
+			node = ((AbstractTypeDeclaration)compilationUnit.types().get(0));
+			assertEquals("Not a Record Declaration", ASTNode.RECORD_DECLARATION, node.getNodeType());
+			RecordDeclaration record = (RecordDeclaration)node;
+			List<SingleVariableDeclaration> recordComponents = record.recordComponents();
+			assertEquals("There should be 1 record component", 1, recordComponents.size());
+			SingleVariableDeclaration recordComponent = recordComponents.get(0);
+			assertEquals("Record component name should be lo","lo" , recordComponent.getName().toString());
+			assertEquals("Record component type is int" , "int", recordComponent.getType().toString());
+			IVariableBinding resolveBinding = recordComponent.resolveBinding();
+			assertEquals("Record component binding" , true, resolveBinding.isRecordComponent());
+			MarkerAnnotation annotation = (MarkerAnnotation)recordComponent.modifiers().get(0);
+			assertEquals("Record component annotation name should be MyAnnot","@MyAnnot" , annotation.toString());
+			assertEquals("Record component binding should not have annotation",0 , resolveBinding.getAnnotations().length);
+
+
+		} finally {
+			javaProject.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, old);
+		}
+	}
+
 }
diff --git a/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ImportRewrite14Test.java b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ImportRewrite14Test.java
new file mode 100644
index 0000000..59074e6
--- /dev/null
+++ b/org.eclipse.jdt.core.tests.model/src/org/eclipse/jdt/core/tests/rewrite/describing/ImportRewrite14Test.java
@@ -0,0 +1,176 @@
+/*******************************************************************************
+ * Copyright (c) 2020 IBM Corporation 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
+ *
+ * Contributors:
+ *		IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.jdt.core.tests.rewrite.describing;
+
+import java.io.IOException;
+import java.util.List;
+
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.jdt.core.ICompilationUnit;
+import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.core.IPackageFragmentRoot;
+import org.eclipse.jdt.core.JavaCore;
+import org.eclipse.jdt.core.dom.AST;
+import org.eclipse.jdt.core.dom.ASTParser;
+import org.eclipse.jdt.core.dom.CompilationUnit;
+import org.eclipse.jdt.core.dom.ITypeBinding;
+import org.eclipse.jdt.core.dom.IVariableBinding;
+import org.eclipse.jdt.core.dom.RecordDeclaration;
+import org.eclipse.jdt.core.dom.SingleVariableDeclaration;
+import org.eclipse.jdt.core.dom.Type;
+import org.eclipse.jdt.core.dom.rewrite.ImportRewrite;
+import org.eclipse.jdt.core.formatter.DefaultCodeFormatterConstants;
+import org.eclipse.jdt.core.tests.model.AbstractJavaModelTests;
+import org.eclipse.jface.text.BadLocationException;
+import org.eclipse.jface.text.Document;
+import org.eclipse.text.edits.MalformedTreeException;
+import org.eclipse.text.edits.TextEdit;
+import org.osgi.service.prefs.BackingStoreException;
+
+import junit.framework.Test;
+
+
+@SuppressWarnings("rawtypes")
+public class ImportRewrite14Test extends AbstractJavaModelTests {
+
+
+	private static final Class THIS= ImportRewrite14Test.class;
+	private static final String PROJECT = "ImportRewrite14TestProject";
+
+	protected IPackageFragmentRoot sourceFolder;
+
+
+	public ImportRewrite14Test(String name) {
+		super(name);
+	}
+
+	public static Test allTests() {
+		return new Suite(THIS);
+	}
+
+	public static Test suite() {
+		return allTests();
+	}
+
+	@Override
+	protected void setUp() throws Exception {
+		super.setUp();
+
+		IJavaProject proj= createJavaProject(PROJECT, new String[] {"src"}, new String[] {"JCL_LIB"}, "bin", "14");
+		proj.setOption(DefaultCodeFormatterConstants.FORMATTER_TAB_CHAR, JavaCore.SPACE);
+		proj.setOption(DefaultCodeFormatterConstants.FORMATTER_TAB_SIZE, "4");
+		proj.setOption(JavaCore.COMPILER_COMPLIANCE, JavaCore.VERSION_14);
+		proj.setOption(JavaCore.COMPILER_PB_ASSERT_IDENTIFIER, JavaCore.ERROR);
+		proj.setOption(JavaCore.COMPILER_SOURCE, JavaCore.VERSION_14);
+		proj.setOption(JavaCore.COMPILER_CODEGEN_TARGET_PLATFORM, JavaCore.VERSION_14);
+		proj.setOption(DefaultCodeFormatterConstants.FORMATTER_NUMBER_OF_EMPTY_LINES_TO_PRESERVE, String.valueOf(99));
+
+		proj.setOption(DefaultCodeFormatterConstants.FORMATTER_BLANK_LINES_BETWEEN_IMPORT_GROUPS, String.valueOf(1));
+		proj.setOption(JavaCore.COMPILER_PB_ENABLE_PREVIEW_FEATURES, JavaCore.ENABLED);
+		proj.setOption(JavaCore.COMPILER_PB_REPORT_PREVIEW_FEATURES, JavaCore.IGNORE);
+
+
+		this.sourceFolder = getPackageFragmentRoot(PROJECT, "src");
+
+		waitUntilIndexesReady();
+	}
+	protected static int getJLS14() {
+		return AST.JLS14;
+	}
+	@Override
+	protected void tearDown() throws Exception {
+		deleteProject(PROJECT);
+		super.tearDown();
+	}
+
+	/*
+	 * typeBinding shows "int value" rather than "@MyAnnotation int value"
+	 */
+	public void _test001() throws Exception {
+		String contents = "package pack1;\n" +
+				"import pack2.MyAnnotation;\n" +
+				"public record X(@MyAnnotation int value){\n" +
+				"}\n";
+		createFolder("/" + PROJECT + "/src/pack1");
+		createFile("/" + PROJECT + "/src/pack1/X.java", contents);
+		contents = "package pack2;\n" +
+				"public @interface MyAnnotation{}\n";
+		createFolder("/" + PROJECT + "/src/pack2");
+		createFile("/" + PROJECT + "/src/pack2/MyAnnotation.java", contents);
+
+		ASTParser parser = ASTParser.newParser(getJLS14());
+		parser.setSource(getCompilationUnit("/" + PROJECT + "/src/pack2/MyAnnotation.java"));
+		parser.setResolveBindings(true);
+		parser.setStatementsRecovery(true);
+		CompilationUnit astRoot = (CompilationUnit) parser.createAST(null);
+
+		ICompilationUnit cu = getCompilationUnit("/" + PROJECT + "/src/pack1/X.java");
+		parser.setSource(cu);
+		parser.setResolveBindings(true);
+		parser.setStatementsRecovery(true);
+		astRoot = (CompilationUnit) parser.createAST(null);
+		RecordDeclaration record= (RecordDeclaration) astRoot.types().get(0);
+		List<SingleVariableDeclaration> recordComponents = record.recordComponents();
+		SingleVariableDeclaration recordComponent = recordComponents.get(0);
+		assertEquals("Record component type is int" , "int", recordComponent.getType().toString());
+		IVariableBinding binding = recordComponent.resolveBinding();
+
+		ITypeBinding typeBinding = binding.getType();
+		ImportRewrite rewrite = newImportsRewrite(cu, new String[0], 99, 99, true);
+		cu = getCompilationUnit("/" + PROJECT + "/src/pack1/X.java");
+		rewrite = newImportsRewrite(cu, new String[0], 99, 99, true);
+		Type actualType = rewrite.addImport(typeBinding, astRoot.getAST());
+		assertEquals("int", actualType.toString());
+		assertTrue(actualType.isPrimitiveType());
+		apply(rewrite);
+		String contentsA = "package pack1;\n" +
+				"\n" +
+				"import pack2.MyAnnotation;\n" +
+				"\n" +
+				"public record X(@MyAnnotation int value){\n"+
+				"}\n";
+		assertEqualStringIgnoreDelim(cu.getSource(), contentsA);
+	}
+
+	protected void assertEqualStringIgnoreDelim(String actual, String expected) throws IOException {
+		StringAsserts.assertEqualStringIgnoreDelim(actual, expected);
+	}
+
+	protected ImportRewrite newImportsRewrite(ICompilationUnit cu, String[] order, int normalThreshold, int staticThreshold, boolean restoreExistingImports) throws CoreException, BackingStoreException {
+		ImportRewrite rewrite= ImportRewrite.create(cu, restoreExistingImports);
+		rewrite.setImportOrder(order);
+		rewrite.setOnDemandImportThreshold(normalThreshold);
+		rewrite.setStaticOnDemandImportThreshold(staticThreshold);
+		return rewrite;
+	}
+
+	protected ImportRewrite newImportsRewrite(CompilationUnit cu, String[] order, int normalThreshold, int staticThreshold, boolean restoreExistingImports) {
+		ImportRewrite rewrite= ImportRewrite.create(cu, restoreExistingImports);
+		rewrite.setImportOrder(order);
+		rewrite.setOnDemandImportThreshold(normalThreshold);
+		rewrite.setStaticOnDemandImportThreshold(staticThreshold);
+		return rewrite;
+	}
+
+	protected void apply(ImportRewrite rewrite) throws CoreException, MalformedTreeException, BadLocationException {
+		TextEdit edit= rewrite.rewriteImports(null);
+
+		// not the efficient way!
+		ICompilationUnit compilationUnit= rewrite.getCompilationUnit();
+		Document document= new Document(compilationUnit.getSource());
+		edit.apply(document);
+		compilationUnit.getBuffer().setContents(document.get());
+	}
+
+}
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java
index cd7e8c5..2829c51 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/DefaultBindingResolver.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -68,6 +68,7 @@
 import org.eclipse.jdt.internal.compiler.lookup.ProblemPackageBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ProblemReasons;
 import org.eclipse.jdt.internal.compiler.lookup.ProblemReferenceBinding;
+import org.eclipse.jdt.internal.compiler.lookup.RecordComponentBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
 import org.eclipse.jdt.internal.compiler.lookup.Scope;
 import org.eclipse.jdt.internal.compiler.lookup.SyntheticArgumentBinding;
@@ -861,6 +862,9 @@
 							} else if (binding instanceof org.eclipse.jdt.internal.compiler.lookup.MethodBinding) {
 								// it is a type
 								return getMethodBinding((org.eclipse.jdt.internal.compiler.lookup.MethodBinding)binding);
+							} else if (binding instanceof RecordComponentBinding) {
+								IVariableBinding variableBinding = this.getVariableBinding((RecordComponentBinding) binding);
+								return variableBinding == null ? null : variableBinding;
 							}
 						} else {
 							if (binding instanceof org.eclipse.jdt.internal.compiler.lookup.TypeBinding) {
@@ -1081,6 +1085,9 @@
 							case Binding.LOCAL:
 								type = ((LocalVariableBinding) qualifiedNameReference.binding).type;
 								break;
+							case Binding.RECORD_COMPONENT:
+								type = ((RecordComponentBinding) qualifiedNameReference.binding).type;
+								break;
 						}
 					}
 					return this.getTypeBinding(type);
@@ -1184,7 +1191,7 @@
 			}
 		} if (node instanceof JavadocSingleNameReference) {
 			JavadocSingleNameReference singleNameReference = (JavadocSingleNameReference) node;
-			LocalVariableBinding localVariable = (LocalVariableBinding)singleNameReference.binding;
+			org.eclipse.jdt.internal.compiler.lookup.VariableBinding localVariable = (org.eclipse.jdt.internal.compiler.lookup.VariableBinding)singleNameReference.binding;
 			if (localVariable != null) {
 				return this.getTypeBinding(localVariable.type);
 			}
@@ -1390,6 +1397,9 @@
 					} else if (binding instanceof org.eclipse.jdt.internal.compiler.lookup.MethodBinding) {
 						// it is a type
 						return getMethodBinding((org.eclipse.jdt.internal.compiler.lookup.MethodBinding)binding);
+					} else if (binding instanceof org.eclipse.jdt.internal.compiler.lookup.RecordComponentBinding) {
+						// it is a type
+						return this.getVariableBinding((org.eclipse.jdt.internal.compiler.lookup.RecordComponentBinding)binding);
 					} else {
 						return null;
 					}
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/IVariableBinding.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/IVariableBinding.java
index 80c7e6e..524874c 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/IVariableBinding.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/IVariableBinding.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2013 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -57,6 +57,20 @@
 	public boolean isParameter();
 
 	/**
+	 * Returns whether this binding is for a record component constant.
+	 * Note that this method returns <code>false</code> for local variables
+	 * and for fields other than record component.
+	 *
+	 * @return <code>true</code> if this is the binding for a record component,
+	 *    and <code>false</code> otherwise
+	 * @since 3.22
+	 * @noreference This method is not intended to be referenced by clients.
+	 */
+	public default boolean isRecordComponent() {
+		return false;
+	}
+
+	/**
 	 * Returns the name of the field or local variable declared in this binding.
 	 * The name is always a simple identifier.
 	 *
diff --git a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/VariableBinding.java b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/VariableBinding.java
index 77324c7..76a5c45 100644
--- a/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/VariableBinding.java
+++ b/org.eclipse.jdt.core/dom/org/eclipse/jdt/core/dom/VariableBinding.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2016 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -216,6 +216,16 @@
 						defaultBindingResolver.getBindingsToNodesMap());
 			}
 			return null;
+		}else if (isRecordComponent()) {
+			if (this.resolver instanceof DefaultBindingResolver) {
+				DefaultBindingResolver defaultBindingResolver = (DefaultBindingResolver) this.resolver;
+				if (!defaultBindingResolver.fromJavaProject) return null;
+				return Util.getUnresolvedJavaElement(
+						(RecordComponentBinding) this.binding,
+						defaultBindingResolver.workingCopyOwner,
+						defaultBindingResolver.getBindingsToNodesMap());
+			}
+			return null;
 		}
 		// local variable
 		if (!(this.resolver instanceof DefaultBindingResolver)) return null;
@@ -385,6 +395,11 @@
 		return (!this.binding.isFinal() && this.binding.isEffectivelyFinal());
 	}
 
+	@Override
+	public boolean isRecordComponent() {
+		return this.binding instanceof RecordComponentBinding;
+	}
+
 	/*
 	 * For debugging purpose only.
 	 * @see java.lang.Object#toString()
@@ -393,4 +408,5 @@
 	public String toString() {
 		return this.binding.toString();
 	}
+
 }
diff --git a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/Util.java b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/Util.java
index 9ec1f1d..45b893e 100644
--- a/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/Util.java
+++ b/org.eclipse.jdt.core/model/org/eclipse/jdt/internal/core/util/Util.java
@@ -1,5 +1,5 @@
 /*******************************************************************************
- * Copyright (c) 2000, 2019 IBM Corporation and others.
+ * Copyright (c) 2000, 2020 IBM Corporation and others.
  *
  * This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License 2.0
@@ -62,6 +62,7 @@
 import org.eclipse.jdt.internal.compiler.lookup.Binding;
 import org.eclipse.jdt.internal.compiler.lookup.FieldBinding;
 import org.eclipse.jdt.internal.compiler.lookup.MethodBinding;
+import org.eclipse.jdt.internal.compiler.lookup.RecordComponentBinding;
 import org.eclipse.jdt.internal.compiler.lookup.ReferenceBinding;
 import org.eclipse.jdt.internal.compiler.lookup.TypeBinding;
 import org.eclipse.jdt.internal.compiler.lookup.TypeIds;
@@ -1364,6 +1365,18 @@
 	}
 
 	/**
+	 * Return the java element corresponding to the given compiler binding.
+	 */
+	public static JavaElement getUnresolvedJavaElement(RecordComponentBinding binding, WorkingCopyOwner workingCopyOwner, BindingsToNodesMap bindingsToNodes) {
+		if (binding.declaringRecord == null) return null; // array length
+		JavaElement unresolvedJavaElement = getUnresolvedJavaElement(binding.declaringRecord, workingCopyOwner, bindingsToNodes);
+		if (unresolvedJavaElement == null || unresolvedJavaElement.getElementType() != IJavaElement.TYPE) {
+			return null;
+		}
+		return (JavaElement) ((IType) unresolvedJavaElement).getField(String.valueOf(binding.name));
+	}
+
+	/**
 	 * Returns the IInitializer that contains the given local variable in the given type
 	 */
 	public static JavaElement getUnresolvedJavaElement(int localSourceStart, int localSourceEnd, JavaElement type) {