Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for analyzing whether a type reference is generic or not (method isGeneric) #1562

Merged
merged 2 commits into from
Sep 29, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ Spoon Roadmap
Short-term, long term and crazy ideas about Spoon

* Model
* support for analyzing bound vs unbound type references (`List<T>` vs `List<String>`)
* build model of binary code using a decompiler
* Improves usage of generics on the model (See https://github.com/INRIA/spoon/issues/583#issue-148728790)
* Add an embedded DSL / builder mechanism (see https://github.com/INRIA/spoon/pull/741)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,9 @@ public interface CtTypeInformation {
boolean isAnnotationType();

/**
* Returns true if this element is a generics (eg "T") and false if it is an actual type (eg 'Book" or "String")
* Returns true if it refers to a type parameter (ie not a concrete class, eg "T foo"). It can refer to it directly (eg T), or indirectly (eg List&lt;T&gt;, or Set&lt;List&lt;T&gt;&gt;).
*/
@DerivedProperty
boolean isGenerics();

/**
Expand Down
1 change: 1 addition & 0 deletions src/main/java/spoon/reflect/reference/CtTypeReference.java
Original file line number Diff line number Diff line change
Expand Up @@ -210,4 +210,5 @@ public interface CtTypeReference<T> extends CtReference, CtActualTypeContainer,
@DerivedProperty
CtTypeParameter getTypeParameterDeclaration();


}
Original file line number Diff line number Diff line change
Expand Up @@ -83,11 +83,6 @@ public <T extends CtTypeParameterReference> T setUpper(boolean upper) {
return (T) this;
}

@Override
public boolean isGenerics() {
return true;
}

@Override
public boolean isPrimitive() {
return false;
Expand Down Expand Up @@ -251,4 +246,15 @@ public boolean isSubtypeOf(CtTypeReference<?> type) {
public CtTypeParameterReference clone() {
return (CtTypeParameterReference) super.clone();
}

@Override
public boolean isGenerics() {
if (getDeclaration() instanceof CtTypeParameter) {
return true;
}
if (getBoundingType() != null && getBoundingType().isGenerics()) {
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -622,11 +622,6 @@ public boolean isEnum() {
}
}

@Override
public boolean isGenerics() {
return false;
}

@Override
public boolean canAccess(CtTypeReference<?> type) {
try {
Expand Down Expand Up @@ -824,6 +819,19 @@ public CtTypeParameter getTypeParameterDeclaration() {
return null;
}

@Override
public boolean isGenerics() {
if (getDeclaration() instanceof CtTypeParameter) {
return true;
}
for (CtTypeReference ref : getActualTypeArguments()) {
if (ref.isGenerics()) {
return true;
}
}
return false;
}

private CtTypeParameter findTypeParamDeclarationByPosition(CtFormalTypeDeclarer type, int position) {
return type.getFormalCtTypeParameters().get(position);
}
Expand Down
175 changes: 174 additions & 1 deletion src/test/java/spoon/test/generics/GenericsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import spoon.reflect.declaration.CtTypeMember;
import spoon.reflect.declaration.CtTypeParameter;
import spoon.reflect.declaration.CtTypedElement;
import spoon.reflect.declaration.CtVariable;
import spoon.reflect.factory.Factory;
import spoon.reflect.reference.CtReference;
import spoon.reflect.reference.CtTypeParameterReference;
Expand Down Expand Up @@ -59,7 +60,9 @@
import java.io.File;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
Expand Down Expand Up @@ -599,7 +602,10 @@ public void testIsGenericsMethod() throws Exception {

CtTypeReference ctTypeReference = aTacos.getSuperInterfaces().toArray(new CtTypeReference[aTacos.getSuperInterfaces().size()])[0];
assertFalse(aTacos.isGenerics());
assertFalse(ctTypeReference.isGenerics());

// this is a generic type reference spoon.test.generics.testclasses.ITacos<V>
assertEquals("spoon.test.generics.testclasses.ITacos<V>", ctTypeReference.toString());
assertTrue(ctTypeReference.isGenerics());
}
@Test
public void testTypeParameterReferenceAsActualTypeArgument() throws Exception {
Expand Down Expand Up @@ -655,6 +661,173 @@ public void testGenericTypeReference() throws Exception {
}
}
@Test
public void testisGeneric() throws Exception {
Factory factory = build(new File("src/test/java/spoon/test/generics/testclasses"));

/*
// this code has been used to generate the list of assertEquals below,
// and then each assertEquals was verified
Set<String> s = new HashSet<>();
factory.getModel().getElements(new TypeFilter<CtTypeReference>(CtTypeReference.class) {
@Override
public boolean matches(CtTypeReference element) {
return super.matches(element) && element.getParent() instanceof CtVariable;
}
}).forEach(x -> {
String simpleName = ((CtVariable) x.getParent()).getSimpleName();
if (!s.contains(simpleName)) {
System.out.println("\t\t// "+x.toString());
System.out.println("\t\tCtTypeReference<?> "+simpleName+"Ref = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, \"" + simpleName+ "\")).first(CtVariable.class).getType();");
System.out.println("\t\tassertEquals("+x.isGeneric() + ", " + simpleName + "Ref.isGeneric());");
System.out.println();
}
s.add(simpleName);
});
*/

// T
CtTypeReference<?> var1Ref = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "var1")).first(CtVariable.class).getType();
assertEquals(true, var1Ref.isGenerics());

// spoon.test.generics.testclasses.rxjava.Subscriber<? super T>
CtTypeReference<?> sRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "s")).first(CtVariable.class).getType();
assertEquals(true, sRef.isGenerics());

// spoon.test.generics.testclasses.rxjava.Try<java.util.Optional<java.lang.Object>>
CtTypeReference<?> notificationRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "notification")).first(CtVariable.class).getType();
assertEquals(false, notificationRef.isGenerics());

// java.util.function.Function<? super spoon.test.generics.testclasses.rxjava.Observable<spoon.test.generics.testclasses.rxjava.Try<java.util.Optional<java.lang.Object>>>, ? extends spoon.test.generics.testclasses.rxjava.Publisher<?>>
CtTypeReference<?> managerRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "manager")).first(CtVariable.class).getType();
assertEquals(false, managerRef.isGenerics());

// spoon.test.generics.testclasses.rxjava.BehaviorSubject<spoon.test.generics.testclasses.rxjava.Try<java.util.Optional<java.lang.Object>>>
CtTypeReference<?> subjectRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "subject")).first(CtVariable.class).getType();
assertEquals(false, subjectRef.isGenerics());

// spoon.test.generics.testclasses.rxjava.PublisherRedo.RedoSubscriber<T>
CtTypeReference<?> parentRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "parent")).first(CtVariable.class).getType();
assertEquals(true, parentRef.isGenerics());

// spoon.test.generics.testclasses.rxjava.Publisher<?>
CtTypeReference<?> actionRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "action")).first(CtVariable.class).getType();
assertEquals(false, actionRef.isGenerics());

// spoon.test.generics.testclasses.rxjava.ToNotificationSubscriber
CtTypeReference<?> trucRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "truc")).first(CtVariable.class).getType();
assertEquals(false, trucRef.isGenerics());

// java.util.function.Consumer<? super spoon.test.generics.testclasses.rxjava.Try<java.util.Optional<java.lang.Object>>>
CtTypeReference<?> consumerRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "consumer")).first(CtVariable.class).getType();
assertEquals(false, consumerRef.isGenerics());

// S
CtTypeReference<?> sectionRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "section")).first(CtVariable.class).getType();
assertEquals(true, sectionRef.isGenerics());

// X
CtTypeReference<?> paramARef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "paramA")).first(CtVariable.class).getType();
assertEquals(true, paramARef.isGenerics());

// spoon.test.generics.testclasses.Tacos
CtTypeReference<?> paramBRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "paramB")).first(CtVariable.class).getType();
assertEquals(false, paramBRef.isGenerics());

// C
CtTypeReference<?> paramCRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "paramC")).first(CtVariable.class).getType();
assertEquals(true, paramCRef.isGenerics());

// R
CtTypeReference<?> cookRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "cook")).first(CtVariable.class).getType();
assertEquals(true, cookRef.isGenerics());

// spoon.test.generics.testclasses.CelebrationLunch<java.lang.Integer, java.lang.Long, java.lang.Double>
CtTypeReference<?> clRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "cl")).first(CtVariable.class).getType();
assertEquals(false, clRef.isGenerics());

// spoon.test.generics.testclasses.CelebrationLunch<java.lang.Integer, java.lang.Long, java.lang.Double>.WeddingLunch<spoon.test.generics.testclasses.Mole>
CtTypeReference<?> disgustRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "disgust")).first(CtVariable.class).getType();
assertEquals(false, disgustRef.isGenerics());

// L
CtTypeReference<?> paramRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "param")).first(CtVariable.class).getType();
assertEquals(true, paramRef.isGenerics());

// spoon.reflect.declaration.CtType<? extends spoon.reflect.declaration.CtNamedElement>
CtTypeReference<?> targetTypeRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "targetType")).first(CtVariable.class).getType();
assertEquals(false, targetTypeRef.isGenerics());

// spoon.reflect.declaration.CtType<?>
CtTypeReference<?> somethingRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "something")).first(CtVariable.class).getType();
assertEquals(false, somethingRef.isGenerics());

// int
CtTypeReference<?> iRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "i")).first(CtVariable.class).getType();
assertEquals(false, iRef.isGenerics());

// T
CtTypeReference<?> biduleRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "bidule")).first(CtVariable.class).getType();
assertEquals(true, biduleRef.isGenerics());

// Cook<java.lang.String>
CtTypeReference<?> aClassRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "aClass")).first(CtVariable.class).getType();
assertEquals(false, aClassRef.isGenerics());

// java.util.List<java.util.List<M>>
CtTypeReference<?> list2mRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "list2m")).first(CtVariable.class).getType();
assertEquals(true, list2mRef.isGenerics());

// spoon.test.generics.testclasses.Panini.Subscriber<? extends java.lang.Long>
CtTypeReference<?> tRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "t")).first(CtVariable.class).getType();
assertEquals(false, tRef.isGenerics());

// spoon.test.generics.testclasses.Spaghetti<B>.Tester
CtTypeReference<?> testerRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "tester")).first(CtVariable.class).getType();
assertEquals(false, testerRef.isGenerics());

// spoon.test.generics.testclasses.Spaghetti<B>.Tester
CtTypeReference<?> tester1Ref = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "tester1")).first(CtVariable.class).getType();
assertEquals(false, tester1Ref.isGenerics());

// spoon.test.generics.testclasses.Spaghetti<B>.That<java.lang.String, java.lang.String>
CtTypeReference<?> fieldRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "field")).first(CtVariable.class).getType();
assertEquals(false, fieldRef.isGenerics());

// spoon.test.generics.testclasses.Spaghetti<java.lang.String>.That<java.lang.String, java.lang.String>
CtTypeReference<?> field1Ref = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "field1")).first(CtVariable.class).getType();
assertEquals(false, field1Ref.isGenerics());

// spoon.test.generics.testclasses.Spaghetti<java.lang.Number>.That<java.lang.String, java.lang.String>
CtTypeReference<?> field2Ref = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "field2")).first(CtVariable.class).getType();
assertEquals(false, field2Ref.isGenerics());

// spoon.test.generics.testclasses.Tacos<K, java.lang.String>.Burritos<K, V>
CtTypeReference<?> burritosRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "burritos")).first(CtVariable.class).getType();
assertEquals(true, burritosRef.isGenerics());

// int
CtTypeReference<?> nbTacosRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "nbTacos")).first(CtVariable.class).getType();
assertEquals(false, nbTacosRef.isGenerics());

// java.util.List<java.lang.String>
CtTypeReference<?> lRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "l")).first(CtVariable.class).getType();
assertEquals(false, lRef.isGenerics());

// java.util.List
CtTypeReference<?> l2Ref = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "l2")).first(CtVariable.class).getType();
assertEquals(false, l2Ref.isGenerics());

// java.util.List<?>
CtTypeReference<?> l3Ref = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "l3")).first(CtVariable.class).getType();
assertEquals(false, l3Ref.isGenerics());

// T
CtTypeReference<?> anObjectRef = factory.getModel().getRootPackage().filterChildren(new NamedElementFilter(CtVariable.class, "anObject")).first(CtVariable.class).getType();
assertEquals(true, anObjectRef.isGenerics());

}
@Test
public void testCtTypeReference_getSuperclass() throws Exception {
Factory factory = build(new File("src/test/java/spoon/test/generics/testclasses"));
CtClass<?> ctClassCelebrationLunch = factory.Class().get(CelebrationLunch.class);
Expand Down