Merge branch 'clem.type-annots-in-infer' into 7.0.x
This commit is contained in:
113 files changed
+3454
-1158
No files matched your search
+1
-1
@@ -134,7 +134,7 @@ abstract class IteratorBasedNStream<T extends Node> implements NodeStream<T> {
|
||||
}
|
||||
|
||||
@Override
|
||||
public <R, A> R collect(Collector<? super T, A, R> collector) {
|
||||
public final <R, A> R collect(Collector<? super T, A, R> collector) {
|
||||
A container = collector.supplier().get();
|
||||
BiConsumer<A, ? super T> accumulator = collector.accumulator();
|
||||
forEach(u -> accumulator.accept(container, u));
|
||||
|
||||
@@ -6,10 +6,8 @@ package net.sourceforge.pmd.util;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyIterator;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Collections.emptyMap;
|
||||
import static java.util.Collections.emptySet;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
@@ -36,8 +34,10 @@ import java.util.stream.Collectors;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.pcollections.ConsPStack;
|
||||
import org.pcollections.HashTreePSet;
|
||||
import org.pcollections.PMap;
|
||||
import org.pcollections.PSequence;
|
||||
import org.pcollections.PSet;
|
||||
|
||||
import net.sourceforge.pmd.annotation.InternalApi;
|
||||
@@ -159,9 +159,9 @@ public final class CollectionUtil {
|
||||
*/
|
||||
public static <T> List<T> concatView(List<? extends T> head, List<? extends T> tail) {
|
||||
if (head.isEmpty()) {
|
||||
return Collections.unmodifiableList(tail);
|
||||
return makeUnmodifiableAndNonNull(tail);
|
||||
} else if (tail.isEmpty()) {
|
||||
return Collections.unmodifiableList(head);
|
||||
return makeUnmodifiableAndNonNull(head);
|
||||
} else {
|
||||
return new ConsList<>(head, tail);
|
||||
}
|
||||
@@ -268,7 +268,7 @@ public final class CollectionUtil {
|
||||
@SafeVarargs
|
||||
public static <T> List<T> listOf(T first, T... rest) {
|
||||
if (rest.length == 0) {
|
||||
return Collections.singletonList(first);
|
||||
return ConsPStack.singleton(first);
|
||||
}
|
||||
List<T> union = new ArrayList<>();
|
||||
union.add(first);
|
||||
@@ -323,6 +323,26 @@ public final class CollectionUtil {
|
||||
return newM;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a new list with the elements of the first, and one additional
|
||||
* item. The returned list is immutable.
|
||||
*/
|
||||
public static <V> List<V> plus(List<V> list, V v) {
|
||||
if (list instanceof PSequence) {
|
||||
return ((PSequence<V>) list).plus(v);
|
||||
} else if (list.isEmpty()) {
|
||||
return ConsPStack.singleton(v);
|
||||
}
|
||||
return ConsPStack.from(list).plus(v);
|
||||
}
|
||||
|
||||
/** Returns the empty list. */
|
||||
public static <V> List<V> emptyList() {
|
||||
// We use this implementation so that it plays well with other
|
||||
// operations that expect immutable data.
|
||||
return ConsPStack.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an unmodifiable set containing the set union of the collection,
|
||||
* and the new elements.
|
||||
@@ -461,7 +481,7 @@ public final class CollectionUtil {
|
||||
if (!from.hasNext()) {
|
||||
return emptyList();
|
||||
} else if (sizeHint == 1) {
|
||||
return Collections.singletonList(f.apply(from.next()));
|
||||
return ConsPStack.singleton(f.apply(from.next()));
|
||||
}
|
||||
List<R> res = sizeHint == UNKNOWN_SIZE ? new ArrayList<>() : new ArrayList<>(sizeHint);
|
||||
while (from.hasNext()) {
|
||||
@@ -592,7 +612,7 @@ public final class CollectionUtil {
|
||||
|
||||
|
||||
public static <T> List<T> listOfNotNull(T t) {
|
||||
return t == null ? emptyList() : singletonList(t);
|
||||
return t == null ? emptyList() : ConsPStack.singleton(t);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -641,10 +661,15 @@ public final class CollectionUtil {
|
||||
* @param <T> Type of items
|
||||
*/
|
||||
public static <T> List<T> defensiveUnmodifiableCopy(List<? extends T> list) {
|
||||
if (list.isEmpty()) {
|
||||
return emptyList();
|
||||
if (list instanceof PSequence) {
|
||||
return (List<T>) list; // is already immutable
|
||||
}
|
||||
return Collections.unmodifiableList(new ArrayList<>(list));
|
||||
if (list.isEmpty()) {
|
||||
return ConsPStack.empty();
|
||||
} else if (list.size() == 1) {
|
||||
return ConsPStack.singleton(list.get(0));
|
||||
}
|
||||
return ConsPStack.from(list);
|
||||
}
|
||||
|
||||
public static <T> Set<T> defensiveUnmodifiableCopyToSet(Collection<? extends T> list) {
|
||||
@@ -696,6 +721,9 @@ public final class CollectionUtil {
|
||||
}
|
||||
|
||||
public static @NonNull <T> List<T> makeUnmodifiableAndNonNull(@Nullable List<? extends T> list) {
|
||||
if (list instanceof PSequence) {
|
||||
return (List<T>) list;
|
||||
}
|
||||
return list == null || list.isEmpty() ? emptyList()
|
||||
: Collections.unmodifiableList(list);
|
||||
}
|
||||
|
||||
@@ -1578,7 +1578,7 @@ void TypeArgument() #void:
|
||||
void WildcardType():
|
||||
{}
|
||||
{
|
||||
"?" [ ("extends" {jjtThis.setUpperBound(true);}| "super") AnnotatedRefType() ]
|
||||
"?" [ ("extends" | "super" {jjtThis.setLowerBound(true);}) AnnotatedRefType() ]
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ public final class ASTArrayType extends AbstractJavaTypeNode implements ASTRefer
|
||||
|
||||
@Override
|
||||
public NodeStream<ASTAnnotation> getDeclaredAnnotations() {
|
||||
return getDimensions().getLastChild().getDeclaredAnnotations();
|
||||
return getDimensions().getFirstChild().getDeclaredAnnotations();
|
||||
}
|
||||
|
||||
public ASTArrayDimensions getDimensions() {
|
||||
|
||||
@@ -18,14 +18,14 @@ import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
*/
|
||||
public final class ASTWildcardType extends AbstractJavaTypeNode implements ASTReferenceType {
|
||||
|
||||
private boolean isUpperBound;
|
||||
private boolean isLowerBound;
|
||||
|
||||
ASTWildcardType(int id) {
|
||||
super(id);
|
||||
}
|
||||
|
||||
void setUpperBound(boolean upperBound) {
|
||||
isUpperBound = upperBound;
|
||||
void setLowerBound(boolean lowerBound) {
|
||||
isLowerBound = lowerBound;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -34,11 +34,12 @@ public final class ASTWildcardType extends AbstractJavaTypeNode implements ASTRe
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if this is an upper type bound, e.g.
|
||||
* in {@code <? extends Integer>}.
|
||||
* Return true if this is an upper type bound, e.g.
|
||||
* {@code <? extends Integer>}, or the unbounded
|
||||
* wildcard {@code <?>}.
|
||||
*/
|
||||
public boolean hasUpperBound() {
|
||||
return isUpperBound && getNumChildren() > 0;
|
||||
public boolean isUpperBound() {
|
||||
return !isLowerBound;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,18 +47,18 @@ public final class ASTWildcardType extends AbstractJavaTypeNode implements ASTRe
|
||||
* Returns true if this is a lower type bound, e.g.
|
||||
* in {@code <? super Node>}.
|
||||
*/
|
||||
public boolean hasLowerBound() {
|
||||
return !isUpperBound && getNumChildren() > 0;
|
||||
public boolean isLowerBound() {
|
||||
return isLowerBound;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the type node representing the bound, e.g.
|
||||
* the {@code Node} in {@code <? super Node>}, or null.
|
||||
* the {@code Node} in {@code <? super Node>}, or null in
|
||||
* the unbounded wildcard {@code <?>}.
|
||||
*/
|
||||
@Nullable
|
||||
public ASTReferenceType getTypeBoundNode() {
|
||||
return getFirstChildOfType(ASTReferenceType.class);
|
||||
public @Nullable ASTReferenceType getTypeBoundNode() {
|
||||
return firstChild(ASTReferenceType.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -33,8 +33,7 @@ public interface Annotatable extends JavaNode {
|
||||
* Returns true if an annotation with the given qualified name is
|
||||
* applied to this node.
|
||||
*
|
||||
* @param annotQualifiedName
|
||||
* Note: for now, canonical names are tolerated, this may be changed in PMD 7.
|
||||
* @param annotQualifiedName Note: for now, canonical names are tolerated, this may be changed in PMD 7.
|
||||
*/
|
||||
default boolean isAnnotationPresent(String annotQualifiedName) {
|
||||
return getDeclaredAnnotations().any(t -> TypeTestUtil.isA(StringUtils.deleteWhitespace(annotQualifiedName), t));
|
||||
|
||||
@@ -17,6 +17,7 @@ import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JElementSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JVariableSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.table.JSymbolTable;
|
||||
@@ -207,6 +208,10 @@ public final class InternalApiBridge {
|
||||
return TypesFromAst.fromAst(ts, lexicalSubst, node);
|
||||
}
|
||||
|
||||
public static JTypeDeclSymbol getReferencedSym(ASTClassOrInterfaceType type) {
|
||||
return type.getReferencedSym();
|
||||
}
|
||||
|
||||
public static void setTypedSym(ASTFieldAccess expr, JVariableSig.FieldSig sym) {
|
||||
expr.setTypedSym(sym);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.ast;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
@@ -11,10 +12,13 @@ import java.util.List;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.pcollections.PSet;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.ast.SymbolResolutionPass;
|
||||
import net.sourceforge.pmd.lang.java.symbols.table.internal.JavaResolvers;
|
||||
import net.sourceforge.pmd.lang.java.types.JClassType;
|
||||
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
|
||||
@@ -60,9 +64,10 @@ final class TypesFromAst {
|
||||
|
||||
ASTWildcardType wild = (ASTWildcardType) node;
|
||||
@Nullable JTypeMirror bound = fromAst(ts, lexicalSubst, wild.getTypeBoundNode());
|
||||
return bound == null
|
||||
? ts.UNBOUNDED_WILD
|
||||
: ts.wildcard(wild.hasUpperBound(), bound);
|
||||
if (bound == null) {
|
||||
bound = ts.OBJECT;
|
||||
}
|
||||
return ts.wildcard(wild.isUpperBound(), bound).withAnnotations(getTypeAnnotations(node));
|
||||
|
||||
|
||||
} else if (node instanceof ASTIntersectionType) {
|
||||
@@ -79,13 +84,20 @@ final class TypesFromAst {
|
||||
}
|
||||
} else if (node instanceof ASTArrayType) {
|
||||
|
||||
JTypeMirror eltType = fromAst(ts, lexicalSubst, ((ASTArrayType) node).getElementType());
|
||||
JTypeMirror t = fromAst(ts, lexicalSubst, ((ASTArrayType) node).getElementType());
|
||||
ASTArrayDimensions dimensions = ((ASTArrayType) node).getDimensions();
|
||||
// we have to iterate in reverse
|
||||
for (int i = dimensions.size() - 1; i >= 0; i--) {
|
||||
ASTArrayTypeDim dim = dimensions.get(i);
|
||||
PSet<SymAnnot> annots = getSymbolicAnnotations(dim);
|
||||
t = ts.arrayType(t).withAnnotations(annots);
|
||||
}
|
||||
|
||||
return ts.arrayType(eltType, node.getArrayDepth());
|
||||
return t;
|
||||
|
||||
} else if (node instanceof ASTPrimitiveType) {
|
||||
|
||||
return ts.getPrimitive(((ASTPrimitiveType) node).getKind());
|
||||
return ts.getPrimitive(((ASTPrimitiveType) node).getKind()).withAnnotations(getTypeAnnotations(node));
|
||||
|
||||
} else if (node instanceof ASTAmbiguousName) {
|
||||
|
||||
@@ -95,29 +107,36 @@ final class TypesFromAst {
|
||||
|
||||
return ts.lub(CollectionUtil.map(((ASTUnionType) node).getComponents(), TypeNode::getTypeMirror));
|
||||
|
||||
} else if (node instanceof ASTVoidType) {
|
||||
|
||||
return ts.NO_TYPE;
|
||||
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Illegal type " + node.getClass() + " " + node);
|
||||
}
|
||||
|
||||
private static JTypeMirror makeFromClassType(TypeSystem ts, ASTClassOrInterfaceType node, Substitution subst) {
|
||||
|
||||
private static PSet<SymAnnot> getSymbolicAnnotations(Annotatable dim) {
|
||||
return SymbolResolutionPass.buildSymbolicAnnotations(dim.getDeclaredAnnotations());
|
||||
}
|
||||
|
||||
|
||||
private static JTypeMirror makeFromClassType(TypeSystem ts, ASTClassOrInterfaceType node, Substitution subst) {
|
||||
if (node == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// TODO error handling, what if we're saying List<String, Int> in source: should be caught before
|
||||
|
||||
ASTClassOrInterfaceType lhsType = node.getQualifier();
|
||||
|
||||
|
||||
PSet<SymAnnot> typeAnnots = getTypeAnnotations(node);
|
||||
JTypeDeclSymbol reference = getReferenceEnsureResolved(node);
|
||||
|
||||
if (reference instanceof JTypeParameterSymbol) {
|
||||
return subst.apply(((JTypeParameterSymbol) reference).getTypeMirror());
|
||||
return subst.apply(((JTypeParameterSymbol) reference).getTypeMirror()).withAnnotations(typeAnnots);
|
||||
}
|
||||
|
||||
JClassType enclosing = getEnclosing(ts, node, subst, lhsType, reference);
|
||||
JClassType enclosing = getEnclosing(ts, node, subst, node.getQualifier(), reference);
|
||||
|
||||
ASTTypeArguments typeArguments = node.getTypeArguments();
|
||||
|
||||
@@ -134,13 +153,13 @@ final class TypesFromAst {
|
||||
}
|
||||
|
||||
if (enclosing != null) {
|
||||
return enclosing.selectInner((JClassSymbol) reference, boundGenerics);
|
||||
return enclosing.selectInner((JClassSymbol) reference, boundGenerics, typeAnnots);
|
||||
} else {
|
||||
return ts.parameterise((JClassSymbol) reference, boundGenerics);
|
||||
return ts.parameterise((JClassSymbol) reference, boundGenerics).withAnnotations(typeAnnots);
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable JClassType getEnclosing(TypeSystem ts, ASTClassOrInterfaceType node, Substitution subst, ASTClassOrInterfaceType lhsType, JTypeDeclSymbol reference) {
|
||||
private static @Nullable JClassType getEnclosing(TypeSystem ts, ASTClassOrInterfaceType node, Substitution subst, @Nullable ASTClassOrInterfaceType lhsType, JTypeDeclSymbol reference) {
|
||||
@Nullable JTypeMirror enclosing = makeFromClassType(ts, lhsType, subst);
|
||||
|
||||
if (enclosing != null && !shouldEnclose(reference)) {
|
||||
@@ -220,4 +239,42 @@ final class TypesFromAst {
|
||||
private static boolean shouldEnclose(JTypeDeclSymbol reference) {
|
||||
return !Modifier.isStatic(reference.getModifiers());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the variable declaration or field or formal, etc, that
|
||||
* may give additional type annotations to the given type.
|
||||
*/
|
||||
private static @Nullable Annotatable getEnclosingAnnotationGiver(JavaNode node) {
|
||||
JavaNode parent = node.getParent();
|
||||
if (node.getIndexInParent() == 0 && parent instanceof ASTClassOrInterfaceType) {
|
||||
// this is an enclosing type
|
||||
return getEnclosingAnnotationGiver(parent);
|
||||
} else if (node.getIndexInParent() == 0 && parent instanceof ASTArrayType) {
|
||||
// the element type of an array type
|
||||
return getEnclosingAnnotationGiver(parent);
|
||||
} else if (!(parent instanceof ASTType) && parent instanceof ASTVariableDeclarator) {
|
||||
return getEnclosingAnnotationGiver(parent);
|
||||
} else if (!(parent instanceof ASTType) && parent instanceof Annotatable) {
|
||||
return (Annotatable) parent;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static PSet<SymAnnot> getTypeAnnotations(ASTType type) {
|
||||
PSet<SymAnnot> annotsOnType = getSymbolicAnnotations(type);
|
||||
if (type instanceof ASTClassOrInterfaceType && ((ASTClassOrInterfaceType) type).getQualifier() != null) {
|
||||
return annotsOnType; // annots on the declaration only apply to the leftmost qualifier
|
||||
}
|
||||
Annotatable parent = getEnclosingAnnotationGiver(type);
|
||||
if (parent != null) {
|
||||
PSet<SymAnnot> parentAnnots = getSymbolicAnnotations(parent);
|
||||
for (SymAnnot parentAnnot : parentAnnots) {
|
||||
// filter annotations by whether they apply to the type use.
|
||||
if (parentAnnot.getAnnotationSymbol().annotationAppliesTo(ElementType.TYPE_USE)) {
|
||||
annotsOnType = annotsOnType.plus(parentAnnot);
|
||||
}
|
||||
}
|
||||
}
|
||||
return annotsOnType;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -117,7 +117,7 @@ public final class PrettyPrintingUtil {
|
||||
sb.append("?");
|
||||
ASTReferenceType bound = ((ASTWildcardType) t).getTypeBoundNode();
|
||||
if (bound != null) {
|
||||
sb.append(((ASTWildcardType) t).hasLowerBound() ? " super " : " extends ");
|
||||
sb.append(((ASTWildcardType) t).isLowerBound() ? " super " : " extends ");
|
||||
prettyPrintTypeNode(sb, bound);
|
||||
}
|
||||
} else if (t instanceof ASTUnionType) {
|
||||
@@ -233,7 +233,7 @@ public final class PrettyPrintingUtil {
|
||||
}
|
||||
|
||||
private static TypePrettyPrinter overloadPrinter() {
|
||||
return new TypePrettyPrinter().useSimpleNames(true).printMethodResult(false);
|
||||
return new TypePrettyPrinter().qualifyNames(false).printMethodResult(false);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ public final class JavaAstProcessor {
|
||||
static {
|
||||
Level level;
|
||||
try {
|
||||
level = Level.valueOf(System.getenv("PMD_DEBUG_LEVEL").toLowerCase(Locale.ROOT));
|
||||
level = Level.valueOf(System.getenv("PMD_DEBUG_LEVEL").toUpperCase(Locale.ROOT));
|
||||
} catch (IllegalArgumentException | NullPointerException ignored) {
|
||||
level = null;
|
||||
}
|
||||
|
||||
+7
-24
@@ -6,25 +6,16 @@ package net.sourceforge.pmd.lang.java.rule.design;
|
||||
|
||||
import static net.sourceforge.pmd.lang.java.ast.AccessNode.Visibility.V_PRIVATE;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnnotation;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAssignableExpr.ASTNamedReferenceExpr;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTClassOrInterfaceDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTConstructorDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTFieldAccess;
|
||||
import net.sourceforge.pmd.lang.java.ast.JModifier;
|
||||
import net.sourceforge.pmd.lang.java.rule.AbstractJavaRulechainRule;
|
||||
import net.sourceforge.pmd.lang.java.types.TypeTestUtil;
|
||||
|
||||
public class ClassWithOnlyPrivateConstructorsShouldBeFinalRule extends AbstractJavaRulechainRule {
|
||||
|
||||
private static final String LOMBOK_VALUE = "lombok.Value";
|
||||
public static final String LOMBOK_NO_ARGS_CONSTRUCTOR = "lombok.NoArgsConstructor";
|
||||
public static final String LOMBOK_REQUIRED_ARGS_CONSTRUCTOR = "lombok.RequiredArgsConstructor";
|
||||
public static final String LOMBOK_ALL_ARGS_CONSTRUCTOR = "lombok.AllArgsConstructor";
|
||||
public static final String LOMBOK_PRIVATE_ACCESS = "PRIVATE";
|
||||
|
||||
public ClassWithOnlyPrivateConstructorsShouldBeFinalRule() {
|
||||
super(ASTClassOrInterfaceDeclaration.class);
|
||||
}
|
||||
@@ -33,7 +24,7 @@ public class ClassWithOnlyPrivateConstructorsShouldBeFinalRule extends AbstractJ
|
||||
public Object visit(ASTClassOrInterfaceDeclaration node, Object data) {
|
||||
if (node.isRegularClass()
|
||||
&& !node.hasModifiers(JModifier.FINAL)
|
||||
&& !node.isAnnotationPresent(LOMBOK_VALUE)
|
||||
&& !node.isAnnotationPresent("lombok.Value")
|
||||
&& !hasPublicLombokConstructors(node)
|
||||
&& hasOnlyPrivateCtors(node)
|
||||
&& hasNoSubclasses(node)) {
|
||||
@@ -43,19 +34,11 @@ public class ClassWithOnlyPrivateConstructorsShouldBeFinalRule extends AbstractJ
|
||||
}
|
||||
|
||||
private boolean hasPublicLombokConstructors(ASTClassOrInterfaceDeclaration node) {
|
||||
List<ASTAnnotation> annotations = node.getDeclaredAnnotations()
|
||||
.filter(t -> TypeTestUtil.isA(LOMBOK_NO_ARGS_CONSTRUCTOR, t)
|
||||
|| TypeTestUtil.isA(LOMBOK_REQUIRED_ARGS_CONSTRUCTOR, t)
|
||||
|| TypeTestUtil.isA(LOMBOK_ALL_ARGS_CONSTRUCTOR, t))
|
||||
.toList();
|
||||
return !annotations.isEmpty()
|
||||
&& annotations.stream().noneMatch(this::hasPrivateAccessModifierOption);
|
||||
}
|
||||
|
||||
private boolean hasPrivateAccessModifierOption(ASTAnnotation annotation) {
|
||||
return annotation.getFlatValue("access")
|
||||
.filterIs(ASTFieldAccess.class)
|
||||
.any(it -> LOMBOK_PRIVATE_ACCESS.equals(it.getName()));
|
||||
return node.getDeclaredAnnotations()
|
||||
.filter(it -> TypeTestUtil.isA("lombok.NoArgsConstructor", it)
|
||||
|| TypeTestUtil.isA("lombok.RequiredArgsConstructor", it)
|
||||
|| TypeTestUtil.isA("lombok.AllArgsConstructor", it))
|
||||
.any(it -> it.getFlatValue("access").filterIs(ASTNamedReferenceExpr.class).none(ref -> "PRIVATE".equals(ref.getName())));
|
||||
}
|
||||
|
||||
private boolean hasNoSubclasses(ASTClassOrInterfaceDeclaration klass) {
|
||||
|
||||
+1
-1
@@ -199,7 +199,7 @@ public class CloseResourceRule extends AbstractJavaRule {
|
||||
.filterNot(ASTVariableDeclaratorId::isExceptionBlockParameter)
|
||||
.filter(this::isVariableNotSpecifiedInTryWithResource)
|
||||
.filter(var -> isResourceTypeOrSubtype(var) || isNodeInstanceOfResourceType(getTypeOfVariable(var)))
|
||||
.filter(var -> var.getAnnotation("lombok.Cleanup") == null)
|
||||
.filterNot(var -> var.isAnnotationPresent("lombok.Cleanup"))
|
||||
.toList();
|
||||
|
||||
for (ASTVariableDeclaratorId var : vars) {
|
||||
|
||||
@@ -5,17 +5,15 @@
|
||||
package net.sourceforge.pmd.lang.java.symbols;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.SymbolEquality;
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.SymbolToStrings;
|
||||
import net.sourceforge.pmd.lang.java.types.TypeSystem;
|
||||
|
||||
/**
|
||||
@@ -43,20 +41,8 @@ final class AnnotWrapper implements SymAnnot {
|
||||
}
|
||||
|
||||
@Override
|
||||
public RetentionPolicy getRetention() {
|
||||
Retention annot = annotationClass.getAnnotation(Retention.class);
|
||||
return annot != null ? annot.value()
|
||||
: RetentionPolicy.CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isOfType(String binaryName) {
|
||||
return annotationClassSymbol.getBinaryName().equals(binaryName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAttributeNames() {
|
||||
return annotationClassSymbol.getAnnotationAttributeNames();
|
||||
public @NonNull JClassSymbol getAnnotationSymbol() {
|
||||
return annotationClassSymbol;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,12 +66,6 @@ final class AnnotWrapper implements SymAnnot {
|
||||
return annotation.equals(o);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getBinaryName() {
|
||||
return annotationClassSymbol.getBinaryName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return SymbolEquality.ANNOTATION.equals(this, o);
|
||||
@@ -96,5 +76,8 @@ final class AnnotWrapper implements SymAnnot {
|
||||
return SymbolEquality.ANNOTATION.hash(this);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return SymbolToStrings.FAKE.toString(this);
|
||||
}
|
||||
}
|
||||
@@ -5,20 +5,32 @@
|
||||
package net.sourceforge.pmd.lang.java.symbols;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.pcollections.HashTreePSet;
|
||||
import org.pcollections.PSet;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
|
||||
|
||||
/**
|
||||
*
|
||||
* A symbol that can have annotations.
|
||||
*/
|
||||
public interface AnnotableSymbol extends JElementSymbol {
|
||||
|
||||
default List<SymAnnot> getDeclaredAnnotations() {
|
||||
return Collections.emptyList();
|
||||
/**
|
||||
* Return the valid symbolic annotations defined on this symbol.
|
||||
* Annotations that could not be converted, eg because
|
||||
* they are written with invalid code, are discarded, so
|
||||
* this might not match the annotations on a node one to one.
|
||||
*/
|
||||
default PSet<SymAnnot> getDeclaredAnnotations() {
|
||||
return HashTreePSet.empty();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return an annotation of the given type, if it is present on this declaration.
|
||||
* This does not consider inherited annotations.
|
||||
*/
|
||||
default SymbolicValue.SymAnnot getDeclaredAnnotation(Class<? extends Annotation> type) {
|
||||
for (SymAnnot a : getDeclaredAnnotations()) {
|
||||
if (a.isOfType(type)) {
|
||||
@@ -28,12 +40,11 @@ public interface AnnotableSymbol extends JElementSymbol {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if an annotation of the given type is present on this declaration.
|
||||
*/
|
||||
default boolean isAnnotationPresent(Class<? extends Annotation> type) {
|
||||
for (SymAnnot a : getDeclaredAnnotations()) {
|
||||
if (a.isOfType(type)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return getDeclaredAnnotation(type) != null;
|
||||
}
|
||||
}
|
||||
@@ -5,18 +5,22 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.symbols;
|
||||
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.pcollections.HashTreePSet;
|
||||
import org.pcollections.PSet;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.ast.ASTAnyTypeDeclaration;
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymEnum;
|
||||
import net.sourceforge.pmd.lang.java.types.JArrayType;
|
||||
import net.sourceforge.pmd.lang.java.types.JClassType;
|
||||
@@ -227,8 +231,30 @@ public interface JClassSymbol extends JTypeDeclSymbol,
|
||||
* Return the simple names of all annotation attributes. If this
|
||||
* is not an annotation type, return an empty set.
|
||||
*/
|
||||
default Set<String> getAnnotationAttributeNames() {
|
||||
return Collections.emptySet();
|
||||
default PSet<String> getAnnotationAttributeNames() {
|
||||
return HashTreePSet.empty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the default value of the attribute if this is an annotation type
|
||||
* with a default. Return null if this is not an annotation type, if there
|
||||
* is no such attribute, or the attribute has no default value. If the name
|
||||
* is in the {@linkplain #getAnnotationAttributeNames() attribute name set},
|
||||
* then the null return value can only mean that the attribute exists but has
|
||||
* no default value.
|
||||
*
|
||||
* @param attrName Attribute name
|
||||
*/
|
||||
default @Nullable SymbolicValue getDefaultAnnotationAttributeValue(String attrName) {
|
||||
if (!isAnnotation()) {
|
||||
return null;
|
||||
}
|
||||
for (JMethodSymbol m : getDeclaredMethods()) {
|
||||
if (m.nameEquals(attrName) && m.isAnnotationAttribute()) {
|
||||
return m.getDefaultAnnotationValue(); // nullable
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -239,14 +265,32 @@ public interface JClassSymbol extends JTypeDeclSymbol,
|
||||
if (!isAnnotation()) {
|
||||
return null;
|
||||
}
|
||||
return Optional.of(this)
|
||||
.map(sym -> sym.getDeclaredAnnotation(Retention.class))
|
||||
return Optional.ofNullable(getDeclaredAnnotation(Retention.class))
|
||||
.map(annot -> annot.getAttribute("value"))
|
||||
.filter(value -> value instanceof SymEnum)
|
||||
.map(value -> ((SymEnum) value).toEnum(RetentionPolicy.class))
|
||||
.orElse(RetentionPolicy.CLASS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether annotations of this annotation type apply to the
|
||||
* given construct, as per the {@link Target} annotation. Return
|
||||
* false if this is not an annotation.
|
||||
*/
|
||||
default boolean annotationAppliesTo(ElementType elementType) {
|
||||
if (!isAnnotation()) {
|
||||
return false;
|
||||
}
|
||||
SymAnnot target = getDeclaredAnnotation(Target.class);
|
||||
if (target == null) {
|
||||
// If an @Target meta-annotation is not present on an annotation type T,
|
||||
// then an annotation of type T may be written as a modifier
|
||||
// for any declaration except a type parameter declaration.
|
||||
return elementType != ElementType.TYPE_PARAMETER;
|
||||
}
|
||||
return target.attributeContains("value", elementType).isTrue();
|
||||
}
|
||||
|
||||
// todo isSealed + getPermittedSubclasses
|
||||
// (isNonSealed is not so useful I think)
|
||||
|
||||
|
||||
+37
-4
@@ -10,8 +10,8 @@ import java.lang.reflect.Modifier;
|
||||
import java.util.List;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
|
||||
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
|
||||
import net.sourceforge.pmd.lang.java.types.Substitution;
|
||||
|
||||
@@ -54,6 +54,32 @@ public interface JExecutableSymbol extends JAccessibleElementSymbol, JTypeParame
|
||||
int getArity();
|
||||
|
||||
|
||||
/**
|
||||
* Return the receiver type with all type annotations, when viewed
|
||||
* under the given substitution. Return null if this method
|
||||
* {@linkplain #hasReceiver() has no receiver}.
|
||||
*
|
||||
* @throws IllegalArgumentException If the argument is not the receiver type of this type.
|
||||
*/
|
||||
@Nullable JTypeMirror getAnnotatedReceiverType(Substitution subst);
|
||||
|
||||
/**
|
||||
* Return true if this method needs to be called on a receiver instance.
|
||||
* This is not the case if the method is static, or a constructor of an
|
||||
* outer or static class.
|
||||
*/
|
||||
default boolean hasReceiver() {
|
||||
if (isStatic()) {
|
||||
return false;
|
||||
}
|
||||
if (this instanceof JConstructorSymbol) {
|
||||
return !getEnclosingClass().isStatic()
|
||||
&& getEnclosingClass().getEnclosingClass() != null;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the class symbol declaring this method or constructor.
|
||||
* This is similar to {@link Constructor#getDeclaringClass()}, resp.
|
||||
@@ -70,11 +96,18 @@ public interface JExecutableSymbol extends JAccessibleElementSymbol, JTypeParame
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the types of the formal parameters, when viewed under the
|
||||
* given substitution. The returned list has one item for each formal.
|
||||
*
|
||||
* @see #getFormalParameters()
|
||||
*/
|
||||
List<JTypeMirror> getFormalParameterTypes(Substitution subst);
|
||||
|
||||
/**
|
||||
* Returns the types of the thrown exceptions, when viewed under the
|
||||
* given substitution.
|
||||
*/
|
||||
List<JTypeMirror> getThrownExceptionTypes(Substitution subst);
|
||||
|
||||
default List<SymAnnot> getFormalParameterAnnotations(int parameterIndex) {
|
||||
return getFormalParameters().get(parameterIndex).getDeclaredAnnotations();
|
||||
}
|
||||
}
|
||||
@@ -43,6 +43,14 @@ public interface JMethodSymbol extends JExecutableSymbol, BoundToNode<ASTMethodD
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return whether this method defines an attribute of the enclosing
|
||||
* annotation type.
|
||||
*/
|
||||
default boolean isAnnotationAttribute() {
|
||||
return !isStatic() && getEnclosingClass().isAnnotation() && getArity() == 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
default <R, P> R acceptVisitor(SymbolVisitor<R, P> visitor, P param) {
|
||||
|
||||
@@ -12,15 +12,16 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.commons.lang3.AnnotationUtils;
|
||||
import org.apache.commons.lang3.ArrayUtils;
|
||||
import org.apache.commons.lang3.ClassUtils;
|
||||
import org.apache.commons.lang3.EnumUtils;
|
||||
import org.apache.commons.lang3.NotImplementedException;
|
||||
import org.apache.commons.lang3.reflect.MethodUtils;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.pcollections.PSet;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.asm.ClassNamesUtil;
|
||||
import net.sourceforge.pmd.lang.java.types.TypeSystem;
|
||||
@@ -72,7 +73,8 @@ public interface SymbolicValue {
|
||||
/**
|
||||
* Returns a symbolic value for the given java object
|
||||
* Returns an annotation element for the given java value. Returns
|
||||
* null if the value cannot be an annotation element.
|
||||
* null if the value cannot be an annotation element or cannot be
|
||||
* constructed.
|
||||
*/
|
||||
static @Nullable SymbolicValue of(TypeSystem ts, Object value) {
|
||||
Objects.requireNonNull(ts);
|
||||
@@ -119,10 +121,31 @@ public interface SymbolicValue {
|
||||
*/
|
||||
@Nullable SymbolicValue getAttribute(String attrName);
|
||||
|
||||
/**
|
||||
* Return the symbol for the declaring class of the annotation.
|
||||
*/
|
||||
@NonNull JClassSymbol getAnnotationSymbol();
|
||||
|
||||
Set<String> getAttributeNames();
|
||||
/**
|
||||
* Return the simple names of all attributes, including those
|
||||
* defined in the annotation type but not explicitly set in this annotation.
|
||||
* Note that if the annotation is reflected from a class file,
|
||||
* we can't know which annotations used their default value, so it
|
||||
* returns a set of all attribute names.
|
||||
*/
|
||||
default PSet<String> getAttributeNames() {
|
||||
return getAnnotationSymbol().getAnnotationAttributeNames();
|
||||
}
|
||||
|
||||
String getBinaryName();
|
||||
/** Return the binary name of the annotation type. */
|
||||
default String getBinaryName() {
|
||||
return getAnnotationSymbol().getBinaryName();
|
||||
}
|
||||
|
||||
/** Return the simple name of the annotation type. */
|
||||
default String getSimpleName() {
|
||||
return getAnnotationSymbol().getSimpleName();
|
||||
}
|
||||
|
||||
@Override
|
||||
default boolean valueEquals(Object o) {
|
||||
@@ -145,7 +168,8 @@ public interface SymbolicValue {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!getAttribute(attrName).valueEquals(attr)) {
|
||||
SymbolicValue myAttr = getAttribute(attrName);
|
||||
if (myAttr == null || !myAttr.valueEquals(attr)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -156,9 +180,17 @@ public interface SymbolicValue {
|
||||
* The retention policy. Note that naturally, members accessed
|
||||
* from class files cannot reflect annotations with {@link RetentionPolicy#SOURCE}.
|
||||
*/
|
||||
RetentionPolicy getRetention();
|
||||
default RetentionPolicy getRetention() {
|
||||
return getAnnotationSymbol().getAnnotationRetention();
|
||||
}
|
||||
|
||||
boolean isOfType(String binaryName);
|
||||
/**
|
||||
* Return true if this annotation's binary name matches the given
|
||||
* binary name.
|
||||
*/
|
||||
default boolean isOfType(String binaryName) {
|
||||
return getBinaryName().equals(binaryName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the annotation has the given type. Note that only
|
||||
@@ -181,11 +213,26 @@ public interface SymbolicValue {
|
||||
return OptionalBool.UNKNOWN;
|
||||
}
|
||||
|
||||
if (attrValue instanceof SymbolicValue) {
|
||||
return OptionalBool.definitely(attr.equals(attrValue));
|
||||
} else {
|
||||
return OptionalBool.definitely(attr.valueEquals(attrValue));
|
||||
return OptionalBool.definitely(SymbolicValueHelper.equalsModuloWrapper(attr, attrValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns YES if the annotation has the attribute set to the
|
||||
* given value, or to an array containing the given value. Returns
|
||||
* NO if that's not the case. Returns UNKNOWN if the attribute
|
||||
* does not exist or is unresolved.
|
||||
*/
|
||||
default OptionalBool attributeContains(String attrName, Object attrValue) {
|
||||
SymbolicValue attr = getAttribute(attrName);
|
||||
if (attr == null) {
|
||||
return OptionalBool.UNKNOWN;
|
||||
}
|
||||
if (attr instanceof SymArray) {
|
||||
// todo what if the value is an array itself
|
||||
return OptionalBool.definitely(((SymArray) attr).containsValue(attrValue));
|
||||
}
|
||||
|
||||
return OptionalBool.definitely(SymbolicValueHelper.equalsModuloWrapper(attr, attrValue));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -266,6 +313,22 @@ public interface SymbolicValue {
|
||||
return length;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return true if this array contains the given object. If the
|
||||
* object is a {@link SymbolicValue}, it uses {@link #equals(Object)},
|
||||
* otherwise it uses {@link #valueEquals(Object)} to compare elements.
|
||||
*/
|
||||
public boolean containsValue(Object value) {
|
||||
if (primArray != null) {
|
||||
// todo I don't know how to code that without switching on the type
|
||||
throw new NotImplementedException("not implemented: containsValue with a primitive array");
|
||||
} else if (elements != null) {
|
||||
return elements.stream().anyMatch(it -> SymbolicValueHelper.equalsModuloWrapper(it, value));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean valueEquals(Object o) {
|
||||
if (!o.getClass().isArray() || !isOkComponentType(o.getClass().getComponentType())) {
|
||||
@@ -312,6 +375,7 @@ public interface SymbolicValue {
|
||||
if (elements != null) {
|
||||
return elements.hashCode();
|
||||
} else {
|
||||
assert primArray != null;
|
||||
return primArray.hashCode();
|
||||
}
|
||||
}
|
||||
@@ -500,7 +564,7 @@ public interface SymbolicValue {
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(binaryName);
|
||||
return binaryName.hashCode();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.symbols;
|
||||
|
||||
/**
|
||||
* Private helper for {@link SymbolicValue} and implementations.
|
||||
*
|
||||
* @author Clément Fournier
|
||||
*/
|
||||
final class SymbolicValueHelper {
|
||||
|
||||
private SymbolicValueHelper() {
|
||||
// utility class
|
||||
}
|
||||
|
||||
static boolean equalsModuloWrapper(SymbolicValue sv, Object other) {
|
||||
if (other instanceof SymbolicValue) {
|
||||
return sv.equals(other);
|
||||
} else {
|
||||
return sv.valueEquals(other);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* BSD-style license; for more info see http://pmd.sourceforge.net/license.html
|
||||
*/
|
||||
|
||||
package net.sourceforge.pmd.lang.java.symbols.internal;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue;
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
|
||||
|
||||
/**
|
||||
* Pretends to be an annotation with no explicit attributes.
|
||||
*
|
||||
* @author Clément Fournier
|
||||
*/
|
||||
public class FakeSymAnnot implements SymAnnot {
|
||||
|
||||
private final JClassSymbol annotationClass;
|
||||
|
||||
public FakeSymAnnot(JClassSymbol annotationClass) {
|
||||
this.annotationClass = annotationClass;
|
||||
assert annotationClass.isAnnotation() : "Not an annotation " + annotationClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable SymbolicValue getAttribute(String attrName) {
|
||||
return annotationClass.getDefaultAnnotationAttributeValue(attrName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull JClassSymbol getAnnotationSymbol() {
|
||||
return annotationClass;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return SymbolToStrings.FAKE.toString(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return SymbolEquality.ANNOTATION.equals(this, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return SymbolEquality.ANNOTATION.hash(this);
|
||||
}
|
||||
}
|
||||
+9
@@ -158,6 +158,7 @@ public final class ImplicitMemberSymbols {
|
||||
private final int modifiers;
|
||||
private final List<JFormalParamSymbol> formals;
|
||||
|
||||
|
||||
FakeExecutableSymBase(JClassSymbol owner,
|
||||
String name,
|
||||
int modifiers,
|
||||
@@ -208,6 +209,14 @@ public final class ImplicitMemberSymbols {
|
||||
return formals.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable JTypeMirror getAnnotatedReceiverType(Substitution subst) {
|
||||
if (!this.hasReceiver()) {
|
||||
return null;
|
||||
}
|
||||
return getTypeSystem().declaration(owner).subst(subst);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getModifiers() {
|
||||
return modifiers;
|
||||
|
||||
+2
-3
@@ -144,7 +144,7 @@ public final class SymbolEquality {
|
||||
public static final EqAndHash<SymAnnot> ANNOTATION = new EqAndHash<SymAnnot>() {
|
||||
@Override
|
||||
public int hash(SymAnnot t1) {
|
||||
return Objects.hash(t1.getBinaryName(), t1.getAttributeNames());
|
||||
return Objects.hash(t1.getBinaryName());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -153,8 +153,7 @@ public final class SymbolEquality {
|
||||
return false;
|
||||
}
|
||||
SymAnnot f2 = (SymAnnot) o;
|
||||
return f1.getBinaryName().equals(f2.getBinaryName())
|
||||
&& f1.getAttributeNames().equals(f2.getAttributeNames());
|
||||
return f1.getBinaryName().equals(f2.getBinaryName());
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
+16
@@ -4,6 +4,8 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.symbols.internal;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JElementSymbol;
|
||||
@@ -14,6 +16,7 @@ import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JTypeParameterSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolVisitor;
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
|
||||
|
||||
public class SymbolToStrings {
|
||||
|
||||
@@ -32,6 +35,19 @@ public class SymbolToStrings {
|
||||
return symbol.acceptVisitor(visitor, new StringBuilder()).toString();
|
||||
}
|
||||
|
||||
public String toString(SymAnnot annot) {
|
||||
String attrs;
|
||||
if (annot.getAttributeNames().isEmpty()) {
|
||||
attrs = "";
|
||||
} else {
|
||||
attrs = annot.getAttributeNames()
|
||||
.stream()
|
||||
.map(name -> name + "=" + annot.getAttribute(name))
|
||||
.collect(Collectors.joining(", ", "(", ")"));
|
||||
}
|
||||
return "@" + annot.getBinaryName() + attrs;
|
||||
}
|
||||
|
||||
private static final class ToStringVisitor implements SymbolVisitor<StringBuilder, StringBuilder> {
|
||||
|
||||
private final String impl;
|
||||
|
||||
+34
@@ -4,6 +4,9 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.symbols.internal.asm;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.objectweb.asm.TypePath;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue;
|
||||
|
||||
class AnnotationBuilderVisitor extends SymbolicValueBuilder {
|
||||
@@ -26,4 +29,35 @@ class AnnotationBuilderVisitor extends SymbolicValueBuilder {
|
||||
public void visitEnd() {
|
||||
owner.addAnnotation(annot);
|
||||
}
|
||||
|
||||
static class TypeAnnotBuilderImpl extends SymbolicValueBuilder {
|
||||
|
||||
private final TypeAnnotationReceiver owner;
|
||||
private final int typeRef;
|
||||
private final @Nullable TypePath path;
|
||||
private final SymbolicAnnotationImpl annot;
|
||||
|
||||
TypeAnnotBuilderImpl(AsmSymbolResolver resolver,
|
||||
TypeAnnotationReceiver owner,
|
||||
int typeRef,
|
||||
@Nullable TypePath path,
|
||||
boolean visible,
|
||||
String descriptor) {
|
||||
super(resolver);
|
||||
this.owner = owner;
|
||||
this.typeRef = typeRef;
|
||||
this.path = path;
|
||||
this.annot = new SymbolicAnnotationImpl(resolver, visible, descriptor);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void acceptValue(String name, SymbolicValue v) {
|
||||
annot.addAttribute(name, v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
owner.acceptTypeAnnotation(typeRef, path, annot);
|
||||
}
|
||||
}
|
||||
}
|
||||
-12
@@ -5,13 +5,6 @@
|
||||
package net.sourceforge.pmd.lang.java.symbols.internal.asm;
|
||||
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
|
||||
/**
|
||||
* Common interface for symbols wrapping a class file "stub".
|
||||
* The class is parsed with ASM, only the signature information
|
||||
@@ -29,9 +22,4 @@ interface AsmStub {
|
||||
return getResolver().getSigParser();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
static <T> List<T> toList(@Nullable T[] arr) {
|
||||
return arr == null ? Collections.emptyList() : Arrays.asList(arr);
|
||||
}
|
||||
|
||||
}
|
||||
+28
-10
@@ -10,14 +10,14 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.pcollections.HashTreePSet;
|
||||
import org.pcollections.PSet;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
|
||||
@@ -27,6 +27,7 @@ import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JMethodSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JTypeDeclSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JTypeParameterOwnerSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue;
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.SymbolEquality;
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.asm.ExecutableStub.CtorStub;
|
||||
@@ -62,9 +63,9 @@ final class ClassStub implements JClassSymbol, AsmStub, AnnotationOwner {
|
||||
private List<JConstructorSymbol> ctors = new ArrayList<>();
|
||||
private List<JFieldSymbol> enumConstants = null;
|
||||
|
||||
private List<SymAnnot> annotations = new ArrayList<>();
|
||||
private PSet<SymAnnot> annotations = HashTreePSet.empty();
|
||||
|
||||
private Set<String> annotAttributes;
|
||||
private PSet<String> annotAttributes;
|
||||
|
||||
private final ParseLock parseLock;
|
||||
|
||||
@@ -126,10 +127,11 @@ final class ClassStub implements JClassSymbol, AsmStub, AnnotationOwner {
|
||||
names.finishOuterClass();
|
||||
}
|
||||
}
|
||||
annotations = Collections.unmodifiableList(annotations);
|
||||
annotAttributes = (accessFlags & Opcodes.ACC_ANNOTATION) != 0
|
||||
? getDeclaredMethods().stream().map(JElementSymbol::getSimpleName).collect(Collectors.toSet())
|
||||
: Collections.emptySet();
|
||||
? getDeclaredMethods().stream().filter(JMethodSymbol::isAnnotationAttribute)
|
||||
.map(JElementSymbol::getSimpleName)
|
||||
.collect(CollectionUtil.toPersistentSet())
|
||||
: HashTreePSet.empty();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -250,7 +252,7 @@ final class ClassStub implements JClassSymbol, AsmStub, AnnotationOwner {
|
||||
|
||||
@Override
|
||||
public void addAnnotation(SymAnnot annot) {
|
||||
annotations.add(annot);
|
||||
annotations = annotations.plus(annot);
|
||||
}
|
||||
|
||||
|
||||
@@ -287,6 +289,12 @@ final class ClassStub implements JClassSymbol, AsmStub, AnnotationOwner {
|
||||
return signature.getTypeParams();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isGeneric() {
|
||||
parseLock.ensureParsed();
|
||||
return signature.isGeneric();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LexicalScope getLexicalScope() {
|
||||
if (scope == null) {
|
||||
@@ -320,17 +328,27 @@ final class ClassStub implements JClassSymbol, AsmStub, AnnotationOwner {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SymAnnot> getDeclaredAnnotations() {
|
||||
public PSet<SymAnnot> getDeclaredAnnotations() {
|
||||
parseLock.ensureParsed();
|
||||
return annotations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getAnnotationAttributeNames() {
|
||||
public PSet<String> getAnnotationAttributeNames() {
|
||||
parseLock.ensureParsed();
|
||||
return annotAttributes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable SymbolicValue getDefaultAnnotationAttributeValue(String attrName) {
|
||||
parseLock.ensureParsed();
|
||||
if (!annotAttributes.contains(attrName)) {
|
||||
// this is a shortcut, because the default impl checks each method
|
||||
return null;
|
||||
}
|
||||
return JClassSymbol.super.getDefaultAnnotationAttributeValue(attrName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable JClassSymbol getEnclosingClass() {
|
||||
parseLock.ensureParsed();
|
||||
|
||||
+5
-2
@@ -11,6 +11,8 @@ import org.objectweb.asm.FieldVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
import org.objectweb.asm.TypePath;
|
||||
import org.objectweb.asm.TypeReference;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.asm.ExecutableStub.CtorStub;
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.asm.ExecutableStub.MethodStub;
|
||||
@@ -65,8 +67,9 @@ class ClassStubBuilder extends ClassVisitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
field.finalizeVisit();
|
||||
public AnnotationVisitor visitTypeAnnotation(int typeRef, @Nullable TypePath typePath, String descriptor, boolean visible) {
|
||||
assert new TypeReference(typeRef).getSort() == TypeReference.FIELD : typeRef;
|
||||
return new AnnotationBuilderVisitor.TypeAnnotBuilderImpl(resolver, field, typeRef, typePath, visible, descriptor);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+37
-20
@@ -4,16 +4,17 @@
|
||||
|
||||
package net.sourceforge.pmd.lang.java.symbols.internal.asm;
|
||||
|
||||
import static net.sourceforge.pmd.util.CollectionUtil.map;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.TypePath;
|
||||
import org.pcollections.HashTreePSet;
|
||||
import org.pcollections.IntTreePMap;
|
||||
import org.pcollections.PMap;
|
||||
import org.pcollections.PSet;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.JConstructorSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JExecutableSymbol;
|
||||
@@ -30,12 +31,12 @@ import net.sourceforge.pmd.lang.java.types.Substitution;
|
||||
import net.sourceforge.pmd.lang.java.types.TypeOps;
|
||||
import net.sourceforge.pmd.lang.java.types.TypeSystem;
|
||||
|
||||
abstract class ExecutableStub extends MemberStubBase implements JExecutableSymbol {
|
||||
abstract class ExecutableStub extends MemberStubBase implements JExecutableSymbol, TypeAnnotationReceiver {
|
||||
|
||||
private final String descriptor;
|
||||
protected final LazyMethodType type;
|
||||
private List<JFormalParamSymbol> params;
|
||||
private Map<Integer, List<SymAnnot>> parameterAnnotations = Collections.emptyMap();
|
||||
private PMap<Integer, PSet<SymAnnot>> parameterAnnotations = IntTreePMap.empty();
|
||||
|
||||
protected ExecutableStub(ClassStub owner,
|
||||
String simpleName,
|
||||
@@ -62,8 +63,12 @@ abstract class ExecutableStub extends MemberStubBase implements JExecutableSymbo
|
||||
@Override
|
||||
public List<JFormalParamSymbol> getFormalParameters() {
|
||||
if (params == null) {
|
||||
this.params = Collections.unmodifiableList(map(type.getParameterTypes(),
|
||||
FormalParamStub::new));
|
||||
List<JTypeMirror> ptypes = type.getParameterTypes();
|
||||
List<JFormalParamSymbol> newParams = new ArrayList<>(ptypes.size());
|
||||
for (int i = 0; i < ptypes.size(); i++) {
|
||||
newParams.add(new FormalParamStub(ptypes.get(i), i));
|
||||
}
|
||||
params = Collections.unmodifiableList(newParams);
|
||||
}
|
||||
return params;
|
||||
}
|
||||
@@ -77,10 +82,18 @@ abstract class ExecutableStub extends MemberStubBase implements JExecutableSymbo
|
||||
public int getArity() {
|
||||
return type.getParameterTypes().size();
|
||||
}
|
||||
|
||||
|
||||
PSet<SymAnnot> getFormalParameterAnnotations(int parameterIndex) {
|
||||
return parameterAnnotations.getOrDefault(parameterIndex, HashTreePSet.empty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<SymAnnot> getFormalParameterAnnotations(int parameterIndex) {
|
||||
return parameterAnnotations.getOrDefault(parameterIndex, Collections.emptyList());
|
||||
public @Nullable JTypeMirror getAnnotatedReceiverType(Substitution subst) {
|
||||
if (!this.hasReceiver()) {
|
||||
return null;
|
||||
}
|
||||
JTypeMirror receiver = getTypeSystem().declaration(getEnclosingClass()).subst(subst);
|
||||
return type.applyReceiverAnnotations(receiver);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -97,11 +110,14 @@ abstract class ExecutableStub extends MemberStubBase implements JExecutableSymbo
|
||||
// overridden by MethodStub
|
||||
}
|
||||
|
||||
@Override
|
||||
public void acceptTypeAnnotation(int typeRef, @Nullable TypePath path, SymAnnot annot) {
|
||||
type.acceptTypeAnnotation(typeRef, path, annot);
|
||||
}
|
||||
|
||||
void addParameterAnnotation(int paramIndex, SymbolicValue.SymAnnot annot) {
|
||||
if (parameterAnnotations.isEmpty()) {
|
||||
parameterAnnotations = new HashMap<>(); // Make writable
|
||||
}
|
||||
parameterAnnotations.computeIfAbsent(paramIndex, ArrayList::new).add(annot);
|
||||
PSet<SymAnnot> newAnnots = parameterAnnotations.getOrDefault(paramIndex, HashTreePSet.empty()).plus(annot);
|
||||
parameterAnnotations = parameterAnnotations.plus(paramIndex, newAnnots);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -112,9 +128,11 @@ abstract class ExecutableStub extends MemberStubBase implements JExecutableSymbo
|
||||
class FormalParamStub implements JFormalParamSymbol {
|
||||
|
||||
private final JTypeMirror type;
|
||||
private final int index;
|
||||
|
||||
FormalParamStub(JTypeMirror type) {
|
||||
FormalParamStub(JTypeMirror type, int index) {
|
||||
this.type = type;
|
||||
this.index = index;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -141,11 +159,10 @@ abstract class ExecutableStub extends MemberStubBase implements JExecutableSymbo
|
||||
public TypeSystem getTypeSystem() {
|
||||
return ExecutableStub.this.getTypeSystem();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public List<SymAnnot> getDeclaredAnnotations() {
|
||||
int paramIndex = ExecutableStub.this.getFormalParameters().indexOf(this);
|
||||
return ExecutableStub.this.getFormalParameterAnnotations(paramIndex);
|
||||
public PSet<SymAnnot> getDeclaredAnnotations() {
|
||||
return ExecutableStub.this.getFormalParameterAnnotations(index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+11
-3
@@ -6,14 +6,17 @@ package net.sourceforge.pmd.lang.java.symbols.internal.asm;
|
||||
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.TypePath;
|
||||
import org.objectweb.asm.TypeReference;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.JFieldSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.SymbolEquality;
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.SymbolToStrings;
|
||||
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
|
||||
import net.sourceforge.pmd.lang.java.types.Substitution;
|
||||
|
||||
class FieldStub extends MemberStubBase implements JFieldSymbol {
|
||||
class FieldStub extends MemberStubBase implements JFieldSymbol, TypeAnnotationReceiver {
|
||||
|
||||
private final LazyTypeSig type;
|
||||
private final @Nullable Object constValue;
|
||||
@@ -29,9 +32,14 @@ class FieldStub extends MemberStubBase implements JFieldSymbol {
|
||||
this.constValue = constValue;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public Object getConstValue() {
|
||||
public void acceptTypeAnnotation(int typeRef, @Nullable TypePath path, SymAnnot annot) {
|
||||
assert new TypeReference(typeRef).getSort() == TypeReference.FIELD : typeRef;
|
||||
this.type.addTypeAnnotation(path, annot);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable Object getConstValue() {
|
||||
return constValue;
|
||||
}
|
||||
|
||||
|
||||
+170
-3
@@ -5,6 +5,8 @@
|
||||
package net.sourceforge.pmd.lang.java.symbols.internal.asm;
|
||||
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -13,10 +15,17 @@ import java.util.stream.Stream;
|
||||
import org.apache.commons.lang3.Validate;
|
||||
import org.checkerframework.checker.nullness.qual.NonNull;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.TypePath;
|
||||
import org.objectweb.asm.TypeReference;
|
||||
|
||||
import net.sourceforge.pmd.lang.java.symbols.JClassSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.JTypeParameterOwnerSymbol;
|
||||
import net.sourceforge.pmd.lang.java.symbols.SymbolicValue.SymAnnot;
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.asm.TypeAnnotationHelper.TypeAnnotationSet;
|
||||
import net.sourceforge.pmd.lang.java.symbols.internal.asm.TypeAnnotationHelper.TypeAnnotationSetWithReferences;
|
||||
import net.sourceforge.pmd.lang.java.types.JClassType;
|
||||
import net.sourceforge.pmd.lang.java.types.JIntersectionType;
|
||||
import net.sourceforge.pmd.lang.java.types.JTypeMirror;
|
||||
import net.sourceforge.pmd.lang.java.types.JTypeVar;
|
||||
import net.sourceforge.pmd.lang.java.types.LexicalScope;
|
||||
@@ -63,7 +72,7 @@ abstract class GenericSigBase<T extends JTypeParameterOwnerSymbol & AsmStub> {
|
||||
return enclosing == null ? LexicalScope.EMPTY : enclosing.getLexicalScope();
|
||||
}
|
||||
|
||||
protected void ensureParsed() {
|
||||
protected final void ensureParsed() {
|
||||
lock.ensureParsed();
|
||||
}
|
||||
|
||||
@@ -72,6 +81,8 @@ abstract class GenericSigBase<T extends JTypeParameterOwnerSymbol & AsmStub> {
|
||||
|
||||
protected abstract boolean postCondition();
|
||||
|
||||
protected abstract boolean isGeneric();
|
||||
|
||||
public void setTypeParams(List<JTypeVar> tvars) {
|
||||
assert this.typeParameters == null : "Type params were already parsed for " + this;
|
||||
this.typeParameters = tvars;
|
||||
@@ -145,6 +156,11 @@ abstract class GenericSigBase<T extends JTypeParameterOwnerSymbol & AsmStub> {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isGeneric() {
|
||||
return signature != null && TypeParamsParser.hasTypeParams(signature);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean postCondition() {
|
||||
return (superItfs != null && superType != null || signature == null) && typeParameters != null;
|
||||
@@ -187,27 +203,82 @@ abstract class GenericSigBase<T extends JTypeParameterOwnerSymbol & AsmStub> {
|
||||
/**
|
||||
* Method or constructor type.
|
||||
*/
|
||||
static class LazyMethodType extends GenericSigBase<ExecutableStub> {
|
||||
static class LazyMethodType extends GenericSigBase<ExecutableStub> implements TypeAnnotationReceiver {
|
||||
|
||||
private final @NonNull String signature;
|
||||
|
||||
private @Nullable TypeAnnotationSet receiverAnnotations;
|
||||
private List<JTypeMirror> parameterTypes;
|
||||
private List<JTypeMirror> exceptionTypes;
|
||||
private JTypeMirror returnType;
|
||||
private @Nullable TypeAnnotationSetWithReferences typeAnnots;
|
||||
private @Nullable String[] rawExceptions;
|
||||
|
||||
/** Used for constructors of inner non-static classes. */
|
||||
private final boolean skipFirstParam;
|
||||
|
||||
LazyMethodType(ExecutableStub ctx, @NonNull String descriptor, @Nullable String genericSig, @SuppressWarnings("PMD.UnusedFormalParameter") @Nullable String[] exceptions, boolean skipFirstParam) {
|
||||
|
||||
// TODO exceptions. Couple of notes:
|
||||
// - the descriptor never contains thrown exceptions
|
||||
// - the signature might not contain the thrown exception types (if they do not depend on type variables)
|
||||
// - the exceptions array also contains unchecked exceptions
|
||||
//
|
||||
// See https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.9.1
|
||||
// TODO test cases
|
||||
// <E extends Exception> void foo() throws E; // descriptor "()V" signature "<TE;>()V^TE;" exceptions: ???
|
||||
// <E> void foo(E e) throws Exception; // descriptor "(Ljava.lang.Object;)V" signature "<TE;>(TE;)V" exceptions: [ "java/lang/Exception" ]
|
||||
// void foo() throws Exception; // descriptor "()V" signature null exceptions: [ "java/lang/Exception" ]
|
||||
LazyMethodType(ExecutableStub ctx,
|
||||
@NonNull String descriptor,
|
||||
@Nullable String genericSig,
|
||||
@Nullable String[] exceptions,
|
||||
boolean skipFirstParam) {
|
||||
super(ctx);
|
||||
this.signature = genericSig != null ? genericSig : descriptor;
|
||||
// generic signatures already omit the synthetic param
|
||||
this.skipFirstParam = skipFirstParam && genericSig == null;
|
||||
this.rawExceptions = exceptions;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doParse() {
|
||||
ctx.sigParser().parseMethodType(this, signature);
|
||||
if (rawExceptions != null && this.exceptionTypes.isEmpty()) {
|
||||
// the descriptor did not contain exceptions. They're in this string array.
|
||||
this.exceptionTypes = Arrays.stream(rawExceptions)
|
||||
.map(ctx.getResolver()::resolveFromInternalNameCannotFail)
|
||||
.map(ctx.getTypeSystem()::rawType)
|
||||
.collect(CollectionUtil.toUnmodifiableList());
|
||||
}
|
||||
if (typeAnnots != null) {
|
||||
// apply type annotations here
|
||||
// this may change type parameters
|
||||
boolean typeParamsWereMutated = typeAnnots.forEach(this::acceptAnnotationAfterParse);
|
||||
if (typeParamsWereMutated) {
|
||||
// Some type parameters were mutated. We need to replace
|
||||
// the old tparams with the annotated ones in all other
|
||||
// types of this signature.
|
||||
|
||||
// This substitution looks like the identity mapping.
|
||||
// It actually does work, because JTypeVar#equals considers only the symbol
|
||||
// and not the type annotations. So unannotated tvars in the type will be
|
||||
// matched with the annotated tvar that has the same symbol.
|
||||
Substitution subst = Substitution.mapping(typeParameters, typeParameters);
|
||||
this.returnType = this.returnType.subst(subst);
|
||||
this.parameterTypes = TypeOps.subst(parameterTypes, subst);
|
||||
this.exceptionTypes = TypeOps.subst(exceptionTypes, subst);
|
||||
}
|
||||
}
|
||||
// null this transient data out
|
||||
this.rawExceptions = null;
|
||||
this.typeAnnots = null;
|
||||
}
|
||||
|
||||
public JTypeMirror applyReceiverAnnotations(JTypeMirror typeMirror) {
|
||||
if (receiverAnnotations == null) {
|
||||
return typeMirror;
|
||||
}
|
||||
return receiverAnnotations.decorate(typeMirror);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -215,6 +286,12 @@ abstract class GenericSigBase<T extends JTypeParameterOwnerSymbol & AsmStub> {
|
||||
return parameterTypes != null && exceptionTypes != null && returnType != null;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean isGeneric() {
|
||||
return TypeParamsParser.hasTypeParams(signature);
|
||||
}
|
||||
|
||||
void setParameterTypes(List<JTypeMirror> params) {
|
||||
Validate.validState(parameterTypes == null);
|
||||
parameterTypes = skipFirstParam ? params.subList(1, params.size())
|
||||
@@ -250,5 +327,95 @@ abstract class GenericSigBase<T extends JTypeParameterOwnerSymbol & AsmStub> {
|
||||
public String toString() {
|
||||
return signature;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void acceptTypeAnnotation(int typeRefInt, @Nullable TypePath path, SymAnnot annot) {
|
||||
// Accumulate type annotations for later
|
||||
// They shouldn't be applied right now because the descriptor maybe has not been parsed yet.
|
||||
if (typeAnnots == null) {
|
||||
typeAnnots = new TypeAnnotationSetWithReferences();
|
||||
}
|
||||
typeAnnots.add(new TypeReference(typeRefInt), path, annot);
|
||||
}
|
||||
|
||||
/**
|
||||
* See {@link MethodVisitor#visitTypeAnnotation(int, TypePath, String, boolean)} for possible
|
||||
* values of typeRef sort (they're each case of the switch).
|
||||
* Returns true if type parameters have been mutated.
|
||||
*/
|
||||
boolean acceptAnnotationAfterParse(TypeReference tyRef, @Nullable TypePath path, SymAnnot annot) {
|
||||
switch (tyRef.getSort()) {
|
||||
case TypeReference.METHOD_RETURN: {
|
||||
assert returnType != null : "Return type is not set";
|
||||
returnType = TypeAnnotationHelper.applySinglePath(returnType, path, annot);
|
||||
return false;
|
||||
}
|
||||
case TypeReference.METHOD_FORMAL_PARAMETER: {
|
||||
assert parameterTypes != null : "Parameter types are not set";
|
||||
int idx = tyRef.getFormalParameterIndex();
|
||||
JTypeMirror annotatedFormal = TypeAnnotationHelper.applySinglePath(parameterTypes.get(idx), path, annot);
|
||||
parameterTypes = TypeAnnotationHelper.replaceAtIndex(parameterTypes, idx, annotatedFormal);
|
||||
return false;
|
||||
}
|
||||
case TypeReference.THROWS: {
|
||||
assert exceptionTypes != null : "Exception types are not set";
|
||||
int idx = tyRef.getExceptionIndex();
|
||||
JTypeMirror annotatedFormal = TypeAnnotationHelper.applySinglePath(exceptionTypes.get(idx), path, annot);
|
||||
exceptionTypes = TypeAnnotationHelper.replaceAtIndex(exceptionTypes, idx, annotatedFormal);
|
||||
return false;
|
||||
}
|
||||
case TypeReference.METHOD_TYPE_PARAMETER: {
|
||||
assert typeParameters != null;
|
||||
assert path == null : "unexpected path " + path;
|
||||
int idx = tyRef.getTypeParameterIndex();
|
||||
// Here we add to the symbol, not the type var.
|
||||
// This ensures that all occurrences of the type var
|
||||
// share these annotations (symbol is unique, contrary to jtypevar)
|
||||
((TParamStub) typeParameters.get(idx).getSymbol()).addAnnotation(annot);
|
||||
return false;
|
||||
}
|
||||
case TypeReference.METHOD_TYPE_PARAMETER_BOUND: {
|
||||
assert typeParameters != null;
|
||||
int tparamIdx = tyRef.getTypeParameterIndex();
|
||||
int boundIdx = tyRef.getTypeParameterBoundIndex();
|
||||
|
||||
JTypeVar tparam = typeParameters.get(tparamIdx);
|
||||
final JTypeMirror newUb = computeNewUpperBound(path, annot, boundIdx, tparam.getUpperBound());
|
||||
typeParameters.set(tparamIdx, tparam.withUpperBound(newUb));
|
||||
return true;
|
||||
}
|
||||
case TypeReference.METHOD_RECEIVER: {
|
||||
if (receiverAnnotations == null) {
|
||||
receiverAnnotations = new TypeAnnotationSet();
|
||||
}
|
||||
receiverAnnotations.add(path, annot);
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid type reference for method or ctor type annotation: " + tyRef.getSort());
|
||||
}
|
||||
}
|
||||
|
||||
private static JTypeMirror computeNewUpperBound(@Nullable TypePath path, SymAnnot annot, int boundIdx, JTypeMirror ub) {
|
||||
final JTypeMirror newUb;
|
||||
if (ub instanceof JIntersectionType) {
|
||||
JIntersectionType intersection = (JIntersectionType) ub;
|
||||
|
||||
// Object is pruned from the component list
|
||||
boundIdx = intersection.getPrimaryBound().isTop() ? boundIdx - 1 : boundIdx;
|
||||
|
||||
List<JTypeMirror> components = new ArrayList<>(intersection.getComponents());
|
||||
JTypeMirror bound = components.get(boundIdx);
|
||||
JTypeMirror newBound = TypeAnnotationHelper.applySinglePath(bound, path, annot);
|
||||
components.set(boundIdx, newBound);
|
||||
JTypeMirror newIntersection = intersection.getTypeSystem().glb(components);
|
||||
newUb = newIntersection;
|
||||
} else {
|
||||
newUb = TypeAnnotationHelper.applySinglePath(ub, path, annot);
|
||||
}
|
||||
return newUb;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loaded 30 of 113 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user