Add typeGenericity enum
This commit is contained in:
6 files changed
+110
-55
No files matched your search
@@ -4,8 +4,6 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.types;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
import org.pcollections.PSet;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
|
||||
@@ -26,7 +24,7 @@ final class BoxedPrimitive extends ClassTypeImpl {
|
||||
|
||||
// constructor called by JPrimitiveType, exactly once per type system and per primitive
|
||||
BoxedPrimitive(TypeSystem factory, JClassSymbol boxType, JPrimitiveType unboxed, PSet<SymAnnot> typeAnnots) {
|
||||
super(factory, boxType, emptyList(), true, typeAnnots); // not erased
|
||||
super(factory, boxType, typeAnnots);
|
||||
this.unboxed = unboxed;
|
||||
}
|
||||
|
||||
|
||||
@@ -28,14 +28,13 @@ import net.sourceforge.pmd.lang.java.types.JVariableSig.FieldSig;
|
||||
import net.sourceforge.pmd.util.CollectionUtil;
|
||||
|
||||
class ClassTypeImpl implements JClassType {
|
||||
|
||||
private final @Nullable JClassType enclosingType;
|
||||
|
||||
private final JClassSymbol symbol;
|
||||
private final TypeSystem ts;
|
||||
private final List<JTypeMirror> typeArgs;
|
||||
private final PSet<SymAnnot> typeAnnotations;
|
||||
private final boolean isDecl;
|
||||
private final TypeGenericity genericity;
|
||||
|
||||
private JClassType superClass;
|
||||
private List<JClassType> interfaces;
|
||||
@@ -53,7 +52,7 @@ class ClassTypeImpl implements JClassType {
|
||||
* and isDecl is false, this will represent a raw type.
|
||||
* If empty and isDecl is true, this will represent a
|
||||
* generic type declaration.
|
||||
* @param isDecl Choose a bias towards generic type declaration or raw
|
||||
* @param isRaw Choose a bias towards generic type declaration or raw
|
||||
* type. If the [rawType] param has no type parameters,
|
||||
* then this parameter makes *no difference*.
|
||||
*
|
||||
@@ -61,22 +60,53 @@ class ClassTypeImpl implements JClassType {
|
||||
* as the type's type parameters
|
||||
* @throws IllegalArgumentException if any type argument is of a primitive type.
|
||||
*/
|
||||
ClassTypeImpl(TypeSystem ts, JClassSymbol symbol, List<JTypeMirror> typeArgs, boolean isDecl, PSet<SymAnnot> typeAnnotations) {
|
||||
this(ts, null, symbol, typeArgs, typeAnnotations, isDecl);
|
||||
ClassTypeImpl(TypeSystem ts, JClassSymbol symbol, List<JTypeMirror> typeArgs, boolean isRaw, PSet<SymAnnot> typeAnnotations) {
|
||||
this(ts, null, symbol, typeArgs, typeAnnotations, isRaw);
|
||||
}
|
||||
|
||||
private ClassTypeImpl(TypeSystem ts, JClassType enclosing, JClassSymbol symbol, List<JTypeMirror> typeArgs, PSet<SymAnnot> typeAnnotations, boolean isDecl) {
|
||||
private ClassTypeImpl(TypeSystem ts, JClassType enclosing, JClassSymbol symbol, List<JTypeMirror> typeArgs, PSet<SymAnnot> typeAnnotations, boolean isRaw) {
|
||||
this.typeAnnotations = typeAnnotations;
|
||||
validateParams(enclosing, symbol, typeArgs);
|
||||
|
||||
this.ts = ts;
|
||||
this.symbol = symbol;
|
||||
this.typeArgs = typeArgs;
|
||||
this.isDecl = isDecl;
|
||||
|
||||
this.enclosingType = enclosing != null
|
||||
? enclosing
|
||||
: makeEnclosingOf(symbol);
|
||||
|
||||
this.genericity = computeGenericity(isRaw);
|
||||
|
||||
}
|
||||
|
||||
// Special ctor for boxed primitives and other specials that are built
|
||||
// during initialization of TypeSystem. This cannot call JClassSymbol#isGeneric,
|
||||
// because that would trigger parsing of the class file while the type system is not ready
|
||||
protected ClassTypeImpl(TypeSystem ts, JClassSymbol symbol, PSet<SymAnnot> typeAnnotations) {
|
||||
this.typeAnnotations = typeAnnotations;
|
||||
this.ts = ts;
|
||||
this.symbol = symbol;
|
||||
this.typeArgs = emptyList();
|
||||
this.enclosingType = null;
|
||||
this.genericity = TypeGenericity.NON_GENERIC;
|
||||
}
|
||||
|
||||
private @NonNull TypeGenericity computeGenericity(boolean isRaw) {
|
||||
boolean isGeneric = symbol.isGeneric();
|
||||
if (enclosingType != null && enclosingType.isRaw()) {
|
||||
return TypeGenericity.RAW;
|
||||
} else if (typeArgs.isEmpty()) {
|
||||
if (isGeneric && isRaw) {
|
||||
return TypeGenericity.RAW;
|
||||
} else if (isGeneric) {
|
||||
return TypeGenericity.GENERIC_TYPEDECL;
|
||||
} else {
|
||||
return TypeGenericity.NON_GENERIC;
|
||||
}
|
||||
} else {
|
||||
return TypeGenericity.GENERIC_PARAMETERIZED;
|
||||
}
|
||||
}
|
||||
|
||||
private JClassType makeEnclosingOf(JClassSymbol sym) {
|
||||
@@ -110,7 +140,7 @@ class ClassTypeImpl implements JClassType {
|
||||
if (newTypeAnnots.isEmpty() && this.typeAnnotations.isEmpty()) {
|
||||
return this;
|
||||
}
|
||||
return new ClassTypeImpl(ts, enclosingType, symbol, typeArgs, newTypeAnnots, isDecl);
|
||||
return new ClassTypeImpl(ts, enclosingType, symbol, typeArgs, newTypeAnnots, isRaw());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -139,52 +169,78 @@ class ClassTypeImpl implements JClassType {
|
||||
}
|
||||
}
|
||||
|
||||
static JTypeMirror eraseToRaw(JTypeMirror m, Substitution typeSubst) {
|
||||
// todo pretty sure this can be written m.subst(typeSubst).let { if (it === m) it else it.erasure }
|
||||
if (TypeOps.mentionsAny(m, typeSubst.getMap().keySet())) {
|
||||
return m.getErasure();
|
||||
/**
|
||||
* Given a type appearing in a member, and the given substitution
|
||||
*/
|
||||
static JTypeMirror eraseToRaw(JTypeMirror t, Substitution typeSubst) {
|
||||
if (TypeOps.mentionsAny(t, typeSubst.getMap().keySet())) {
|
||||
return t.getErasure();
|
||||
} else {
|
||||
// less brutal than erasure,
|
||||
// some parameterized types should be kept, if they don't depend
|
||||
// on type parameters of this method
|
||||
return m.subst(typeSubst);
|
||||
// This type does not depend on any of the type variables to erase.
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a type appearing in a member of the given owner, erase
|
||||
* the member type if the owner is raw. The type needs not be erased
|
||||
* if it is generic but does not mention any of the type variables
|
||||
* to erase.
|
||||
*/
|
||||
static JTypeMirror maybeEraseMemberType(JClassType owner, JTypeMirror t) {
|
||||
if (owner.isRaw() && TypeOps.mentionsAny(t, owner.getTypeParamSubst().getMap().keySet())) {
|
||||
return t.getErasure();
|
||||
} else {
|
||||
// This type does not depend on any of the type variables to erase.
|
||||
return t;
|
||||
}
|
||||
}
|
||||
|
||||
static List<JTypeMirror> maybeEraseMemberType(JClassType owner, List<JTypeMirror> ts) {
|
||||
if (owner.isRaw()) {
|
||||
return map(ts, t -> maybeEraseMemberType(owner, t));
|
||||
} else {
|
||||
// This type does not depend on any of the type variables to erase.
|
||||
return ts;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public JClassType selectInner(JClassSymbol symbol, List<? extends JTypeMirror> targs, PSet<SymAnnot> typeAnnotations) {
|
||||
public final JClassType selectInner(JClassSymbol symbol, List<? extends JTypeMirror> targs, PSet<SymAnnot> typeAnnotations) {
|
||||
return new ClassTypeImpl(ts,
|
||||
this,
|
||||
symbol,
|
||||
CollectionUtil.defensiveUnmodifiableCopy(targs),
|
||||
typeAnnotations,
|
||||
this.isDecl);
|
||||
isRaw());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRaw() {
|
||||
return !isDecl && isGeneric() && typeArgs.isEmpty()
|
||||
|| getEnclosingType() != null && getEnclosingType().isRaw();
|
||||
public final boolean isRaw() {
|
||||
return genericity == TypeGenericity.RAW;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGenericTypeDeclaration() {
|
||||
return isDecl && isGeneric() && typeArgs.isEmpty();
|
||||
public final boolean isGenericTypeDeclaration() {
|
||||
return genericity == TypeGenericity.GENERIC_TYPEDECL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isParameterizedType() {
|
||||
return isGeneric() && !typeArgs.isEmpty();
|
||||
public final boolean isParameterizedType() {
|
||||
return genericity == TypeGenericity.GENERIC_PARAMETERIZED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGeneric() {
|
||||
return symbol.isGeneric();
|
||||
public final boolean isGeneric() {
|
||||
return genericity != TypeGenericity.NON_GENERIC;
|
||||
}
|
||||
|
||||
@Override
|
||||
public JClassType getGenericTypeDeclaration() {
|
||||
return isGeneric() ? withTypeArguments(getFormalTypeParams()) : this;
|
||||
if (isGenericTypeDeclaration() || !isGeneric()) {
|
||||
return this;
|
||||
}
|
||||
return new ClassTypeImpl(ts, symbol, emptyList(), false, typeAnnotations);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -222,7 +278,7 @@ class ClassTypeImpl implements JClassType {
|
||||
if (expected == 0 && typeArgs.isEmpty() && this.typeArgs.isEmpty()) {
|
||||
return this; // non-generic
|
||||
}
|
||||
return new ClassTypeImpl(ts, symbol, CollectionUtil.defensiveUnmodifiableCopy(typeArgs), false, typeAnnotations);
|
||||
return new ClassTypeImpl(ts, symbol, CollectionUtil.defensiveUnmodifiableCopy(typeArgs), true, typeAnnotations);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -261,7 +317,7 @@ class ClassTypeImpl implements JClassType {
|
||||
|
||||
private JClassType getDeclaredClass(JClassSymbol inner) {
|
||||
if (Modifier.isStatic(inner.getModifiers())) {
|
||||
return new ClassTypeImpl(ts, null, inner, emptyList(), typeAnnotations, true);
|
||||
return new ClassTypeImpl(ts, null, inner, emptyList(), typeAnnotations, isRaw());
|
||||
} else {
|
||||
return selectInner(inner, emptyList());
|
||||
}
|
||||
@@ -281,7 +337,7 @@ class ClassTypeImpl implements JClassType {
|
||||
JClassSymbol declaredClass = symbol.getDeclaredClass(simpleName);
|
||||
if (declaredClass != null) {
|
||||
if (Modifier.isStatic(declaredClass.getModifiers())) {
|
||||
return new ClassTypeImpl(ts, null, declaredClass, emptyList(), HashTreePSet.empty(), true);
|
||||
return new ClassTypeImpl(ts, null, declaredClass, emptyList(), HashTreePSet.empty(), isRaw());
|
||||
} else {
|
||||
return selectInner(declaredClass, emptyList());
|
||||
}
|
||||
@@ -406,4 +462,10 @@ class ClassTypeImpl implements JClassType {
|
||||
}
|
||||
}
|
||||
|
||||
private enum TypeGenericity {
|
||||
RAW,
|
||||
GENERIC_TYPEDECL,
|
||||
GENERIC_PARAMETERIZED,
|
||||
NON_GENERIC
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
|
||||
final class ErasedClassType extends ClassTypeImpl {
|
||||
|
||||
ErasedClassType(TypeSystem typeSystem, JClassSymbol symbol, PSet<SymAnnot> typeAnnots) {
|
||||
super(typeSystem, symbol, Collections.emptyList(), false, typeAnnots);
|
||||
super(typeSystem, symbol, Collections.emptyList(), true, typeAnnots);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -255,6 +255,7 @@ public interface JClassType extends JTypeMirror {
|
||||
/**
|
||||
* Return the list of declared nested classes. They are substituted
|
||||
* with the actual type arguments of this type, if it is parameterized.
|
||||
* They are raw if this type is raw.
|
||||
* Does not look into supertypes.
|
||||
*/
|
||||
List<JClassType> getDeclaredClasses();
|
||||
|
||||
@@ -304,7 +304,7 @@ public final class TypeSystem {
|
||||
|
||||
private JClassType addSpecial(Class<?> klass, Map<JClassSymbol, JTypeMirror> shared) {
|
||||
JClassSymbol sym = getBootStrapSymbol(klass);
|
||||
JClassType nonErased = new ClassTypeImpl(this, sym, emptyList(), false, HashTreePSet.empty());
|
||||
JClassType nonErased = new ClassTypeImpl(this, sym, HashTreePSet.empty());
|
||||
shared.put(sym, nonErased);
|
||||
return nonErased;
|
||||
}
|
||||
@@ -455,7 +455,7 @@ public final class TypeSystem {
|
||||
assert component != null : "the symbol necessarily has an array component symbol";
|
||||
return arrayType(component, classSym);
|
||||
} else {
|
||||
return new ClassTypeImpl(this, classSym, emptyList(), !isErased, HashTreePSet.empty());
|
||||
return new ClassTypeImpl(this, classSym, emptyList(), isErased, HashTreePSet.empty());
|
||||
}
|
||||
} else if (symbol instanceof JTypeParameterSymbol) {
|
||||
return ((JTypeParameterSymbol) symbol).getTypeMirror();
|
||||
@@ -519,7 +519,7 @@ public final class TypeSystem {
|
||||
return rawType(klass); // note this ensures that OBJECT and such is preserved
|
||||
}
|
||||
// if the type arguments are mismatched, the constructor will throw
|
||||
return new ClassTypeImpl(this, klass, CollectionUtil.defensiveUnmodifiableCopy(typeArgs), false, HashTreePSet.empty());
|
||||
return new ClassTypeImpl(this, klass, CollectionUtil.defensiveUnmodifiableCopy(typeArgs), true, HashTreePSet.empty());
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -5,29 +5,16 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.types
|
||||
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.inspectors.forNone
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.property.Exhaustive
|
||||
import io.kotest.property.checkAll
|
||||
import io.kotest.property.exhaustive.ints
|
||||
import io.kotest.property.forAll
|
||||
import net.sourceforge.pmd.lang.ast.test.shouldBeA
|
||||
import net.sourceforge.pmd.lang.java.ast.ParserTestCtx
|
||||
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.UnresolvedClassStore
|
||||
import net.sourceforge.pmd.lang.ast.test.IntelliMarker
|
||||
import net.sourceforge.pmd.lang.ast.test.shouldBe
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.asm.createUnresolvedAsmSymbol
|
||||
import net.sourceforge.pmd.lang.java.types.TypeConversion.*
|
||||
import net.sourceforge.pmd.lang.java.types.TypeOps.Convertibility.*
|
||||
import net.sourceforge.pmd.lang.java.types.testdata.ComparableList
|
||||
import net.sourceforge.pmd.lang.java.types.testdata.SomeEnum
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
/**
|
||||
* @author Clément Fournier
|
||||
*/
|
||||
class ClassTypeImplTest : FunSpec({
|
||||
class ClassTypeImplTest : IntelliMarker,FunSpec({
|
||||
|
||||
val ts = testTypeSystem
|
||||
with(TypeDslOf(ts)) {
|
||||
@@ -36,7 +23,7 @@ class ClassTypeImplTest : FunSpec({
|
||||
|
||||
|
||||
test("Test repeated withTypeArguments on unresolved type") {
|
||||
val sym = ts.createUnresolvedAsmSymbol("does.not.Exist") as JClassSymbol
|
||||
val sym = ts.createUnresolvedAsmSymbol("does.not.Exist")
|
||||
val t = ts.declaration(sym) as JClassType
|
||||
t.withTypeArguments(listOf(t_String)).typeArgs shouldBe listOf(t_String)
|
||||
t.withTypeArguments(listOf(t_String))
|
||||
@@ -44,6 +31,13 @@ class ClassTypeImplTest : FunSpec({
|
||||
|
||||
}
|
||||
|
||||
test("Test generic type decl") {
|
||||
t_List::isRaw shouldBe true
|
||||
val `t_List{T}` = t_List.genericTypeDeclaration
|
||||
`t_List{T}`::isGenericTypeDeclaration shouldBe true
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user