Merge branch '7.0.x' into pr/3271

This commit is contained in:
Clément Fournier committed 2021-05-15 17:13:49 +02:00
commit c063e14d0c
80 files changed
+2634 -670

No files matched your search

+1 -1
View File
@@ -110,7 +110,7 @@
<rule ref="category/java/codestyle.xml/UnnecessaryLocalBeforeReturn"/>
<rule ref="category/java/codestyle.xml/UnnecessaryModifier"/>
<rule ref="category/java/codestyle.xml/UnnecessaryReturn"/>
<!-- <rule ref="category/java/codestyle.xml/UseDiamondOperator"/> -->
<rule ref="category/java/codestyle.xml/UseDiamondOperator"/>
<rule ref="category/java/codestyle.xml/UseShortArrayInitializer"/>
<rule ref="category/java/codestyle.xml/UseUnderscoresInNumericLiterals"/>
<rule ref="category/java/codestyle.xml/UselessParentheses"/>
+12 -9
View File
@@ -80,15 +80,17 @@ The default version is always ES6.
##### Java
* {% rule "java/codestyle/UnnecessaryFullyQualifiedName" %} has two new properties, to selectively disable reporting on
static field and method qualifiers. The rule also has been improved to be more precise.
* The rule {% rule "java/codestyle/UselessParentheses" %} has two new properties which control how strict
the rule should be applied. With `ignoreClarifying` (default: true) parentheses that are strictly speaking
not necessary are allowed, if they separate expressions of different precedence.
The other property `ignoreBalancing` (default: true) is similar, in that it allows parentheses that help
reading and understanding the expressions.
* The rule {% rule "java/bestpractices/LooseCoupling" %} has a new property to allow some types to be coupled to (`allowedTypes`).
* {% rule "java/errorprone/EmptyCatchBlock" %}: `CloneNotSupportedException` and `InterruptedException` are not special-cased anymore. Rename the exception parameter to `ignored` to ignore them.
* {% rule "java/codestyle/UnnecessaryFullyQualifiedName" %}: the rule has two new properties,
to selectively disable reporting on static field and method qualifiers. The rule also has been improved to be more precise.
* {% rule "java/codestyle/UselessParentheses" %}: the rule has two new properties which control how strict
the rule should be applied. With `ignoreClarifying` (default: true) parentheses that are strictly speaking
not necessary are allowed, if they separate expressions of different precedence.
The other property `ignoreBalancing` (default: true) is similar, in that it allows parentheses that help
reading and understanding the expressions.
* {% rule "java/bestpractices/LooseCoupling" %}: the rule has a new property to allow some types to be coupled to (`allowedTypes`).
* {% rule "java/errorprone/EmptyCatchBlock" %}: `CloneNotSupportedException` and `InterruptedException` are not special-cased anymore. Rename the exception parameter to `ignored` to ignore them.
* {% rule "java/codestyle/UseDiamondOperator" %}: the property `java7Compatibility` is removed. The rule now handles Java 7
properly without a property.
#### Removed Rules
@@ -151,6 +153,7 @@ The following previously deprecated rules have been finally removed:
* [#1790](https://github.com/pmd/pmd/issues/1790): \[java] UnnecessaryFullyQualifiedName false positive with enum constant
* [#1918](https://github.com/pmd/pmd/issues/1918): \[java] UselessParentheses false positive with boolean operators
* [#2299](https://github.com/pmd/pmd/issues/2299): \[java] UnnecessaryFullyQualifiedName false positive with similar package name
* [#2391](https://github.com/pmd/pmd/issues/2391): \[java] UseDiamondOperator FP when expected type and constructed type have a different parameterization
* [#2528](https://github.com/pmd/pmd/issues/2528): \[java] MethodNamingConventions - JUnit 5 method naming not support ParameterizedTest
* [#2739](https://github.com/pmd/pmd/issues/2739): \[java] UselessParentheses false positive for string concatenation
* [#2748](https://github.com/pmd/pmd/issues/2748): \[java] UnnecessaryCast false positive with unchecked cast
@@ -7,6 +7,7 @@ package net.sourceforge.pmd.internal.util;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ContextedRuntimeException;
import org.checkerframework.checker.nullness.qual.NonNull;
public final class AssertionUtil {
@@ -54,6 +55,14 @@ public final class AssertionUtil {
return "Invalid range [" + startInclusive + "," + endExclusive + "[ in [" + minIndex + "," + maxIndex + "[";
}
public static void validateState(boolean condition, String failed) {
if (!condition) {
throw new IllegalStateException(failed);
}
}
/**
* @throws IllegalArgumentException if [startInclusive,endExclusive[ is
* not a valid substring range for the given string
@@ -128,4 +137,14 @@ public final class AssertionUtil {
: prefix + ": " + message;
return new AssertionError(message);
}
public static @NonNull ContextedAssertionError contexted(AssertionError e) {
return ContextedAssertionError.wrap(e);
}
public static @NonNull ContextedRuntimeException contexted(RuntimeException e) {
return e instanceof ContextedRuntimeException ? (ContextedRuntimeException) e
: new ContextedRuntimeException(e);
}
}
@@ -0,0 +1,73 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.internal.util;
import java.util.List;
import java.util.Set;
import org.apache.commons.lang3.exception.DefaultExceptionContext;
import org.apache.commons.lang3.exception.ExceptionContext;
import org.apache.commons.lang3.tuple.Pair;
/**
* An {@link AssertionError} with nice messages.
*/
public final class ContextedAssertionError extends AssertionError implements ExceptionContext {
private final ExceptionContext exceptionContext = new DefaultExceptionContext();
private ContextedAssertionError(AssertionError e) {
super(e.getMessage());
setStackTrace(e.getStackTrace()); // pretend we're a regular assertion error
}
public static ContextedAssertionError wrap(AssertionError e) {
return e instanceof ContextedAssertionError ? (ContextedAssertionError) e
: new ContextedAssertionError(e);
}
@Override
public String getMessage() {
return getFormattedExceptionMessage(super.getMessage());
}
@Override
public ContextedAssertionError addContextValue(String label, Object value) {
exceptionContext.addContextValue(label, value);
return this;
}
@Override
public ContextedAssertionError setContextValue(String label, Object value) {
exceptionContext.addContextValue(label, value);
return this;
}
@Override
public List<Object> getContextValues(String label) {
return exceptionContext.getContextValues(label);
}
@Override
public Object getFirstContextValue(String label) {
return exceptionContext.getFirstContextValue(label);
}
@Override
public Set<String> getContextLabels() {
return exceptionContext.getContextLabels();
}
@Override
public List<Pair<String, Object>> getContextEntries() {
return exceptionContext.getContextEntries();
}
@Override
public String getFormattedExceptionMessage(String baseMessage) {
return exceptionContext.getFormattedExceptionMessage(baseMessage);
}
}
@@ -61,6 +61,24 @@ public class LanguageVersion implements Comparable<LanguageVersion> {
return version.length() > 0 ? language.getTerseName() + ' ' + version : language.getTerseName();
}
/**
* Compare this version to another version of the same language identified
* by the given version string.
*
* @param versionString The version with which to compare
*
* @throws IllegalArgumentException If the argument is not a valid version
* string for the parent language
*/
public int compareToVersion(String versionString) {
LanguageVersion otherVersion = language.getVersion(versionString);
if (otherVersion == null) {
throw new IllegalArgumentException(
"No such version '" + versionString + "' for language " + language.getName());
}
return this.compareTo(otherVersion);
}
@Override
public int compareTo(LanguageVersion o) {
if (o == null) {
@@ -17,6 +17,7 @@ import net.sourceforge.pmd.benchmark.TimeTracker;
import net.sourceforge.pmd.benchmark.TimedOperation;
import net.sourceforge.pmd.benchmark.TimedOperationCategory;
import net.sourceforge.pmd.internal.SystemProps;
import net.sourceforge.pmd.internal.util.AssertionUtil;
import net.sourceforge.pmd.lang.ast.Node;
/** Applies a set of rules to a set of ASTs. */
@@ -62,32 +63,38 @@ public class RuleApplicator {
rule.apply(node, ctx);
rcto.close(1);
} catch (RuntimeException e) {
if (ctx.isIgnoreExceptions()) {
ctx.getReport().addError(new ProcessingError(e, String.valueOf(ctx.getSourceCodeFile())));
if (LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Exception applying rule " + rule.getName() + " on file "
+ ctx.getSourceCodeFile() + ", continuing with next rule", e);
}
} else {
throw e;
}
} catch (StackOverflowError | AssertionError e) {
if (SystemProps.isErrorRecoveryMode()) {
ctx.getReport().addError(new ProcessingError(e, String.valueOf(ctx.getSourceCodeFile())));
if (LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Exception applying rule " + rule.getName() + " on file "
+ ctx.getSourceCodeFile() + ", continuing with next rule", e);
}
} else {
throw e;
}
reportOrRethrow(ctx, rule, node, e, ctx.isIgnoreExceptions());
} catch (AssertionError | StackOverflowError e) {
reportOrRethrow(ctx, rule, node, e, SystemProps.isErrorRecoveryMode());
}
}
}
}
private <E extends Throwable> void reportOrRethrow(RuleContext ctx, Rule rule, Node node, E e, boolean reportAndDontThrow) throws E {
if (reportAndDontThrow) {
reportException(ctx, rule, node, e);
} else {
if (e instanceof RuntimeException) {
throw AssertionUtil.contexted((RuntimeException) e).addContextValue("Rule applied on node", node);
} else if (e instanceof AssertionError) {
throw AssertionUtil.contexted((AssertionError) e).addContextValue("Rule applied on node", node);
} else {
throw e;
}
}
}
private void reportException(RuleContext ctx, Rule rule, Node node, Throwable e) {
ctx.getReport().addError(new ProcessingError(e, String.valueOf(ctx.getSourceCodeFile())));
if (LOG.isLoggable(Level.WARNING)) {
LOG.log(Level.WARNING, "Exception applying rule " + rule.getName() + " on file "
+ ctx.getSourceCodeFile() + ", continuing with next rule", e);
LOG.log(Level.WARNING, "Exception occurred on node " + node, e);
}
}
private void indexTree(Node top, TreeIndex idx) {
idx.indexNode(top);
for (Node child : top.children()) {
@@ -17,6 +17,7 @@ import net.sourceforge.pmd.lang.ast.impl.GenericNode;
import net.sourceforge.pmd.lang.java.symbols.table.JSymbolTable;
import net.sourceforge.pmd.lang.java.typeresolution.ClassTypeResolver;
import net.sourceforge.pmd.lang.java.types.TypeSystem;
import net.sourceforge.pmd.lang.java.types.ast.LazyTypeResolver;
// FUTURE Change this class to extend from SimpleJavaNode, as TypeNode is not appropriate (unless I'm wrong)
public final class ASTCompilationUnit extends AbstractJavaTypeNode implements JavaNode, GenericNode<JavaNode>, RootNode {
@@ -55,7 +55,15 @@ public final class ASTConditionalExpression extends AbstractJavaExpr {
return visitor.visit(this, data);
}
/**
* Note: this method is not used at all in pre-Java 8 analysis,
* because standalone/poly exprs weren't formalized before java 8.
* Calling this method then is undefined.
*/
// very internal
boolean isStandalone() {
assert getAstInfo().getLanguageVersion().compareToVersion("8") >= 0
: "This method's result is undefined in pre java 8 code";
return this.isStandalone;
}
@@ -8,6 +8,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.annotation.Experimental;
import net.sourceforge.pmd.lang.java.types.ast.ExprContext;
/**
* Represents an expression, in the most general sense.
@@ -8,6 +8,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.lang.java.ast.InternalInterfaces.VariableIdOwner;
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
import net.sourceforge.pmd.lang.java.types.TypingContext;
/**
@@ -68,8 +69,7 @@ public final class ASTFormalParameter extends AbstractJavaNode
* Returns the declarator ID of this formal parameter.
*/
@Override
@NonNull
public ASTVariableDeclaratorId getVarId() {
public @NonNull ASTVariableDeclaratorId getVarId() {
return firstChild(ASTVariableDeclaratorId.class);
}
@@ -87,7 +87,7 @@ public final class ASTFormalParameter extends AbstractJavaNode
// The node that represents the variable is the variable ID.
@Override
public @NonNull JTypeMirror getTypeMirror() {
return getVarId().getTypeMirror();
public @NonNull JTypeMirror getTypeMirror(TypingContext ctx) {
return getVarId().getTypeMirror(ctx);
}
}
@@ -22,7 +22,7 @@ import net.sourceforge.pmd.lang.java.types.JTypeMirror;
*
* </pre>
*/
public final class ASTLambdaExpression extends AbstractJavaExpr {
public final class ASTLambdaExpression extends AbstractJavaExpr implements FunctionalExpression {
private JMethodSig functionalMethod;
@@ -50,6 +50,7 @@ public final class ASTLambdaExpression extends AbstractJavaExpr {
*
* @see #getTypeMirror()
*/
@Override
public JMethodSig getFunctionalMethod() {
forceTypeResolution();
return assertNonNullAfterTypeRes(functionalMethod);
@@ -26,7 +26,8 @@ public final class ASTMethodReference extends AbstractJavaExpr
implements ASTPrimaryExpression,
QualifiableExpression,
LeftRecursiveNode,
MethodUsage {
MethodUsage,
FunctionalExpression {
private JMethodSig functionalMethod;
private JMethodSig compileTimeDecl;
@@ -131,6 +132,7 @@ public final class ASTMethodReference extends AbstractJavaExpr
* @see #getReferencedMethod()
* @see #getTypeMirror()
*/
@Override
public JMethodSig getFunctionalMethod() {
forceTypeResolution();
return assertNonNullAfterTypeRes(functionalMethod);
@@ -8,6 +8,7 @@ import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.lang.java.ast.InternalInterfaces.AtLeastOneChild;
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
import net.sourceforge.pmd.lang.java.types.TypingContext;
/**
* Wraps a type node but presents the interface of {@link ASTExpression}.
@@ -63,8 +64,8 @@ public final class ASTTypeExpression extends AbstractJavaNode implements ASTPrim
}
@Override
public @NonNull JTypeMirror getTypeMirror() {
return getTypeNode().getTypeMirror();
public @NonNull JTypeMirror getTypeMirror(TypingContext ctx) {
return getTypeNode().getTypeMirror(ctx);
}
}
@@ -48,16 +48,14 @@ abstract class AbstractAnyTypeDeclaration extends AbstractTypedSymbolDeclarator<
return super.getImage();
}
@NonNull
@Override
public String getBinaryName() {
public @NonNull String getBinaryName() {
assert binaryName != null : "Null binary name";
return binaryName;
}
@Nullable
@Override
public String getCanonicalName() {
public @Nullable String getCanonicalName() {
assert binaryName != null : "Canonical name wasn't set";
return canonicalName;
}
@@ -73,9 +71,8 @@ abstract class AbstractAnyTypeDeclaration extends AbstractTypedSymbolDeclarator<
this.canonicalName = canon;
}
@NonNull
@Override
public JClassType getTypeMirror() {
public @NonNull JClassType getTypeMirror() {
return (JClassType) super.getTypeMirror();
}
}
@@ -4,10 +4,12 @@
package net.sourceforge.pmd.lang.java.ast;
import org.apache.commons.lang3.exception.ContextedRuntimeException;
import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.internal.util.AssertionUtil;
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
import net.sourceforge.pmd.lang.java.types.TypingContext;
import net.sourceforge.pmd.lang.java.types.ast.LazyTypeResolver;
/**
* An extension of the SimpleJavaNode which implements the TypeNode interface.
@@ -34,22 +36,30 @@ abstract class AbstractJavaTypeNode extends AbstractJavaNode implements TypeNode
@Override
public @NonNull JTypeMirror getTypeMirror() {
if (typeMirror == null) {
try {
LazyTypeResolver resolver = getRoot().getLazyTypeResolver();
typeMirror = this.acceptVisitor(resolver, null);
assert typeMirror != null : "LazyTypeResolver returned null";
} catch (Exception | AssertionError e) {
// this will add every type in the chain
throw addContextValue(e, "Resolving type of", this);
}
}
return typeMirror;
return getTypeMirror(TypingContext.DEFAULT);
}
private static ContextedRuntimeException addContextValue(Throwable e, String label, Object value) {
return e instanceof ContextedRuntimeException ? ((ContextedRuntimeException) e).addContextValue(label, value)
: new ContextedRuntimeException(e).addContextValue(label, value);
@Override
public @NonNull JTypeMirror getTypeMirror(TypingContext context) {
if (context.isEmpty() && typeMirror != null) {
return typeMirror;
}
LazyTypeResolver resolver = getRoot().getLazyTypeResolver();
JTypeMirror result;
try {
result = this.acceptVisitor(resolver, context);
assert result != null : "LazyTypeResolver returned null";
} catch (RuntimeException e) {
throw AssertionUtil.contexted(e).addContextValue("Resolving type of", this);
} catch (AssertionError e) {
throw AssertionUtil.contexted(e).addContextValue("Resolving type of", this);
}
if (context.isEmpty() && typeMirror == null) {
typeMirror = result; // cache it
}
return result;
}
JTypeMirror getTypeMirrorInternal() {
@@ -27,6 +27,7 @@ import net.sourceforge.pmd.lang.java.types.JClassType;
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
import net.sourceforge.pmd.lang.java.types.JVariableSig;
import net.sourceforge.pmd.lang.java.types.JVariableSig.FieldSig;
import net.sourceforge.pmd.lang.java.types.ast.LazyTypeResolver;
/**
* This implements name disambiguation following <a href="https://docs.oracle.com/javase/specs/jls/se8/html/jls-6.html#jls-6.5.2">JLS§6.5.2</a>.
@@ -0,0 +1,38 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.ast;
import org.checkerframework.checker.nullness.qual.NonNull;
import net.sourceforge.pmd.lang.java.types.JMethodSig;
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
/**
* A method reference or lambda expression.
*/
public interface FunctionalExpression extends ASTExpression {
/**
* Returns the type of the functional interface.
* E.g. in {@code stringStream.map(s -> s.isEmpty())}, this is
* {@code java.util.function.Function<java.lang.String, java.lang.Boolean>}.
*
* @see #getFunctionalMethod()
*/
@Override
@NonNull JTypeMirror getTypeMirror();
/**
* Returns the method that is overridden in the functional interface.
* E.g. in {@code stringStream.map(s -> s.isEmpty())}, this is
* {@code java.util.function.Function#apply(java.lang.String) ->
* java.lang.Boolean}
*
* @see #getTypeMirror()
*/
JMethodSig getFunctionalMethod();
}
@@ -4,9 +4,11 @@
package net.sourceforge.pmd.lang.java.ast;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.annotation.InternalApi;
import net.sourceforge.pmd.internal.util.AssertionUtil;
import net.sourceforge.pmd.lang.ast.NodeStream;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccToken;
import net.sourceforge.pmd.lang.ast.impl.javacc.JavaccTokenDocument;
@@ -26,6 +28,11 @@ import net.sourceforge.pmd.lang.java.types.JTypeMirror;
import net.sourceforge.pmd.lang.java.types.JVariableSig;
import net.sourceforge.pmd.lang.java.types.JVariableSig.FieldSig;
import net.sourceforge.pmd.lang.java.types.OverloadSelectionResult;
import net.sourceforge.pmd.lang.java.types.Substitution;
import net.sourceforge.pmd.lang.java.types.TypeSystem;
import net.sourceforge.pmd.lang.java.types.ast.ExprContext;
import net.sourceforge.pmd.lang.java.types.ast.LazyTypeResolver;
import net.sourceforge.pmd.lang.java.types.internal.infer.Infer;
import net.sourceforge.pmd.lang.java.types.internal.infer.TypeInferenceLogger;
import net.sourceforge.pmd.lang.symboltable.Scope;
@@ -153,12 +160,14 @@ public final class InternalApiBridge {
node.setTypedSym(sig);
}
public static void setFunctionalMethod(ASTMethodReference methodReference, JMethodSig methodType) {
methodReference.setFunctionalMethod(methodType);
}
public static void setFunctionalMethod(ASTLambdaExpression lambda, @Nullable JMethodSig methodType) {
lambda.setFunctionalMethod(methodType);
public static void setFunctionalMethod(FunctionalExpression node, JMethodSig methodType) {
if (node instanceof ASTMethodReference) {
((ASTMethodReference) node).setFunctionalMethod(methodType);
} else if (node instanceof ASTLambdaExpression) {
((ASTLambdaExpression) node).setFunctionalMethod(methodType);
} else {
throw AssertionUtil.shouldNotReachHere("" + node);
}
}
public static void setCompileTimeDecl(ASTMethodReference methodReference, JMethodSig methodType) {
@@ -185,6 +194,18 @@ public final class InternalApiBridge {
return n.getRoot().getLazyTypeResolver().getProcessor();
}
public static Infer getInferenceEntryPoint(JavaNode n) {
return n.getRoot().getLazyTypeResolver().getInfer();
}
public static @NonNull LazyTypeResolver getLazyTypeResolver(JavaNode n) {
return n.getRoot().getLazyTypeResolver();
}
public static @NonNull ExprContext getTopLevelExprContext(TypeNode n) {
return n.getRoot().getLazyTypeResolver().getTopLevelContextIncludingInvocation(n);
}
public static void setSymbolTable(JavaNode node, JSymbolTable table) {
((AbstractJavaNode) node).setSymbolTable(table);
}
@@ -208,4 +229,20 @@ public final class InternalApiBridge {
public static void setStandaloneTernary(ASTConditionalExpression node) {
node.setStandaloneTernary();
}
public static boolean isStandaloneInternal(ASTConditionalExpression node) {
return node.isStandalone();
}
public static JTypeMirror buildTypeFromAstInternal(TypeSystem ts, Substitution lexicalSubst, ASTType node) {
return TypesFromAst.fromAst(ts, lexicalSubst, node);
}
public static void setTypedSym(ASTFieldAccess expr, JVariableSig.FieldSig sym) {
expr.setTypedSym(sym);
}
public static void setTypedSym(ASTVariableAccess expr, JVariableSig sym) {
expr.setTypedSym(sym);
}
}
@@ -10,6 +10,7 @@ import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.annotation.DeprecatedUntil700;
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
import net.sourceforge.pmd.lang.java.types.TypeSystem;
import net.sourceforge.pmd.lang.java.types.TypingContext;
/**
* A node that has a statically known type. This includes e.g.
@@ -34,10 +35,14 @@ public interface TypeNode extends JavaNode {
* API will be added to expose this information.
*
* @return The type mirror. Never returns null; if the type is unresolved, returns
* {@link TypeSystem#UNKNOWN}.
* {@link TypeSystem#UNKNOWN}.
*/
@NonNull
JTypeMirror getTypeMirror();
default JTypeMirror getTypeMirror() {
return getTypeMirror(TypingContext.DEFAULT);
}
JTypeMirror getTypeMirror(TypingContext typing);
/**
@@ -49,7 +49,7 @@ final class TypesFromAst {
return fromAstImpl(ts, lexicalSubst, node);
}
public static JTypeMirror fromAstImpl(TypeSystem ts, Substitution lexicalSubst, ASTType node) {
private static JTypeMirror fromAstImpl(TypeSystem ts, Substitution lexicalSubst, ASTType node) {
if (node instanceof ASTClassOrInterfaceType) {
@@ -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(),
@@ -28,7 +28,6 @@ import net.sourceforge.pmd.lang.java.ast.ASTInfixExpression;
import net.sourceforge.pmd.lang.java.ast.ASTLambdaExpression;
import net.sourceforge.pmd.lang.java.ast.ASTMethodReference;
import net.sourceforge.pmd.lang.java.ast.BinaryOp;
import net.sourceforge.pmd.lang.java.ast.ExprContext;
import net.sourceforge.pmd.lang.java.ast.internal.PrettyPrintingUtil;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
import net.sourceforge.pmd.lang.java.rule.internal.JavaRuleUtil;
@@ -36,6 +35,8 @@ import net.sourceforge.pmd.lang.java.types.JTypeMirror;
import net.sourceforge.pmd.lang.java.types.TypeConversion;
import net.sourceforge.pmd.lang.java.types.TypeOps;
import net.sourceforge.pmd.lang.java.types.TypeTestUtil;
import net.sourceforge.pmd.lang.java.types.ast.ExprContext;
import net.sourceforge.pmd.lang.java.types.ast.ExprContext.ExprContextKind;
/**
* Detects casts where the operand is already a subtype of the context
@@ -72,7 +73,7 @@ public class UnnecessaryCastRule extends AbstractJavaRulechainRule {
if (operand instanceof ASTLambdaExpression || operand instanceof ASTMethodReference) {
// Then the cast provides a target type for the expression (always).
// We need to check the enclosing context, as if it's invocation we give up for now
if (context.isMissing() || context.isInvocationContext()) {
if (context.isMissing() || context.hasKind(ExprContextKind.INVOCATION)) {
// Then the cast may be used to determine the overload.
// We need to treat the casted lambda as a whole unit.
// todo see below
@@ -119,7 +120,7 @@ public class UnnecessaryCastRule extends AbstractJavaRulechainRule {
private static boolean castIsUnnecessaryToMatchContext(ExprContext context,
JTypeMirror coercionType,
JTypeMirror operandType) {
if (context.isInvocationContext()) {
if (context.hasKind(ExprContextKind.INVOCATION)) {
// todo unsupported for now, the cast may be disambiguating overloads
return false;
}
@@ -152,13 +153,13 @@ public class UnnecessaryCastRule extends AbstractJavaRulechainRule {
// a branch of a ternary
return true;
} else if (context.isString() && isInfixExprWithOperator(castExpr.getParent(), ADD)) {
} else if (context.hasKind(ExprContextKind.STRING) && isInfixExprWithOperator(castExpr.getParent(), ADD)) {
// inside string concatenation
return !TypeTestUtil.isA(String.class, JavaRuleUtil.getOtherOperandIfInInfixExpr(castExpr))
&& !TypeTestUtil.isA(String.class, operandType);
} else if (context.isNumeric() && castExpr.getParent() instanceof ASTInfixExpression) {
} else if (context.hasKind(ExprContextKind.NUMERIC) && castExpr.getParent() instanceof ASTInfixExpression) {
// numeric expr
ASTInfixExpression parent = (ASTInfixExpression) castExpr.getParent();
@@ -0,0 +1,286 @@
/*
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
*/
package net.sourceforge.pmd.lang.java.rule.codestyle;
import java.util.List;
import java.util.Objects;
import org.apache.commons.lang3.StringUtils;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;
import net.sourceforge.pmd.lang.java.ast.ASTArgumentList;
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceType;
import net.sourceforge.pmd.lang.java.ast.ASTConstructorCall;
import net.sourceforge.pmd.lang.java.ast.ASTExpression;
import net.sourceforge.pmd.lang.java.ast.ASTTypeArguments;
import net.sourceforge.pmd.lang.java.ast.InternalApiBridge;
import net.sourceforge.pmd.lang.java.ast.InvocationNode;
import net.sourceforge.pmd.lang.java.ast.JavaNode;
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
import net.sourceforge.pmd.lang.java.types.JClassType;
import net.sourceforge.pmd.lang.java.types.JMethodSig;
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
import net.sourceforge.pmd.lang.java.types.TypeOps;
import net.sourceforge.pmd.lang.java.types.TypingContext;
import net.sourceforge.pmd.lang.java.types.ast.ExprContext;
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror;
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.CtorInvocationMirror;
import net.sourceforge.pmd.lang.java.types.internal.infer.ExprMirror.InvocationMirror;
import net.sourceforge.pmd.lang.java.types.internal.infer.Infer;
import net.sourceforge.pmd.lang.java.types.internal.infer.MethodCallSite;
import net.sourceforge.pmd.lang.java.types.internal.infer.ast.JavaExprMirrors;
/**
* Checks usages of explicity type arguments in a constructor call that
* may be replaced by a diamond ({@code <>}). In order to determine this,
* we mock a type resolution call site, which is equivalent to the expression
* as if it had a diamond instead of explicit type arguments. We then perform
* overload resolution for this fake call site. If overload resolution fails,
* resolves to another overload, or if the inferred type is not compatible
* with the expected context type, then the type arguments are unnecessary,
* and removing them will not break the program.
*
* <p>Note that type inference in Java 8+ works differently from Java 7.
* In Java 7, type arguments may be necessary in more places. The specifics
* are however implemented within the type resolution code, and this rule does
* not need to know about it.
*/
public class UseDiamondOperatorRule extends AbstractJavaRulechainRule {
private static final String REPLACE_TYPE_ARGS_MESSAGE = "Explicit type arguments can be replaced by a diamond: `{0}`";
private static final String RAW_TYPE_MESSAGE = "Raw type use may be avoided by using a diamond: `{0}`";
/**
* Maximum length of the argument list (including parentheses) for
* it to be included in the violation message instead of an ellipsis {@code (...)}.
*/
private static final int MAX_ARGS_LENGTH = 25;
public UseDiamondOperatorRule() {
super(ASTConstructorCall.class);
}
@Override
public Object visit(ASTConstructorCall ctorCall, Object data) {
ASTClassOrInterfaceType newTypeNode = ctorCall.getTypeNode();
JTypeMirror newType = newTypeNode.getTypeMirror();
ASTTypeArguments targs = newTypeNode.getTypeArguments();
if (targs != null && targs.isDiamond()
// if unresolved we can't know whether the class is generic or not
|| TypeOps.isUnresolved(newType)) {
return null;
}
if (!newType.isGeneric() // targs may be null, in which case this would be a raw type
|| ctorCall.isAnonymousClass() && !supportsDiamondOnAnonymousClass(ctorCall)) {
return null;
}
if (inferenceSucceedsWithoutTypeArgs(ctorCall)) {
// report it
JavaNode reportNode = targs == null ? newTypeNode : targs;
String message = targs == null ? RAW_TYPE_MESSAGE : REPLACE_TYPE_ARGS_MESSAGE;
String replaceWith = produceSuggestedExprImage(ctorCall);
addViolationWithMessage(data, reportNode, message, new String[] { replaceWith });
}
return null;
}
private static boolean supportsDiamondOnAnonymousClass(ASTConstructorCall ctorCall) {
return ctorCall.getAstInfo().getLanguageVersion().compareToVersion("9") >= 0;
}
/** Redo inference as described in the javadoc of this class. */
private static boolean inferenceSucceedsWithoutTypeArgs(ASTConstructorCall call) {
ExprContext context = call.getConversionContext();
if (context.isMissing()) {
return false;
}
Infer infer = InternalApiBridge.getInferenceEntryPoint(call);
// this may not mutate the AST
JavaExprMirrors factory = JavaExprMirrors.forObservation(infer);
InvocationNode invocContext = InternalApiBridge.getTopLevelExprContext(call).getInvocNodeIfInvocContext();
ExprContext topmostContext;
InvocationMirror mirror;
if (invocContext == null) {
CtorInvocationMirror defaultMirror = (CtorInvocationMirror) factory.getTopLevelInvocationMirror(call);
mirror = new SpyInvocMirror(defaultMirror);
topmostContext = call.getConversionContext();
} else {
mirror = factory.getInvocationMirror(invocContext, (e, parent, self) -> {
ExprMirror defaultImpl = factory.defaultMirrorMaker().createMirrorForSubexpression(e, parent, self);
if (e == call) {
return new SpyInvocMirror((CtorInvocationMirror) defaultImpl);
} else {
return defaultImpl;
}
});
if (invocContext instanceof ASTExpression) {
topmostContext = ((ASTExpression) invocContext).getConversionContext();
} else {
topmostContext = ExprContext.getMissingInstance();
}
}
JTypeMirror targetType = topmostContext.getPolyTargetType(false);
MethodCallSite fakeCallSite = infer.newCallSite(mirror, targetType);
infer.inferInvocationRecursively(fakeCallSite);
return mirror.isEquivalentToUnderlyingAst()
&& topmostContext.acceptsType(mirror.getInferredType());
}
private static String produceSuggestedExprImage(ASTConstructorCall ctor) {
StringBuilder sb = new StringBuilder(30);
sb.append("new ");
produceSameTypeWithDiamond(ctor.getTypeNode(), sb, true);
ASTArgumentList arguments = ctor.getArguments();
String argsString;
if (arguments.size() == 0) {
argsString = "()";
} else {
CharSequence text = arguments.getText();
if (text.length() <= MAX_ARGS_LENGTH && !StringUtils.contains(text, '\n')) {
argsString = text.toString();
} else {
argsString = "(...)";
}
}
return sb.append(argsString).toString();
}
private static StringBuilder produceSameTypeWithDiamond(ASTClassOrInterfaceType type, StringBuilder sb, boolean topLevel) {
if (type.isFullyQualified()) {
JTypeDeclSymbol sym = type.getTypeMirror().getSymbol();
Objects.requireNonNull(sym);
sb.append(sym.getPackageName()).append('.');
} else {
ASTClassOrInterfaceType qualifier = type.getQualifier();
if (qualifier != null) {
produceSameTypeWithDiamond(qualifier, sb, false).append('.');
}
}
sb.append(type.getSimpleName());
return topLevel ? sb.append("<>") : sb;
}
/** Proxy that pretends it has diamond type args. */
private static final class SpyInvocMirror implements CtorInvocationMirror {
private final CtorInvocationMirror base;
SpyInvocMirror(CtorInvocationMirror base) {
this.base = base;
}
// overridden methods
@Override
public @NonNull JTypeMirror getNewType() {
// see doc of CtorInvocationMirror#getNewType
return ((JClassType) base.getNewType()).getGenericTypeDeclaration();
}
@Override
public boolean isDiamond() {
return true; // pretend it is
}
// delegated methods
@Override
public List<JTypeMirror> getExplicitTypeArguments() {
return base.getExplicitTypeArguments();
}
@Override
public JavaNode getExplicitTargLoc(int i) {
return base.getExplicitTargLoc(i);
}
@Override
public void setInferredType(JTypeMirror mirror) {
base.setInferredType(mirror);
}
@Override
public JTypeMirror getInferredType() {
return base.getInferredType();
}
@Override
public void setCtDecl(MethodCtDecl methodType) {
base.setCtDecl(methodType);
}
@Override
public @Nullable MethodCtDecl getCtDecl() {
return base.getCtDecl();
}
@Override
public JavaNode getLocation() {
return base.getLocation();
}
@Override
public @NonNull JClassType getEnclosingType() {
return base.getEnclosingType();
}
@Override
public boolean isAnonymous() {
return base.isAnonymous();
}
@Override
public Iterable<JMethodSig> getAccessibleCandidates(JTypeMirror newType) {
return base.getAccessibleCandidates(newType);
}
@Override
public @Nullable JTypeMirror getReceiverType() {
return base.getReceiverType();
}
@Override
public String getName() {
return base.getName();
}
@Override
public List<ExprMirror> getArgumentExpressions() {
return base.getArgumentExpressions();
}
@Override
public int getArgumentCount() {
return base.getArgumentCount();
}
@Override
public String toString() {
return base.toString();
}
@Override
public TypingContext getTypingContext() {
return base.getTypingContext();
}
@Override
public boolean isEquivalentToUnderlyingAst() {
return base.isEquivalentToUnderlyingAst();
}
}
}
@@ -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");
}
/*
@@ -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);
}
@@ -91,12 +91,16 @@ public final class TypeConversion {
/**
* Is t convertible to s by boxing/unboxing/widening conversion?
* Only t can be undergo conversion.
* Only t can undergo conversion.
*/
public static boolean isConvertibleUsingBoxing(JTypeMirror t, JTypeMirror s) {
return isConvertibleCommon(t, s, false);
}
/**
* Is t convertible to s by boxing/unboxing conversion?
* Only t can undergo conversion.
*/
public static boolean isConvertibleInCastContext(JTypeMirror t, JTypeMirror s) {
return isConvertibleCommon(t, s, true);
}
@@ -159,8 +163,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 +178,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 +196,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();
}
Loaded 30 of 80 files, more files were not shown because too many files have changed in this diff. Show more