Repository: SpoonLabs/gumtree-spoon-ast-diff (java)

Issue:
Fail to recognize Generics in Superclass
**Input:**
1.java
```java
package com.test;

public class TestClass extends SuperClass<Old> {

}

```
2.java
```java
package com.test;

public class TestClass extends SuperClass<New> {

}

```
**Output:**
```bash
java -jar gumtree-spoon-ast-diff-1.17-jar-with-dependencies.jar 1.java 2.java
no AST change
```

I would have expected the change "Old > New" to be represented in the AST.
This is happening because this is a `CtTypeReference` and all `CtReference`s are ignored.

Pull request description:
Fix #96: consider superclass
#96 
This PR allows us to consider extends arguments//superclass and introduces the corresponding test cases.

Patch:
diff --git a/src/main/java/gumtree/spoon/builder/LabelFinder.java b/src/main/java/gumtree/spoon/builder/LabelFinder.java
index 124b797..746d9a6 100644
--- a/src/main/java/gumtree/spoon/builder/LabelFinder.java
+++ b/src/main/java/gumtree/spoon/builder/LabelFinder.java
@@ -1,23 +1,6 @@
 package gumtree.spoon.builder;
 
-import spoon.reflect.code.CtAssert;
-import spoon.reflect.code.CtAssignment;
-import spoon.reflect.code.CtBinaryOperator;
-import spoon.reflect.code.CtBlock;
-import spoon.reflect.code.CtBreak;
-import spoon.reflect.code.CtConstructorCall;
-import spoon.reflect.code.CtContinue;
-import spoon.reflect.code.CtIf;
-import spoon.reflect.code.CtInvocation;
-import spoon.reflect.code.CtLiteral;
-import spoon.reflect.code.CtOperatorAssignment;
-import spoon.reflect.code.CtReturn;
-import spoon.reflect.code.CtSuperAccess;
-import spoon.reflect.code.CtThisAccess;
-import spoon.reflect.code.CtTypeAccess;
-import spoon.reflect.code.CtUnaryOperator;
-import spoon.reflect.code.CtVariableAccess;
-import spoon.reflect.code.CtWhile;
+import spoon.reflect.code.*;
 import spoon.reflect.declaration.CtNamedElement;
 import spoon.reflect.path.CtRole;
 import spoon.reflect.reference.CtTypeReference;
diff --git a/src/main/java/gumtree/spoon/builder/NodeCreator.java b/src/main/java/gumtree/spoon/builder/NodeCreator.java
index 87ac0fe..c3bf219 100644
--- a/src/main/java/gumtree/spoon/builder/NodeCreator.java
+++ b/src/main/java/gumtree/spoon/builder/NodeCreator.java
@@ -1,18 +1,19 @@
 package gumtree.spoon.builder;
 
-import java.util.Comparator;
-import java.util.Set;
-import java.util.TreeSet;
-
 import com.github.gumtreediff.tree.ITree;
-
 import spoon.reflect.declaration.CtMethod;
 import spoon.reflect.declaration.CtModifiable;
 import spoon.reflect.declaration.CtVariable;
 import spoon.reflect.declaration.ModifierKind;
+import spoon.reflect.path.CtRole;
+import spoon.reflect.reference.CtReference;
 import spoon.reflect.reference.CtTypeReference;
 import spoon.reflect.visitor.CtInheritanceScanner;
 
+import java.util.Comparator;
+import java.util.Set;
+import java.util.TreeSet;
+
 /**
  * responsible to add additional nodes only overrides scan* to add new nodes
  */
@@ -95,4 +96,19 @@ public class NodeCreator extends CtInheritanceScanner {
 		super.visitCtMethod(e);
 	}
 
+    @Override
+    public void scanCtReference(CtReference reference) {
+
+
+        if (reference instanceof CtTypeReference && reference.getRoleInParent() == CtRole.SUPER_TYPE) {
+            ITree superType = builder.createNode("SUPER_TYPE", reference.toString());
+            CtWrapper<CtReference> k = new CtWrapper<CtReference>(reference, reference.getParent());
+            superType.setMetadata(SpoonGumTreeBuilder.SPOON_OBJECT, k);
+            reference.putMetadata(SpoonGumTreeBuilder.GUMTREE_NODE, superType);
+            builder.addSiblingNode(superType);
+        } else {
+            super.scanCtReference(reference);
+        }
+    }
+
 }
diff --git a/src/main/java/gumtree/spoon/builder/TreeScanner.java b/src/main/java/gumtree/spoon/builder/TreeScanner.java
index 344ba85..a0cd23b 100644
--- a/src/main/java/gumtree/spoon/builder/TreeScanner.java
+++ b/src/main/java/gumtree/spoon/builder/TreeScanner.java
@@ -1,10 +1,7 @@
 package gumtree.spoon.builder;
 
-import java.util.Stack;
-
 import com.github.gumtreediff.tree.ITree;
 import com.github.gumtreediff.tree.TreeContext;
-
 import spoon.reflect.code.CtBlock;
 import spoon.reflect.code.CtCase;
 import spoon.reflect.code.CtStatementList;
@@ -13,6 +10,8 @@ import spoon.reflect.path.CtRole;
 import spoon.reflect.reference.CtReference;
 import spoon.reflect.visitor.CtScanner;
 
+import java.util.Stack;
+
 public class TreeScanner extends CtScanner {
 	public static final String NOTYPE = "<notype>";
 	private final TreeContext treeContext;
@@ -75,6 +74,11 @@ public class TreeScanner extends CtScanner {
 			}
 			return true;
 		}
+
+        if (element instanceof CtReference && element.getRoleInParent() == CtRole.SUPER_TYPE) {
+            return false;
+        }
+
 		return element.isImplicit() || element instanceof CtReference;
 	}
 


Test patch:
diff --git a/src/test/java/gumtree/spoon/AstComparatorTest.java b/src/test/java/gumtree/spoon/AstComparatorTest.java
index 1185507..1b1dac7 100644
--- a/src/test/java/gumtree/spoon/AstComparatorTest.java
+++ b/src/test/java/gumtree/spoon/AstComparatorTest.java
@@ -1,49 +1,35 @@
 package gumtree.spoon;
 
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
-
-import java.io.File;
-import java.io.FileReader;
-import java.io.IOException;
-import java.util.List;
-
-import org.junit.Assert;
-import org.junit.Ignore;
-import org.junit.Test;
-
 import com.github.gumtreediff.matchers.Mapping;
-
 import gumtree.spoon.builder.NodeCreator;
 import gumtree.spoon.builder.SpoonGumTreeBuilder;
 import gumtree.spoon.diff.Diff;
 import gumtree.spoon.diff.DiffImpl;
-import gumtree.spoon.diff.operations.InsertOperation;
 import gumtree.spoon.diff.operations.MoveOperation;
 import gumtree.spoon.diff.operations.Operation;
 import gumtree.spoon.diff.operations.OperationKind;
 import gumtree.spoon.diff.operations.UpdateOperation;
+import org.junit.Assert;
+import org.junit.Ignore;
+import org.junit.Test;
 import spoon.Launcher;
 import spoon.SpoonModelBuilder;
-import spoon.reflect.code.CtBinaryOperator;
-import spoon.reflect.code.CtConstructorCall;
-import spoon.reflect.code.CtIf;
-import spoon.reflect.code.CtInvocation;
-import spoon.reflect.code.CtLocalVariable;
-import spoon.reflect.code.CtNewClass;
-import spoon.reflect.code.CtReturn;
-import spoon.reflect.code.CtThrow;
+import spoon.reflect.code.*;
 import spoon.reflect.declaration.CtClass;
 import spoon.reflect.declaration.CtElement;
-import spoon.reflect.declaration.CtParameter;
 import spoon.reflect.declaration.CtType;
 import spoon.reflect.factory.Factory;
 import spoon.support.compiler.VirtualFile;
 import spoon.support.compiler.jdt.JDTBasedSpoonCompiler;
 import spoon.support.compiler.jdt.JDTSnippetCompiler;
 
+import java.io.File;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.List;
+
+import static org.junit.Assert.*;
+
 /**
  * Test Spoon Diff
  * 
@@ -1541,8 +1527,9 @@ public class AstComparatorTest {
 		List<Operation> actions = result.getRootOperations();
 		result.debugInformation();
 
-		assertEquals(1, actions.size());
+		assertEquals(2, actions.size());
 		assertTrue(result.containsOperation(OperationKind.Insert, "Parameter", "call"));
+        assertTrue(result.containsOperation(OperationKind.Update, "SUPER_TYPE", "Callback<T>"));
 	}
 
 	@Test
@@ -1703,10 +1690,11 @@ public class AstComparatorTest {
 		List<Operation> actions = resulta.getRootOperations();
 		resulta.debugInformation();
 
-		assertEquals(1, actions.size());
+		assertEquals(2, actions.size());
+
+        assertTrue(resulta.containsOperation(OperationKind.Insert, "Parameter", "call"));
+        assertTrue(resulta.containsOperation(OperationKind.Update, "SUPER_TYPE", "Callback<T>"));
 
-		assertTrue(actions.get(0) instanceof InsertOperation);
-		assertTrue(actions.get(0).getSrcNode() instanceof CtParameter);
 		DiffImpl idiff = (DiffImpl) resulta;
 
 		for (Mapping map : idiff.getMappingsComp()) {
@@ -1717,4 +1705,51 @@ public class AstComparatorTest {
 		}
 
 	}
+
+
+    @Test
+    public void testExtends() throws Exception {
+        CtClass c1a = Launcher.parseClass("class Main extends SuperClass1 { }");
+        CtClass c2a = Launcher.parseClass("class Main extends SuperClass2 { }");
+
+        AstComparator diff = new AstComparator();
+        Diff result = diff.compare(c1a, c2a);
+
+        List<Operation> actions = result.getRootOperations();
+        result.debugInformation();
+
+        assertEquals(1, actions.size());
+        assertTrue(result.containsOperation(OperationKind.Update, "SUPER_TYPE", "SuperClass1"));
+    }
+
+    @Test
+    public void testExtendsGenerics1() throws Exception {
+        CtClass c1a = Launcher.parseClass("class Main extends SuperClass<One> { }");
+        CtClass c2a = Launcher.parseClass("class Main extends SuperClass<Two> { }");
+
+        AstComparator diff = new AstComparator();
+        Diff result = diff.compare(c1a, c2a);
+
+        List<Operation> actions = result.getRootOperations();
+        result.debugInformation();
+
+        assertEquals(1, actions.size());
+        assertTrue(result.containsOperation(OperationKind.Update, "SUPER_TYPE", "SuperClass<One>"));
+    }
+
+    @Test
+    public void testExtendsGenerics2() throws Exception {
+        CtClass c1a = Launcher.parseClass("class Main extends SuperClass { }");
+        CtClass c2a = Launcher.parseClass("class Main extends SuperClass<One> { }");
+
+        AstComparator diff = new AstComparator();
+        Diff result = diff.compare(c1a, c2a);
+
+        List<Operation> actions = result.getRootOperations();
+        result.debugInformation();
+
+        assertEquals(1, actions.size());
+        assertTrue(result.containsOperation(OperationKind.Update, "SUPER_TYPE", "SuperClass"));
+    }
+
 }
diff --git a/src/test/resources/examples/spoon.json b/src/test/resources/examples/spoon.json
index 94b1b43..96d3492 100644
--- a/src/test/resources/examples/spoon.json
+++ b/src/test/resources/examples/spoon.json
@@ -17,6 +17,17 @@
             }
           ]
         },
+        {
+          "label": "",
+          "type": "TypeReference",
+          "children": [
+            {
+              "label": "javax.swing.JPanel",
+              "type": "SUPER_TYPE",
+              "children": []
+            }
+          ]
+        },
         {
           "label": "serialVersionUID",
           "type": "Field",