Bug 572782 - Fix evaluation errors due to recursive type bounds

When where are recursive type bounds which contains the type declaration
name which the current type bounds are defined for, the source generator
add these bounds into generated run method which cause the compilation
issue. The fix avoid adding such type bounds since they are not valid
for a method declaration.

Change-Id: Ia8fe4d2bad3ec66366964931f03faa65897243d2
Reviewed-on: https://git.eclipse.org/r/c/jdt/eclipse.jdt.debug/+/179463
Tested-by: JDT Bot <jdt-bot@eclipse.org>
Reviewed-by: Sarika Sinha <sarika.sinha@in.ibm.com>
diff --git a/org.eclipse.jdt.debug.tests/java7/Bug572782.java b/org.eclipse.jdt.debug.tests/java7/Bug572782.java
new file mode 100644
index 0000000..ebf89d2
--- /dev/null
+++ b/org.eclipse.jdt.debug.tests/java7/Bug572782.java
@@ -0,0 +1,24 @@
+public class Bug572782 {
+	static class Generic<T> {
+		
+	}
+	
+	static class ExtendedGeneric<T extends Generic<ExtendedGeneric<T>>> {
+		public ExtendedGeneric() {
+			super();
+			System.out.println("created " + this);
+		}
+	}
+
+	static class SimpleGeneric<T extends Generic<T>> {
+		public SimpleGeneric() {
+			super();
+			System.out.println("created " + this);
+		}
+	}
+
+	public static void main(String[] args) {
+		new ExtendedGeneric();
+		new SimpleGeneric();
+	}
+}
\ No newline at end of file
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
index dec4564..6054221 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AbstractDebugTest.java
@@ -439,6 +439,7 @@
 	    		cfgs.add(createLaunchConfiguration(jp, LiteralTests17.LITERAL_TYPE_NAME));
 				cfgs.add(createLaunchConfiguration(jp, "ThreadNameChange"));
 				cfgs.add(createLaunchConfiguration(jp, "Bug567801"));
+				cfgs.add(createLaunchConfiguration(jp, "Bug572782"));
 	    		loaded17 = true;
 	    		waitForBuild();
 	        }
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java
index b1b583a..9d072e6 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/AutomatedSuite.java
@@ -93,6 +93,7 @@
 import org.eclipse.jdt.debug.tests.core.WorkspaceSourceContainerTests;
 import org.eclipse.jdt.debug.tests.eval.BlockStatementEvaluationTests;
 import org.eclipse.jdt.debug.tests.eval.GeneralEvalTests;
+import org.eclipse.jdt.debug.tests.eval.GenericsEval17Test;
 import org.eclipse.jdt.debug.tests.eval.GenericsEvalTests;
 import org.eclipse.jdt.debug.tests.eval.LambdaVariableTest;
 import org.eclipse.jdt.debug.tests.eval.SyntheticVariableTests;
@@ -330,6 +331,7 @@
 
 	//add the complete eval suite
 		addTest(new TestSuite(GeneralEvalTests.class));
+		addTest(new TestSuite(GenericsEval17Test.class));
 		addTest(new TestSuite(BlockStatementEvaluationTests.class));
 		if (JavaProjectHelper.isJava8Compatible()) {
 			addTest(new TestSuite(LambdaVariableTest.class));
diff --git a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/eval/GenericsEval17Test.java b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/eval/GenericsEval17Test.java
index 041a240..c56d297 100644
--- a/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/eval/GenericsEval17Test.java
+++ b/org.eclipse.jdt.debug.tests/tests/org/eclipse/jdt/debug/tests/eval/GenericsEval17Test.java
@@ -15,6 +15,7 @@
 
 import org.eclipse.debug.core.model.IValue;
 import org.eclipse.jdt.core.IJavaProject;
+import org.eclipse.jdt.debug.core.IJavaObject;
 import org.eclipse.jdt.debug.core.IJavaThread;
 import org.eclipse.jdt.debug.tests.AbstractDebugTest;
 
@@ -39,6 +40,35 @@
 		assertEquals("value is not true", "true", value.getValueString());
 	}
 
+	public void testEvaluate_Bug572782_RecursiveGeneric_ExpectedEvalVarValue() throws Exception {
+		debugWithBreakpoint("Bug572782", 9);
+		String snippet = "this";
+		IValue value = doEval(javaThread, snippet);
+
+		assertNotNull("value is null", value);
+		assertTrue("No a IJavaObjectValue", value instanceof IJavaObject);
+		assertEquals("value don't has the correct generic signature", "<T:LBug572782$Generic<LBug572782$ExtendedGeneric<TT;>;>;>Ljava/lang/Object;",
+				((IJavaObject) value).getGenericSignature());
+	}
+
+	public void testEvaluate_Bug572782_RecursiveGeneric_ExpectedEvalExpressionValue() throws Exception {
+		debugWithBreakpoint("Bug572782", 9);
+		String snippet = "1 + 2";
+		IValue value = doEval(javaThread, snippet);
+
+		assertNotNull("value is null", value);
+		assertEquals("value is not 3", "3", value.getValueString());
+	}
+
+	public void testEvaluate_Bug572782_RecursiveGenericSimple_ExpectedEvalExpressionValue() throws Exception {
+		debugWithBreakpoint("Bug572782", 16);
+		String snippet = "1 + 2";
+		IValue value = doEval(javaThread, snippet);
+
+		assertNotNull("value is null", value);
+		assertEquals("value is not 3", "3", value.getValueString());
+	}
+
 	private void debugWithBreakpoint(String testClass, int lineNumber) throws Exception {
 		createLineBreakpoint(lineNumber, testClass);
 		javaThread = launchToBreakpoint(testClass);
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 a4e101b..b0918cc 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
@@ -21,6 +21,7 @@
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
+import java.util.Optional;
 import java.util.Stack;
 
 import org.eclipse.jdt.core.Flags;
@@ -1624,7 +1625,16 @@
 			while (iterator.hasNext()) {
 				TypeParameter typeParameter = iterator.next();
 				String boundName = typeParameter.getName().getIdentifier();
-				newTypeParameters.put(boundName, typeParameter.toString());
+				String tpString = typeParameter.toString();
+
+				// check if the current parameter contains the current type declaration as a recursive type bound.
+				boolean recursiveTypeBound = Optional.ofNullable(typeParameter.getParent()).filter(TypeDeclaration.class::isInstance)
+					.map(td -> ((TypeDeclaration) td).getName().getIdentifier())
+						.map(tn -> tpString.matches(String.format(".*%s\\<\s*%s\s*\\>.*", tn, boundName))).orElse(false); //$NON-NLS-1$
+
+				if (!recursiveTypeBound) {
+					newTypeParameters.put(boundName, tpString);
+				}
 			}
 			fTypeParameterStack.push(newTypeParameters); // Push the new "scope"
 		} else {