private static List<Type> typeArgsAsSuper(Type baseType, Type superType, VisitorState state) { Type projectedType = state.getTypes().asSuper(baseType, superType.tsym); if (projectedType != null) { return projectedType.getTypeArguments(); } return new ArrayList<>(); }
@Override public Type get(VisitorState state) { Type type = typeSupplier.get(state); if (type.getTypeArguments().size() <= n) { return state.getSymtab().objectType; } return type.getTypeArguments().get(n); } };
@Override public Type get(VisitorState state) { JCExpression jcExpression = (JCExpression) expressionSupplier.get(state); if (jcExpression.type.getTypeArguments().size() <= n) { return state.getSymtab().objectType; } return jcExpression.type.getTypeArguments().get(n); } };
private static Type predicateType(Type type, VisitorState state) { Symbol predicate = state.getSymbolFromString(java.util.function.Predicate.class.getName()); if (predicate == null) { return null; } Type asPredicate = state.getTypes().asSuper(type, predicate); if (asPredicate == null) { return null; } return getOnlyElement(asPredicate.getTypeArguments(), null); }
private static void findUnannotatedTypeVarRefs( TypeVariableSymbol typeVar, Tree sourceNode, Type type, ArrayDeque<Integer> partialSelector, ImmutableSet.Builder<InferenceVariable> resultBuilder) { List<Type> typeArguments = type.getTypeArguments(); for (int i = 0; i < typeArguments.size(); i++) { partialSelector.push(i); findUnannotatedTypeVarRefs( typeVar, sourceNode, typeArguments.get(i), partialSelector, resultBuilder); partialSelector.pop(); } if (type.tsym.equals(typeVar) && !toNullness(type.getAnnotationMirrors()).isPresent()) { resultBuilder.add( TypeArgInferenceVar.create(ImmutableList.copyOf(partialSelector), sourceNode)); } }
private static Type getComparableTypeArgument(ClassTree tree, VisitorState state) { final Type comparable = state .getTypes() .asSuper(ASTHelpers.getType(tree), state.getSymtab().comparableType.asElement()); if (comparable != null && !comparable.getTypeArguments().isEmpty()) { return Iterables.getOnlyElement(comparable.getTypeArguments()); } return null; } }
private Description matchType(ExpressionTree tree, VisitorState state) { if (CONSTRUCTS_SET_OR_MAP.matches(tree, state)) { List<Type> argumentTypes = ASTHelpers.getResultType(tree).getTypeArguments(); if (argumentTypes.isEmpty()) { return Description.NO_MATCH; } Type typeArg = argumentTypes.get(0); if (isBadType(typeArg, state)) { return describeMatch(tree); } } return Description.NO_MATCH; }
@Override public Void visitMethodInvocation(MethodInvocationTree node, Void unused) { if (found[0]) { return null; } if (MOCKITO_MATCHER.matches(node, state)) { Type stubber = ASTHelpers.getReturnType(node); if (!stubber.getTypeArguments().isEmpty() && ASTHelpers.isSameType( getOnlyElement(stubber.getTypeArguments()), type, state)) { found[0] = true; } } return super.visitMethodInvocation(node, null); } },
private void generateConstraintsFromAnnotations( Type type, Tree sourceTree, ArrayDeque<Integer> argSelector) { List<Type> typeArguments = type.getTypeArguments(); int numberOfTypeArgs = typeArguments.size(); for (int i = 0; i < numberOfTypeArgs; i++) { argSelector.push(i); generateConstraintsFromAnnotations(typeArguments.get(i), sourceTree, argSelector); argSelector.pop(); } // Use equality constraints even for top-level type, since we want to "trust" the annotation // TODO(b/121398981): skip for T extends @<Annot> since they constrain one side only ProperInferenceVar.fromTypeIfAnnotated(type) .ifPresent( annot -> { qualifierConstraints.putEdge( TypeArgInferenceVar.create(ImmutableList.copyOf(argSelector), sourceTree), annot); qualifierConstraints.putEdge( annot, TypeArgInferenceVar.create(ImmutableList.copyOf(argSelector), sourceTree)); }); }
private static boolean isFunctionArgSubtypeOf( Tree param, int argIndex, Type type, VisitorState state) { return ASTHelpers.isSubtype( ASTHelpers.getType(param).getTypeArguments().get(argIndex), type, state); }
/** * Extracts the appropriate type argument from a specific supertype of the given {@code type}. * This handles the case when a subtype has different type arguments than the expected type. For * example, {@code ClassToInstanceMap<T>} implements {@code Map<Class<? extends T>, T>}. * * @param type the (sub)type from which to extract the type argument * @param superTypeSym the symbol of the supertype on which the type parameter is defined * @param typeArgIndex the index of the type argument to extract from the supertype * @param types the {@link Types} utility class from the {@link VisitorState} * @return the type argument, if defined, or null otherwise */ @Nullable protected static final Type extractTypeArgAsMemberOfSupertype( Type type, Symbol superTypeSym, int typeArgIndex, Types types) { Type collectionType = types.asSuper(type, superTypeSym); if (collectionType == null) { return null; } com.sun.tools.javac.util.List<Type> tyargs = collectionType.getTypeArguments(); if (tyargs.size() <= typeArgIndex) { // Collection is raw, nothing we can do. return null; } return tyargs.get(typeArgIndex); } }
private Description validateReceiver( MethodInvocationTree tree, MethodInvocationTree receiver, TypeSymbol type, VisitorState state) { if (receiver.getArguments().size() != 1) { return Description.NO_MATCH; } ExpressionTree argument = getOnlyElement(receiver.getArguments()); Type subjectType = getType(argument); if (isSubtype(subjectType, state.getSymtab().iterableType, state)) { if (subjectType.getTypeArguments().isEmpty()) { return Description.NO_MATCH; } subjectType = getOnlyElement(subjectType.getTypeArguments()); } return subjectType == null || subjectType.tsym.equals(type) || !isSubtype(subjectType, GENERATED_MESSAGE.get(state), state) ? Description.NO_MATCH : describeMatch(tree); } }
private static boolean isVariableClassCreator( VariableTree variableTree, VisitorState state, ClassType classType, Symbol parcelableCreatorSymbol) { Tree typeTree = variableTree.getType(); Type type = ASTHelpers.getType(typeTree); Types types = state.getTypes(); Type superType = types.asSuper(type, parcelableCreatorSymbol); if (superType == null) { return false; } List<Type> typeArguments = superType.getTypeArguments(); if (typeArguments.isEmpty()) { // raw creator return true; } return ASTHelpers.isSubtype(classType, Iterables.getOnlyElement(typeArguments), state); } }
/** Ignore some common ThreadLocal type arguments that are fine to have per-instance copies of. */ private boolean wellKnownTypeArgument(NewClassTree tree, VisitorState state) { Type type = getType(tree); if (type == null) { return false; } type = state.getTypes().asSuper(type, state.getSymbolFromString("java.lang.ThreadLocal")); if (type == null) { return false; } if (type.getTypeArguments().isEmpty()) { return false; } Type argType = getOnlyElement(type.getTypeArguments()); if (WELL_KNOWN_TYPES.contains(argType.asElement().getQualifiedName().toString())) { return true; } if (isSubtype(argType, state.getTypeFromString("java.text.DateFormat"), state)) { return true; } return false; } }
@Override public boolean matches(Tree tree, VisitorState state) { Symbol sym = state.getSymbolFromString(clazz); if (sym == null) { return false; } Type type = ASTHelpers.getType(tree); if (!ASTHelpers.isSubtype(type, sym.type, state)) { return false; } Types types = state.getTypes(); Type superType = types.asSuper(type, sym); if (superType == null) { return false; } List<Type> typeArguments = superType.getTypeArguments(); if (typeArguments.isEmpty()) { return false; } return ASTHelpers.isSameType( typeArguments.get(typeArgumentIndex), state.getTypeFromString(URL_CLASS), state); } }
@Override public Description matchMethodInvocation( MethodInvocationTree methodInvocationTree, VisitorState visitorState) { if (!TO_ARRAY_MATCHER.matches(methodInvocationTree, visitorState)) { return NO_MATCH; } Types types = visitorState.getTypes(); Type variableType = types.elemtype(getType(getOnlyElement(methodInvocationTree.getArguments()))); if (variableType == null) { return NO_MATCH; } Type collectionType = types.asSuper( ASTHelpers.getReceiverType(methodInvocationTree), visitorState.getSymbolFromString("java.util.Collection")); List<Type> typeArguments = collectionType.getTypeArguments(); if (!typeArguments.isEmpty() && !types.isCastable( types.erasure(variableType), types.erasure(getOnlyElement(typeArguments)))) { return describeMatch(methodInvocationTree); } return NO_MATCH; } }
@Override public Description matchMethodInvocation(MethodInvocationTree tree, VisitorState state) { if (!MATCHER.matches(tree, state)) { return NO_MATCH; } Type classType = getType(getOnlyElement(tree.getArguments())); if (classType == null || classType.getTypeArguments().isEmpty()) { return NO_MATCH; } Type type = getUpperBound(getOnlyElement(classType.getTypeArguments()), state.getTypes()); if (isSameType(type, state.getSymtab().annotationType, state)) { return NO_MATCH; } RetentionPolicy retention = state.getTypes().getRetention(type.asElement()); switch (retention) { case RUNTIME: break; case SOURCE: case CLASS: return buildDescription(tree) .setMessage( String.format( "%s; %s has %s retention", message(), type.asElement().getSimpleName(), retention)) .build(); } return NO_MATCH; } }
@Override public Description matchMemberReference(MemberReferenceTree tree, VisitorState state) { Type predicateType = predicateType(ASTHelpers.getType(tree), state); if (predicateType == null) { return NO_MATCH; } Type receiverType = ASTHelpers.getReceiverType(tree); if (tree.getName().contentEquals("equals") && !EqualsIncompatibleType.compatibilityOfTypes(receiverType, predicateType, state) .compatible()) { return buildMessage(receiverType, predicateType, tree); } if (tree.getName().contentEquals("isInstance") && ASTHelpers.isSameType(receiverType, state.getSymtab().classType, state) && !receiverType.getTypeArguments().isEmpty()) { Type argumentType = receiverType.getTypeArguments().get(0); Type upperBound = ASTHelpers.getUpperBound(predicateType, state.getTypes()); if (!EqualsIncompatibleType.compatibilityOfTypes(upperBound, argumentType, state) .compatible()) { return buildMessage(upperBound, argumentType, tree); } } return NO_MATCH; }
public static Collection<? extends CodeTransformer> extractRules( ClassTree tree, Context context) { ClassSymbol sym = ASTHelpers.getSymbol(tree); RefasterRuleBuilderScanner scanner = new RefasterRuleBuilderScanner(context); // visit abstract methods first List<MethodTree> methods = new Ordering<MethodTree>() { @Override public int compare(MethodTree l, MethodTree r) { return Boolean.compare( l.getModifiers().getFlags().contains(Modifier.ABSTRACT), r.getModifiers().getFlags().contains(Modifier.ABSTRACT)); } }.reverse().immutableSortedCopy(Iterables.filter(tree.getMembers(), MethodTree.class)); scanner.visit(methods, null); UTemplater templater = new UTemplater(context); List<UType> types = templater.templateTypes(sym.type.getTypeArguments()); return scanner.createMatchers( Iterables.filter(types, UTypeVar.class), sym.getQualifiedName().toString(), UTemplater.annotationMap(sym)); }
@Override public Description matchNewClass(NewClassTree tree, VisitorState state) { // check instantiations of `@ImmutableTypeParameter`s in generic constructor invocations checkInvocation( tree, ((JCNewClass) tree).constructorType, state, ((JCNewClass) tree).constructor); // check instantiations of `@ImmutableTypeParameter`s in class constructor invocations ImmutableAnalysis analysis = createImmutableAnalysis(state); Violation info = analysis.checkInstantiation( ASTHelpers.getSymbol(tree.getIdentifier()).getTypeParameters(), ASTHelpers.getType(tree).getTypeArguments()); if (info.isPresent()) { state.reportMatch(buildDescription(tree).setMessage(info.message()).build()); } return NO_MATCH; }