Merge pull request #3270 from oowekyala:java-classpath-resilience
[java] Improve resilience to broken classpath #3270
This commit is contained in:
commit
5b814aa4b1
21 files changed
+415
-102
No files matched your search
@@ -175,7 +175,7 @@ public final class JavaAstProcessor {
|
||||
SemanticErrorReporter logger,
|
||||
TypeInferenceLogger typeInfLogger) {
|
||||
|
||||
TypeSystem typeSystem = TYPE_SYSTEMS.computeIfAbsent(classLoader, TypeSystem::new);
|
||||
TypeSystem typeSystem = TYPE_SYSTEMS.computeIfAbsent(classLoader, TypeSystem::usingClassLoaderClasspath);
|
||||
return new JavaAstProcessor(
|
||||
typeSystem,
|
||||
typeSystem.bootstrapResolver(),
|
||||
|
||||
+3
-3
@@ -27,7 +27,7 @@ public class AsmSymbolResolver implements SymbolResolver {
|
||||
static final int ASM_API_V = Opcodes.ASM9;
|
||||
|
||||
private final TypeSystem ts;
|
||||
private final ClassLoader classLoader;
|
||||
private final Classpath classLoader;
|
||||
private final SignatureParser typeLoader;
|
||||
|
||||
private final ConcurrentHashMap<String, SoftClassReference> knownStubs = new ConcurrentHashMap<>();
|
||||
@@ -38,7 +38,7 @@ public class AsmSymbolResolver implements SymbolResolver {
|
||||
*/
|
||||
private final SoftClassReference failed;
|
||||
|
||||
public AsmSymbolResolver(TypeSystem ts, ClassLoader classLoader) {
|
||||
public AsmSymbolResolver(TypeSystem ts, Classpath classLoader) {
|
||||
this.ts = ts;
|
||||
this.classLoader = classLoader;
|
||||
this.typeLoader = new SignatureParser(this);
|
||||
@@ -104,7 +104,7 @@ public class AsmSymbolResolver implements SymbolResolver {
|
||||
|
||||
@Nullable
|
||||
URL getUrlOfInternalName(String internalName) {
|
||||
return classLoader.getResource(internalName + ".class");
|
||||
return classLoader.findResource(internalName + ".class");
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.symbols.internal.asm;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.Set;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* Classpath abstraction. PMD's symbol resolver uses the classpath to
|
||||
* find class files.
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface Classpath {
|
||||
|
||||
/**
|
||||
* Returns a URL to load the given resource if it exists in this classpath.
|
||||
* Otherwise returns null. This will typically be used to find Java class files.
|
||||
* A typical input would be {@code java/lang/String.class}.
|
||||
*
|
||||
* @param resourcePath Resource path, as described in {@link ClassLoader#getResource(String)}
|
||||
*
|
||||
* @return A URL if the resource exists, otherwise null
|
||||
*/
|
||||
@Nullable URL findResource(String resourcePath);
|
||||
|
||||
// <editor-fold defaultstate="collapsed" desc="Transformation methods (defaults)">
|
||||
|
||||
/**
|
||||
* Return a classpath that will ignore the given classpath entries,
|
||||
* even if they are present in this classpath. Every call to {@link #findResource(String)}
|
||||
* is otherwise delegated to this one.
|
||||
*
|
||||
* @param deletedEntries Set of resource paths to exclude
|
||||
*/
|
||||
default Classpath exclude(Set<String> deletedEntries) {
|
||||
return resourcePath -> deletedEntries.contains(resourcePath) ? null : findResource(resourcePath);
|
||||
}
|
||||
|
||||
default Classpath delegateTo(Classpath c) {
|
||||
return path -> {
|
||||
URL p = findResource(path);
|
||||
if (p != null) {
|
||||
return p;
|
||||
}
|
||||
return c.findResource(path);
|
||||
};
|
||||
}
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
// <editor-fold defaultstate="collapsed" desc="Creator methods">
|
||||
|
||||
|
||||
/**
|
||||
* Returns a classpath instance that uses {@link ClassLoader#getResource(String)}
|
||||
* to find resources.
|
||||
*/
|
||||
static Classpath forClassLoader(ClassLoader classLoader) {
|
||||
return classLoader::getResource;
|
||||
}
|
||||
|
||||
static Classpath contextClasspath() {
|
||||
return forClassLoader(Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
|
||||
// </editor-fold>
|
||||
|
||||
}
|
||||
@@ -168,7 +168,7 @@ class ClassTypeImpl implements JClassType {
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
||||
public List<JTypeMirror> getTypeArgs() {
|
||||
return isGenericTypeDeclaration() ? (List) getFormalTypeParams() : typeArgs;
|
||||
}
|
||||
@@ -199,9 +199,7 @@ class ClassTypeImpl implements JClassType {
|
||||
}
|
||||
|
||||
int expected = symbol.getTypeParameterCount();
|
||||
if (typeArgs.size() != expected && !typeArgs.isEmpty()) {
|
||||
throw invalidTypeArgs(symbol, typeArgs);
|
||||
} else if (expected == 0) {
|
||||
if (expected == 0 && typeArgs.isEmpty() && this.typeArgs.isEmpty()) {
|
||||
return this; // non-generic
|
||||
}
|
||||
return new ClassTypeImpl(ts, symbol, CollectionUtil.defensiveUnmodifiableCopy(typeArgs), false);
|
||||
@@ -344,19 +342,6 @@ class ClassTypeImpl implements JClassType {
|
||||
checkUserEnclosingTypeIsOk(enclosing, symbol);
|
||||
|
||||
if (!typeArgsAreOk(symbol, typeArgs)) {
|
||||
// fixme relax this
|
||||
// This will throw if the symbol is unresolved and was
|
||||
// resolved through AsmSymbolResolver (ie, a missing dependency
|
||||
// in classpath, found in a signature of some ASM class symbol member).
|
||||
// Currently the AST symbol impl tries to patch unresolved symbols by
|
||||
// making the number of type params flexible. But this does not help
|
||||
// the ASM implementation, and these errors are frequent if your classpath
|
||||
// is missing something. We still want pmd to continue processing in this case.
|
||||
// The best fix IMO is to admit malformed types provided they're unresolved.
|
||||
// We'll have to abandon the assumption that every parameterized type for
|
||||
// the same symbol has the same number of type params. And also, that the
|
||||
// formal type parameter list always matches the type argument lists in length.
|
||||
// Many places rely on this... For instance: TypeConversion#capture, TypeOps#isSameType, etc
|
||||
throw invalidTypeArgs(symbol, typeArgs);
|
||||
}
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol;
|
||||
|
||||
/**
|
||||
* Represents class and interface types, including functional interface
|
||||
@@ -128,31 +129,44 @@ public interface JClassType extends JTypeMirror {
|
||||
|
||||
/**
|
||||
* A specific instantiation of the type variables in {@link #getFormalTypeParams()}.
|
||||
* Note that the type arguments and formal type parameters may be mismatched in size,
|
||||
* (only if the symbol is unresolved). In any case, no attempt is made to check that
|
||||
* the type arguments conform to the bound on type parameters in methods like
|
||||
* {@link #withTypeArguments(List)}, although this is taken into account during type
|
||||
* inference.
|
||||
*
|
||||
* <p>If this type is not generic, or a raw type, returns an empty list.
|
||||
* <p>If this is a {@linkplain #isGenericTypeDeclaration() generic type declaration},
|
||||
* returns exactly the same list as {@link #getFormalTypeParams()}.
|
||||
*
|
||||
* @see #getFormalTypeParams()
|
||||
*/
|
||||
List<JTypeMirror> getTypeArgs();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the list of type variables declared by the generic type declaration.
|
||||
* These match {@link #getTypeArgs()} if this is a {@linkplain #isGenericTypeDeclaration() generic type
|
||||
* declaration},
|
||||
* which is distinct from a {@linkplain #isRaw() raw type}.
|
||||
*
|
||||
* <p>If this type is not generic, returns an empty list.
|
||||
* <p>If this type is not generic, returns an empty list. Note that if the symbol
|
||||
* is unresolved, it is considered non-generic. But it still may have type arguments.
|
||||
*
|
||||
* @see #getTypeArgs()
|
||||
*/
|
||||
List<JTypeVar> getFormalTypeParams();
|
||||
|
||||
|
||||
/**
|
||||
* Returns the substitution mapping the formal type parameters of all
|
||||
* enclosing types to type arguments. If a type is raw, then its type
|
||||
* enclosing types to their type arguments. If a type is raw, then its type
|
||||
* parameters are not part of the returned mapping. Note, that this
|
||||
* does not include type parameters of the supertypes.
|
||||
*
|
||||
* <p>If this type is erased, returns a substitution erasing all type
|
||||
* parameters.
|
||||
*
|
||||
* <p>For instance, in the type {@code List<String>}, this is the substitution mapping
|
||||
* the type parameter {@code T} of {@code interface List<T>} to {@code String}.
|
||||
* It is suitable for use in e.g. {@link JMethodSymbol#getReturnType(Substitution)}.
|
||||
*/
|
||||
Substitution getTypeParamSubst();
|
||||
|
||||
@@ -174,8 +188,7 @@ public interface JClassType extends JTypeMirror {
|
||||
* @throws IllegalArgumentException If the symbol is not a member type
|
||||
* of this type (local/anon classes don't work)
|
||||
* @throws IllegalArgumentException If the type arguments don't match the
|
||||
* type parameters of the symbol (unless they're empty,
|
||||
* in which case the selected type is raw)
|
||||
* type parameters of the symbol (see {@link #withTypeArguments(List)})
|
||||
* @throws IllegalArgumentException If this type is raw and the inner type is not,
|
||||
* or this type is parameterized and the inner type is not
|
||||
*/
|
||||
@@ -248,7 +261,8 @@ public interface JClassType extends JTypeMirror {
|
||||
*
|
||||
* @throws IllegalArgumentException If the type argument list doesn't
|
||||
* match the type parameters of this
|
||||
* type in length
|
||||
* type in length. If the symbol is unresolved,
|
||||
* any number of type arguments is accepted.
|
||||
* @throws IllegalArgumentException If any type of the list is null, or
|
||||
* a primitive type
|
||||
*/
|
||||
|
||||
@@ -186,7 +186,7 @@ public final class JIntersectionType implements JTypeMirror {
|
||||
}
|
||||
} else if (ci instanceof JClassType) {
|
||||
// must be an interface, as per isExclusiveBlabla
|
||||
assert ci.isInterface();
|
||||
assert ci.isInterface() || TypeOps.hasUnresolvedSymbol(ci);
|
||||
} else {
|
||||
throw malformedIntersection(primary, flattened);
|
||||
}
|
||||
|
||||
@@ -159,8 +159,6 @@ public final class TypeConversion {
|
||||
List<JTypeMirror> typeArgs = type.getTypeArgs();
|
||||
List<JTypeVar> typeParams = type.getFormalTypeParams();
|
||||
|
||||
assert typeParams.size() == typeArgs.size() : "Type is not well formed " + type + " (expects " + typeParams.size() + " params)";
|
||||
|
||||
// This is the algorithm described at https://docs.oracle.com/javase/specs/jls/se10/html/jls-5.html#jls-5.1.10
|
||||
|
||||
// Let G name a generic type declaration (§8.1.2, §9.1.2)
|
||||
@@ -176,11 +174,14 @@ public final class TypeConversion {
|
||||
|
||||
List<JTypeMirror> freshVars = makeFreshVars(type);
|
||||
|
||||
// types may be non-well formed if the symbol is unresolved
|
||||
// in this case the typeParams list is most likely empty
|
||||
boolean wellFormed = typeParams.size() == freshVars.size();
|
||||
|
||||
// Map of Ai to Si, for the substitution
|
||||
Substitution subst = Substitution.mapping(typeParams, freshVars);
|
||||
Substitution subst = wellFormed ? Substitution.mapping(typeParams, freshVars) : Substitution.EMPTY;
|
||||
|
||||
for (int i = 0; i < typeArgs.size(); i++) {
|
||||
JTypeVar param = typeParams.get(i); // Ai
|
||||
JTypeMirror fresh = freshVars.get(i); // Si
|
||||
JTypeMirror arg = typeArgs.get(i); // Ti
|
||||
|
||||
@@ -191,7 +192,7 @@ public final class TypeConversion {
|
||||
JWildcardType w = (JWildcardType) arg; // Ti alias
|
||||
TypeVarImpl.CapturedTypeVar freshVar = (TypeVarImpl.CapturedTypeVar) fresh; // Si alias
|
||||
|
||||
JTypeMirror prevUpper = param.getUpperBound(); // Ui
|
||||
JTypeMirror prevUpper = wellFormed ? typeParams.get(i).getUpperBound() : ts.OBJECT; // Ui
|
||||
JTypeMirror substituted = TypeOps.subst(prevUpper, subst);
|
||||
|
||||
if (w.isUnbounded()) {
|
||||
|
||||
@@ -387,11 +387,15 @@ public final class TypeOps {
|
||||
} else if (isSpecialUnresolved(t)) {
|
||||
// error type or unresolved type
|
||||
return Convertibility.SUBTYPING;
|
||||
} else if (hasUnresolvedSymbol(t)) {
|
||||
} else if (hasUnresolvedSymbol(t) && t instanceof JClassType) {
|
||||
// This also considers types with an unresolved symbol
|
||||
// subtypes of (nearly) anything. This allows them to
|
||||
// pass bound checks on type variables.
|
||||
return Convertibility.subtypeIf(s instanceof JClassType); // excludes array or so
|
||||
if (Objects.equals(t.getSymbol(), s.getSymbol())) {
|
||||
return typeArgsAreContained((JClassType) t, (JClassType) s);
|
||||
} else {
|
||||
return Convertibility.subtypeIf(s instanceof JClassType); // excludes array or so
|
||||
}
|
||||
} else if (s instanceof JIntersectionType) { // TODO test intersection with tvars & arrays
|
||||
// If S is an intersection, then T must conform to *all* bounds of S
|
||||
// Symmetrically, if T is an intersection, T <: S requires only that
|
||||
@@ -692,6 +696,11 @@ public final class TypeOps {
|
||||
: Convertibility.UNCHECKED_WARNING;
|
||||
}
|
||||
|
||||
if (targs.size() != sargs.size()) {
|
||||
// types are not well-formed
|
||||
return Convertibility.NEVER;
|
||||
}
|
||||
|
||||
Convertibility result = Convertibility.SUBTYPING;
|
||||
for (int i = 0; i < targs.size(); i++) {
|
||||
Convertibility sub = typeArgContains(sargs.get(i), targs.get(i));
|
||||
@@ -1360,8 +1369,8 @@ public final class TypeOps {
|
||||
* this does not check the static modifier, and tests for hiding
|
||||
* if the method is static.
|
||||
*
|
||||
* @param m Method to test
|
||||
* @param origin Site of the potential override
|
||||
* @param m Method to test
|
||||
* @param origin Site of the potential override
|
||||
*/
|
||||
public static boolean isOverridableIn(JExecutableSymbol m, JTypeDeclSymbol origin) {
|
||||
if (m instanceof JConstructorSymbol) {
|
||||
@@ -1664,11 +1673,12 @@ public final class TypeOps {
|
||||
|
||||
// Notice that this loop needs a well-behaved subtyping relation,
|
||||
// i.e. antisymmetric: A <: B && A != B implies not(B <: A)
|
||||
// This is not the case if we include unchecked conversion in there.
|
||||
// This is not the case if we include unchecked conversion in there,
|
||||
// or special provisions for unresolved types.
|
||||
vLoop:
|
||||
for (JTypeMirror v : set) {
|
||||
for (JTypeMirror w : set) {
|
||||
if (!w.equals(v) && isSubtypePure(w, v).bySubtyping()) {
|
||||
if (!w.equals(v) && !hasUnresolvedSymbol(w) && isSubtypePure(w, v).bySubtyping()) {
|
||||
continue vLoop;
|
||||
}
|
||||
}
|
||||
@@ -1936,9 +1946,14 @@ public final class TypeOps {
|
||||
return t == ts.UNKNOWN || t == ts.ERROR;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the argument is a {@link JClassType} with
|
||||
* {@linkplain JClassSymbol#isUnresolved() an unresolved symbol} or
|
||||
* a {@link JArrayType} whose element type matches the first criterion.
|
||||
*/
|
||||
public static boolean hasUnresolvedSymbol(@Nullable JTypeMirror t) {
|
||||
if (!(t instanceof JClassType)) {
|
||||
return false;
|
||||
return t instanceof JArrayType && hasUnresolvedSymbol(((JArrayType) t).getElementType());
|
||||
}
|
||||
return t.getSymbol() != null && t.getSymbol().isUnresolved();
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolResolver;
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.UnresolvedClassStore;
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.asm.AsmSymbolResolver;
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.asm.Classpath;
|
||||
import net.sourceforge.pmd.lang.java.types.BasePrimitiveSymbol.RealPrimitiveSymbol;
|
||||
import net.sourceforge.pmd.lang.java.types.BasePrimitiveSymbol.VoidSymbol;
|
||||
import net.sourceforge.pmd.lang.java.types.JPrimitiveType.PrimitiveTypeKind;
|
||||
@@ -53,7 +54,7 @@ import net.sourceforge.pmd.util.CollectionUtil;
|
||||
* <p>The lifetime of a type system is the analysis: it is shared by
|
||||
* all compilation units.
|
||||
* TODO this is hacked together by comparing the ClassLoader, but this
|
||||
* should be in the language instance
|
||||
* should be in the language instance
|
||||
*
|
||||
* <p>Nodes have a reference to the type system they were created for:
|
||||
* {@link JavaNode#getTypeSystem()}.
|
||||
@@ -179,8 +180,20 @@ public final class TypeSystem {
|
||||
* to populate the fields of the new type
|
||||
* system
|
||||
*/
|
||||
public TypeSystem(ClassLoader bootstrapResourceLoader) {
|
||||
this(ts -> new AsmSymbolResolver(ts, bootstrapResourceLoader));
|
||||
public static TypeSystem usingClassLoaderClasspath(ClassLoader bootstrapResourceLoader) {
|
||||
return usingClasspath(Classpath.forClassLoader(bootstrapResourceLoader));
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds a new type system. Its public fields will be initialized
|
||||
* with fresh types, unrelated to other types.
|
||||
*
|
||||
* @param bootstrapResourceLoader Classpath used to resolve class files
|
||||
* to populate the fields of the new type
|
||||
* system
|
||||
*/
|
||||
public static TypeSystem usingClasspath(Classpath bootstrapResourceLoader) {
|
||||
return new TypeSystem(ts -> new AsmSymbolResolver(ts, bootstrapResourceLoader));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -482,17 +495,12 @@ public final class TypeSystem {
|
||||
return typeOf(klass, false);
|
||||
}
|
||||
|
||||
|
||||
// TODO spec
|
||||
// - should be equivalent to rawType(klass).withTypeArguments(typeArgs)
|
||||
// - should not accept malformed types, esp. those where there is an enclosing type
|
||||
// - test: should not recreate OBJECT
|
||||
|
||||
/**
|
||||
* Produce a parameterized type with the given symbol and type arguments.
|
||||
* The type argument list must match the declared formal type parameters in
|
||||
* length. Non-generic symbols are accepted by this method, provided the
|
||||
* argument list is empty.
|
||||
* argument list is empty. If the symbol is unresolved, any type argument
|
||||
* list is accepted.
|
||||
*
|
||||
* <p>This method is equivalent to {@code rawType(klass).withTypeArguments(typeArgs)},
|
||||
* but that code would require a cast.
|
||||
@@ -724,6 +732,7 @@ public final class TypeSystem {
|
||||
}
|
||||
|
||||
private static final class NullType implements JTypeMirror {
|
||||
|
||||
private final TypeSystem ts;
|
||||
|
||||
NullType(TypeSystem ts) {
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package javasymbols.testdata;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.asm.Classpath;
|
||||
|
||||
/**
|
||||
* For this test we exclude SuperItf.class from the {@link Classpath}
|
||||
* to mimic an incomplete classpath.
|
||||
*
|
||||
* @see net.sourceforge.pmd.lang.java.symbols.internal.asm.BrokenClasspathTest
|
||||
*/
|
||||
public class BrokenGeneric<T0, T1>
|
||||
extends SuperKlass<T0, T0>
|
||||
implements SuperItf<T1, T1> {
|
||||
}
|
||||
|
||||
class SuperKlass<A, B> { }
|
||||
|
||||
interface SuperItf<A, B> { }
|
||||
@@ -37,7 +37,7 @@ public class JavaParsingHelper extends BaseParsingHelper<JavaParsingHelper, ASTC
|
||||
* default options of JavaParsingHelper. This allows constants like
|
||||
* the null type to be compared.
|
||||
*/
|
||||
public static final TypeSystem TEST_TYPE_SYSTEM = new TypeSystem(JavaParsingHelper.class.getClassLoader());
|
||||
public static final TypeSystem TEST_TYPE_SYSTEM = TypeSystem.usingClassLoaderClasspath(JavaParsingHelper.class.getClassLoader());
|
||||
|
||||
/** This just runs the parser and no processing stages. */
|
||||
public static final JavaParsingHelper JUST_PARSE = new JavaParsingHelper(Params.getDefaultNoProcess(), SemanticErrorReporter.noop(), TEST_TYPE_SYSTEM, TypeInferenceLogger.noop());
|
||||
|
||||
+3
-2
@@ -34,10 +34,11 @@ class AsmLoaderTest : FunSpec({
|
||||
|
||||
// method reference with static ctdecl & zero formal parameters (asInstanceMethod)
|
||||
|
||||
val contextClasspath = Classpath { Thread.currentThread().contextClassLoader.getResource(it) }
|
||||
|
||||
test("First ever ASM test") {
|
||||
|
||||
val symLoader = AsmSymbolResolver(testTypeSystem, Thread.currentThread().contextClassLoader)
|
||||
val symLoader = AsmSymbolResolver(testTypeSystem, contextClasspath)
|
||||
|
||||
val loaded = symLoader.resolveClassFromBinaryName("javasymbols.testdata.StaticNameCollision")!!
|
||||
|
||||
@@ -49,7 +50,7 @@ class AsmLoaderTest : FunSpec({
|
||||
}
|
||||
|
||||
val ts = testTypeSystem
|
||||
val symLoader = AsmSymbolResolver(ts, Thread.currentThread().contextClassLoader)
|
||||
val symLoader = AsmSymbolResolver(ts, contextClasspath)
|
||||
|
||||
test("Generic class") {
|
||||
|
||||
|
||||
+78
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.symbols.internal.asm
|
||||
|
||||
import io.kotest.assertions.fail
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import net.sourceforge.pmd.lang.ast.test.shouldBe
|
||||
import net.sourceforge.pmd.lang.java.types.JClassType
|
||||
import net.sourceforge.pmd.lang.java.types.TypeOps
|
||||
import net.sourceforge.pmd.lang.java.types.TypeSystem
|
||||
import net.sourceforge.pmd.lang.java.types.testTypeSystem
|
||||
|
||||
class BrokenClasspathTest : FunSpec({
|
||||
|
||||
val rootCp = Classpath.contextClasspath()
|
||||
val brokenCp = rootCp.exclude(setOf("javasymbols/testdata/SuperItf.class"))
|
||||
|
||||
test("Test classpath setup ") {
|
||||
rootCp.findResource("javasymbols/testdata/BrokenGeneric.class") shouldNotBe null
|
||||
rootCp.findResource("javasymbols/testdata/SuperItf.class") shouldNotBe null
|
||||
rootCp.findResource("javasymbols/testdata/SuperKlass.class") shouldNotBe null
|
||||
|
||||
// this one is null
|
||||
brokenCp.findResource("javasymbols/testdata/SuperItf.class") shouldBe null
|
||||
brokenCp.findResource("javasymbols/testdata/BrokenGeneric.class") shouldNotBe null
|
||||
brokenCp.findResource("javasymbols/testdata/SuperKlass.class") shouldNotBe null
|
||||
}
|
||||
|
||||
test("Test load subclass (symbol only)") {
|
||||
|
||||
val resolver = AsmSymbolResolver(testTypeSystem, brokenCp)
|
||||
|
||||
val found = resolver.resolveClassFromBinaryName("javasymbols.testdata.BrokenGeneric")
|
||||
?: fail("not found")
|
||||
|
||||
found.superclass!!::getBinaryName shouldBe "javasymbols.testdata.SuperKlass"
|
||||
found.superInterfaces!!.map { it.binaryName } shouldBe listOf("javasymbols.testdata.SuperItf")
|
||||
|
||||
withClue("Isn't resolved") {
|
||||
found.superInterfaces[0]::isUnresolved shouldBe true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test("Test load from typesystem") {
|
||||
|
||||
val ts = TypeSystem.usingClasspath(brokenCp)
|
||||
val subclassSym = ts.getClassSymbol("javasymbols.testdata.BrokenGeneric")!!
|
||||
|
||||
val unresolvedItfSym = subclassSym.superInterfaces[0]
|
||||
unresolvedItfSym::isUnresolved shouldBe true
|
||||
|
||||
// since we're loading things lazily this type hasn't tried to populate its superinterfaces
|
||||
val superItfType = ts.declaration(unresolvedItfSym) as JClassType
|
||||
val subclassType = ts.declaration(subclassSym) as JClassType
|
||||
val (tvarC, tvarD) = subclassType.formalTypeParams
|
||||
|
||||
// and now since the super interface *type* is parameterized, we'll try to create SuperItf<D,D>
|
||||
// Except SuperItf is unresolved.
|
||||
|
||||
val expected = ts.parameterise(unresolvedItfSym, listOf(tvarD, tvarD))
|
||||
|
||||
expected shouldBe superItfType.withTypeArguments(listOf(tvarD, tvarD))
|
||||
subclassType.superInterfaces[0] shouldBe expected
|
||||
|
||||
subclassType.isConvertibleTo(expected) shouldBe TypeOps.Convertibility.SUBTYPING
|
||||
}
|
||||
|
||||
})
|
||||
|
||||
fun TypeSystem.createUnresolvedAsmSymbol(binaryName: String) =
|
||||
AsmSymbolResolver(this, Classpath.contextClasspath())
|
||||
.resolveFromInternalNameCannotFail(binaryName.replace('.', '/'))
|
||||
@@ -7,6 +7,8 @@ package net.sourceforge.pmd.lang.java.types
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.asm.createUnresolvedAsmSymbol
|
||||
import net.sourceforge.pmd.lang.java.types.TypeConversion.*
|
||||
|
||||
/**
|
||||
@@ -55,6 +57,19 @@ class CaptureTest : FunSpec({
|
||||
}
|
||||
}
|
||||
|
||||
test("Capture of malformed types") {
|
||||
val sym = ts.createUnresolvedAsmSymbol("does.not.Exist") as JClassSymbol
|
||||
|
||||
val matcher = captureMatcher(`?` extends t_String).also {
|
||||
capture(sym[t_String, `?` extends t_String]) shouldBe sym[t_String, it]
|
||||
}
|
||||
|
||||
matcher.also {
|
||||
it.isCaptured shouldBe true
|
||||
it.isCaptureOf(`?` extends t_String) shouldBe true
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
|
||||
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.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({
|
||||
|
||||
val ts = testTypeSystem
|
||||
with(TypeDslOf(ts)) {
|
||||
with(gen) {
|
||||
|
||||
|
||||
|
||||
test("Test repeated withTypeArguments on unresolved type") {
|
||||
val sym = ts.createUnresolvedAsmSymbol("does.not.Exist") as JClassSymbol
|
||||
val t = ts.declaration(sym) as JClassType
|
||||
t.withTypeArguments(listOf(t_String)).typeArgs shouldBe listOf(t_String)
|
||||
t.withTypeArguments(listOf(t_String))
|
||||
.withTypeArguments(emptyList()).typeArgs shouldBe emptyList()
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
})
|
||||
@@ -10,8 +10,8 @@ import io.kotest.matchers.collections.shouldContainExactly
|
||||
import io.kotest.matchers.nulls.shouldBeNull
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.property.checkAll
|
||||
import io.kotest.property.forAll
|
||||
import net.sourceforge.pmd.lang.ast.test.shouldBeA
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.asm.createUnresolvedAsmSymbol
|
||||
|
||||
/**
|
||||
* Tests "the greatest lower bound" (glb).
|
||||
@@ -26,8 +26,8 @@ class GlbTest : FunSpec({
|
||||
|
||||
test("Test intersection minimization") {
|
||||
|
||||
forAll(ts.subtypesArb()) { (t, s) ->
|
||||
glb(t, s) == t
|
||||
checkAll(ts.subtypesArb()) { (t, s) ->
|
||||
glb(t, s) shouldBe t
|
||||
}
|
||||
|
||||
// in particular
|
||||
@@ -112,6 +112,20 @@ class GlbTest : FunSpec({
|
||||
it.components.shouldContainExactly(`t_Enum{JPrimitiveType}`, `t_List{String}`, `t_List{? extends Number}`)
|
||||
}
|
||||
}
|
||||
|
||||
test("Test GLB with unresolved things") {
|
||||
val tA = ts.declaration(ts.createUnresolvedAsmSymbol("a.A"))
|
||||
val tB = ts.declaration(ts.createUnresolvedAsmSymbol("a.B"))
|
||||
|
||||
tA shouldBeSubtypeOf tB
|
||||
tB shouldBeSubtypeOf tA
|
||||
|
||||
TypeOps.mostSpecific(setOf(tA, tB)) shouldBe setOf(tA, tB)
|
||||
|
||||
glb(tA, tB).shouldBeA<JIntersectionType> {
|
||||
it.components.shouldContainExactly(tA, tB)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -15,7 +15,9 @@ 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.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
|
||||
@@ -209,7 +211,7 @@ class SubtypingTest : FunSpec({
|
||||
|
||||
test("Test wildcard subtyping (property-based)") {
|
||||
|
||||
checkAll(ts.subtypesArb(false)) { (t, s) ->
|
||||
checkAll(ts.subtypesArb()) { (t, s) ->
|
||||
t_List[t] shouldBeSubtypeOf t_List[`?` extends s]
|
||||
t_List[s] shouldBeSubtypeOf t_List[`?` `super` t]
|
||||
}
|
||||
@@ -260,7 +262,7 @@ class SubtypingTest : FunSpec({
|
||||
|
||||
test("Unresolved symbol is compatible with any class/interface") {
|
||||
val t = ts.declaration(UnresolvedClassStore(ts).makeUnresolvedReference("obj.foo", 0))
|
||||
.shouldBeUnresolvedClass("obj.foo")
|
||||
.shouldBeUnresolvedClass("obj.foo")
|
||||
|
||||
checkAll(ts.primitiveGen) { s ->
|
||||
t shouldNotBeSubtypeOf s
|
||||
@@ -294,7 +296,7 @@ class SubtypingTest : FunSpec({
|
||||
|
||||
test("Captured subtyping wild vs wild") {
|
||||
|
||||
checkAll(ts.subtypesArb(unchecked = false)) { (t, s) ->
|
||||
checkAll(ts.subtypesArb()) { (t, s) ->
|
||||
// println("$t <: $s")
|
||||
|
||||
capture(t_List[`?` extends t]) shouldSubtypeNoCapture t_List[`?` extends s]
|
||||
@@ -315,6 +317,13 @@ class SubtypingTest : FunSpec({
|
||||
t_List[`?` extends t] shouldNotSubtypeNoCapture t_List[`?` extends someCapture]
|
||||
}
|
||||
}
|
||||
|
||||
test("Test non well-formed types") {
|
||||
val sym = ts.createUnresolvedAsmSymbol("does.not.Exist") as JClassSymbol
|
||||
sym[t_String, t_String] shouldBeUnrelatedTo sym[t_String]
|
||||
sym[t_String] shouldBeSubtypeOf sym[t_String]
|
||||
sym[t_String] shouldBeSubtypeOf sym[`?` extends t_String] // containment
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.property.checkAll
|
||||
import io.kotest.property.forAll
|
||||
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.asm.createUnresolvedAsmSymbol
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.forAllEqual
|
||||
|
||||
/**
|
||||
@@ -78,6 +80,20 @@ class TypeEqualityTest : FunSpec({
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
test("Test non well-formed types") {
|
||||
val sym = ts.createUnresolvedAsmSymbol("does.not.Exist") as JClassSymbol
|
||||
// not equal
|
||||
sym[t_String, t_String] shouldNotBe sym[t_String]
|
||||
sym[t_String] shouldNotBe sym[t_String, t_String]
|
||||
sym[t_Integer] shouldNotBe sym[t_String]
|
||||
|
||||
// equal
|
||||
sym[t_String, t_String] shouldBe sym[t_String, t_String]
|
||||
sym[t_String] shouldBe sym[t_String]
|
||||
sym[t_String, t_Integer] shouldBe sym[t_String, t_Integer]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@ import net.sourceforge.pmd.lang.java.ast.ASTTypeParameter
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTTypeParameters
|
||||
import net.sourceforge.pmd.lang.java.ast.ParserTestCtx
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.TestClassesGen
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.asm.createUnresolvedAsmSymbol
|
||||
import javax.lang.model.type.TypeMirror
|
||||
import kotlin.streams.toList
|
||||
|
||||
@@ -31,9 +32,13 @@ val TypeSystem.allTypesGen: Arb<JTypeMirror>
|
||||
}
|
||||
}
|
||||
|
||||
fun TypeSystem.subtypesArb(unchecked: Boolean = false) =
|
||||
/**
|
||||
* Only well-behaved subtypes
|
||||
*/
|
||||
fun TypeSystem.subtypesArb() =
|
||||
Arb.pair(refTypeGen, refTypeGen)
|
||||
.filter { (t, s) -> t.isConvertibleTo(s).bySubtyping() }
|
||||
.filter { (t, s) -> !TypeOps.hasUnresolvedSymbol(t) && !TypeOps.hasUnresolvedSymbol(s) }
|
||||
|
||||
infix fun Boolean.implies(v: () -> Boolean): Boolean = !this || v()
|
||||
|
||||
@@ -47,6 +52,9 @@ class RefTypeGenArb(val ts: TypeSystem) : Arb<JTypeMirror>() {
|
||||
|
||||
private fun generateTypes(rs : RandomSource) : List<JTypeMirror> {
|
||||
with(TypeDslOf(ts).gen) {
|
||||
val unresolved1 = ts.createUnresolvedAsmSymbol("some.fake.Symbol")
|
||||
val unresolved2 = ts.createUnresolvedAsmSymbol("another.fake.Symbol")
|
||||
|
||||
val pool: List<JTypeMirror> = listOf(
|
||||
`t_List{String}`,
|
||||
t_Enum,
|
||||
@@ -61,7 +69,10 @@ class RefTypeGenArb(val ts: TypeSystem) : Arb<JTypeMirror>() {
|
||||
t_CharSequence,
|
||||
t_StringBuilder,
|
||||
t_MapEntry,
|
||||
`t_Array{Object}`
|
||||
`t_Array{Object}`,
|
||||
ts.declaration(unresolved1)[t_String],
|
||||
ts.declaration(unresolved2)[t_Integer, `?` extends `t_List{? extends Number}`],
|
||||
ts.declaration(unresolved2)
|
||||
).flatMap {
|
||||
it.superTypeSet.toList() + it + it.erasure + it.toArray()
|
||||
}
|
||||
|
||||
@@ -8,9 +8,9 @@ import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.collections.shouldContainExactly
|
||||
import io.kotest.matchers.collections.shouldContainExactlyInAnyOrder
|
||||
import io.kotest.property.Arb
|
||||
import io.kotest.property.arbitrary.map
|
||||
import io.kotest.property.arbitrary.shuffle
|
||||
import io.kotest.property.checkAll
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.asm.createUnresolvedAsmSymbol
|
||||
|
||||
/**
|
||||
* @author Clément Fournier
|
||||
@@ -31,7 +31,7 @@ class TypeOpsTest : FunSpec({
|
||||
|
||||
test("Test most specific") {
|
||||
|
||||
checkAll(ts.subtypesArb(false)) { (t, s) ->
|
||||
checkAll(ts.subtypesArb()) { (t, s) ->
|
||||
TypeOps.mostSpecific(setOf(t, s)).shouldContainExactly(t)
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,16 @@ class TypeOpsTest : FunSpec({
|
||||
output = listOf(t_AbstractList))
|
||||
}
|
||||
|
||||
test("Test most specific of unresolved types") {
|
||||
val tA = ts.declaration(ts.createUnresolvedAsmSymbol("a.A"))
|
||||
val tB = ts.declaration(ts.createUnresolvedAsmSymbol("a.B"))
|
||||
|
||||
checkMostSpecific(
|
||||
input = listOf(tA, tB),
|
||||
output = listOf(tA, tB)
|
||||
)
|
||||
}
|
||||
|
||||
test("Test most specific unchecked") {
|
||||
|
||||
|
||||
|
||||
+26
-37
@@ -10,6 +10,7 @@ import io.kotest.matchers.shouldBe
|
||||
import net.sourceforge.pmd.lang.java.ast.*
|
||||
import net.sourceforge.pmd.lang.java.types.*
|
||||
import net.sourceforge.pmd.util.OptionalBool
|
||||
import org.intellij.lang.annotations.Language
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
@@ -21,8 +22,7 @@ class OverridingTest : ProcessorTestSpec({
|
||||
fun overridingSetup(name: String, mod: String) {
|
||||
|
||||
parserTest(name) {
|
||||
val acu = parser.parse(
|
||||
"""
|
||||
val acu = parser.parse("""
|
||||
class Scratch {
|
||||
$mod void m(Throwable t) { }
|
||||
}
|
||||
@@ -85,8 +85,7 @@ class Other extends Scratch {
|
||||
|
||||
|
||||
parserTest("Both inherited and visible from outer class") {
|
||||
val acu = parser.parse(
|
||||
"""
|
||||
val acu = parser.parse("""
|
||||
class Scratch {
|
||||
static void m(Throwable t) { }
|
||||
|
||||
@@ -94,7 +93,7 @@ class Scratch {
|
||||
static void m(Throwable t) { }
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val (scratchM, otherM) = acu.methodDeclarations().toList { it.genericSignature }
|
||||
@@ -118,8 +117,7 @@ class Scratch {
|
||||
|
||||
|
||||
parserTest("Primitive signatures do not merge") {
|
||||
val acu = parser.parse(
|
||||
"""
|
||||
val acu = parser.parse("""
|
||||
class Scratch {
|
||||
void m(long t) { }
|
||||
|
||||
@@ -127,7 +125,7 @@ class Scratch {
|
||||
void m(int t) { }
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val (scratchM, otherM) = acu.methodDeclarations().toList { it.genericSignature }
|
||||
@@ -139,8 +137,7 @@ class Scratch {
|
||||
}
|
||||
|
||||
parserTest("Primitive signatures do not merge 2") {
|
||||
val acu = parser.parse(
|
||||
"""
|
||||
val acu = parser.parse("""
|
||||
class Scratch {
|
||||
void m(int t) { }
|
||||
|
||||
@@ -148,7 +145,7 @@ class Scratch {
|
||||
void m(long t) { }
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val (scratchM, otherM) = acu.methodDeclarations().toList { it.genericSignature }
|
||||
@@ -162,8 +159,7 @@ class Scratch {
|
||||
|
||||
parserTest("Static generic method") {
|
||||
|
||||
val acu = parser.parse(
|
||||
"""
|
||||
val acu = parser.parse("""
|
||||
class Sup {
|
||||
static <E> E m(F<? extends E> e) { return null; }
|
||||
}
|
||||
@@ -177,7 +173,7 @@ class F<G> {
|
||||
Sub.m(); // should be Sub::m, no ambiguity
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val (supE, subE) = acu.declaredMethodSignatures()
|
||||
@@ -190,15 +186,14 @@ class F<G> {
|
||||
|
||||
parserTest("Static method with different bound") {
|
||||
|
||||
val (acu, spy) = parser.parseWithTypeInferenceSpy(
|
||||
|
||||
"""
|
||||
val (acu, spy) = parser.parseWithTypeInferenceSpy("""
|
||||
import java.util.List;
|
||||
class Sup {
|
||||
static <E> E m(F<? extends E> e) { return null; }
|
||||
static <E, _X> E m(F<? extends E> e) { return null; }
|
||||
}
|
||||
|
||||
class Sub extends Sup {
|
||||
static <S extends List<S>> F<S> m(F<? extends S> e) { return null; }
|
||||
static <S extends List<TP>, TP> F<S> m(F<? extends S> e) { return null; }
|
||||
}
|
||||
|
||||
class F<G> {
|
||||
@@ -209,7 +204,7 @@ class F<G> {
|
||||
Sub.m(new F<List<G>>());
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val (supE, subE) = acu.declaredMethodSignatures()
|
||||
@@ -218,7 +213,7 @@ class F<G> {
|
||||
// their type parameters have a different bound
|
||||
// technically this is a compile-time error:
|
||||
// both methods have the same erasure but neither hides the other
|
||||
assertFalse("Methods are override-equivalent") {
|
||||
assertFalse("Methods should not override each other\n\t$subE\n\t$supE") {
|
||||
TypeOps.overrides(subE, supE, subE.declaringType)
|
||||
}
|
||||
|
||||
@@ -230,11 +225,9 @@ class F<G> {
|
||||
|
||||
|
||||
|
||||
parserTest("Static method of interface is not inherited!") {
|
||||
parserTest("Static method of interface is not inherited!") {
|
||||
|
||||
val (acu, spy) = parser.parseWithTypeInferenceSpy(
|
||||
|
||||
"""
|
||||
val (acu, spy) = parser.parseWithTypeInferenceSpy("""
|
||||
interface List<T> {}
|
||||
|
||||
interface Sup {
|
||||
@@ -252,7 +245,7 @@ class F<G> implements List<F<G>> {
|
||||
Sub.m(new F<F<List<G>>>());
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val (supE, subE) = acu.declaredMethodSignatures()
|
||||
@@ -270,11 +263,9 @@ class F<G> implements List<F<G>> {
|
||||
|
||||
|
||||
|
||||
parserTest("Static method of interface is not inherited in subinterfaces either") {
|
||||
parserTest("Static method of interface is not inherited in subinterfaces either") {
|
||||
|
||||
val (acu, spy) = parser.parseWithTypeInferenceSpy(
|
||||
|
||||
"""
|
||||
val (acu, spy) = parser.parseWithTypeInferenceSpy("""
|
||||
interface List<T> {}
|
||||
|
||||
interface Sup {
|
||||
@@ -292,7 +283,7 @@ class F<G> implements List<F<G>> {
|
||||
Sub.m(new F<F<List<G>>>());
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val (supE, subE) = acu.declaredMethodSignatures()
|
||||
@@ -311,8 +302,7 @@ class F<G> implements List<F<G>> {
|
||||
|
||||
|
||||
parserTest("Private method shadowed in inner class") {
|
||||
val acu = parser.parse(
|
||||
"""
|
||||
val acu = parser.parse("""
|
||||
class Scratch {
|
||||
private void m(Throwable t) { } // private methods are not overridden
|
||||
|
||||
@@ -320,7 +310,7 @@ class Scratch {
|
||||
private void m(Throwable t) { }
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val (scratchM, otherM) = acu.methodDeclarations().toList { it.genericSignature }
|
||||
@@ -345,8 +335,7 @@ class Scratch {
|
||||
}
|
||||
|
||||
parserTest("Merged abstract signature in class") {
|
||||
val acu = parser.parse(
|
||||
"""
|
||||
val acu = parser.parse("""
|
||||
class Scratch {
|
||||
static void m(Throwable t) { }
|
||||
|
||||
@@ -354,7 +343,7 @@ class Scratch {
|
||||
static void m(Throwable t) { }
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
val (scratchM, otherM) = acu.methodDeclarations().toList { it.genericSignature }
|
||||
|
||||
Reference in new issue
Block a user