private Pair<ParameterizedTypeTree, AnnotatedDeclaredType> extractParameterizedTypeTree( Tree tree, AnnotatedDeclaredType type) { ParameterizedTypeTree typeargtree = null; switch (tree.getKind()) { case VARIABLE: Tree lt = ((VariableTree) tree).getType(); if (lt instanceof ParameterizedTypeTree) { typeargtree = (ParameterizedTypeTree) lt; } else { // System.out.println("Found a: " + lt); } break; case PARAMETERIZED_TYPE: typeargtree = (ParameterizedTypeTree) tree; break; case NEW_CLASS: NewClassTree nct = (NewClassTree) tree; ExpressionTree nctid = nct.getIdentifier(); if (nctid.getKind() == Tree.Kind.PARAMETERIZED_TYPE) { typeargtree = (ParameterizedTypeTree) nctid; /* * This is quite tricky... for anonymous class instantiations, * the type at this point has no type arguments. By doing the * following, we get the type arguments again. */ type = (AnnotatedDeclaredType) atypeFactory.getAnnotatedType(typeargtree); } break; case ANNOTATED_TYPE: AnnotatedTypeTree tr = (AnnotatedTypeTree) tree; ExpressionTree undtr = tr.getUnderlyingType(); if (undtr instanceof ParameterizedTypeTree) { typeargtree = (ParameterizedTypeTree) undtr; } else if (undtr instanceof IdentifierTree) { // @Something D -> Nothing to do } else { // TODO: add more test cases to ensure that nested types are // handled correctly, // e.g. @Nullable() List<@Nullable Object>[][] Pair<ParameterizedTypeTree, AnnotatedDeclaredType> p = extractParameterizedTypeTree(undtr, type); typeargtree = p.first; type = p.second; } break; case IDENTIFIER: case ARRAY_TYPE: case NEW_ARRAY: case MEMBER_SELECT: case UNBOUNDED_WILDCARD: case EXTENDS_WILDCARD: case SUPER_WILDCARD: case TYPE_PARAMETER: // Nothing to do. // System.out.println("Found a: " + (tree instanceof // ParameterizedTypeTree)); break; default: // the parameterized type is the result of some expression tree. // No need to do anything further. break; // System.err.printf("TypeValidator.visitDeclared unhandled tree: %s of kind %s\n", // tree, tree.getKind()); } return Pair.of(typeargtree, type); }
@Override public Void visitDeclared(AnnotatedDeclaredType type, Tree tree) { if (checker.shouldSkipUses(type.getUnderlyingType().asElement())) return super.visitDeclared(type, tree); { // Ensure that type use is a subtype of the element type // isValidUse determines the erasure of the types. AnnotatedDeclaredType elemType = (AnnotatedDeclaredType) atypeFactory.getAnnotatedType(type.getUnderlyingType().asElement()); if (!visitor.isValidUse(elemType, type, tree)) { reportError(type, tree); } } // System.out.println("Type: " + type); // System.out.println("Tree: " + tree); // System.out.println("Tree kind: " + tree.getKind()); /* * Try to reconstruct the ParameterizedTypeTree from the given tree. * TODO: there has to be a nicer way to do this... */ Pair<ParameterizedTypeTree, AnnotatedDeclaredType> p = extractParameterizedTypeTree(tree, type); ParameterizedTypeTree typeargtree = p.first; type = p.second; if (typeargtree != null) { // We have a ParameterizedTypeTree -> visit it. visitParameterizedType(type, typeargtree); /* * Instead of calling super with the unchanged "tree", adapt the * second argument to be the corresponding type argument tree. This * ensures that the first and second parameter to this method always * correspond. visitDeclared is the only method that had this * problem. */ List<? extends AnnotatedTypeMirror> tatypes = type.getTypeArguments(); if (tatypes == null) return null; // May be zero for a "diamond" (inferred type args in constructor // invocation). int numTypeArgs = typeargtree.getTypeArguments().size(); if (numTypeArgs != 0) { // TODO: this should be an equality, but in // http://buffalo.cs.washington.edu:8080/job/jdk6-daikon-typecheck/2061/console // it failed with: // daikon/Debug.java; message: size mismatch for type arguments: // @NonNull Object and Class<?> // but I didn't manage to reduce it to a test case. assert tatypes.size() <= numTypeArgs : "size mismatch for type arguments: " + type + " and " + typeargtree; for (int i = 0; i < tatypes.size(); ++i) { scan(tatypes.get(i), typeargtree.getTypeArguments().get(i)); } } return null; // Don't call the super version, because it creates a mismatch // between // the first and second parameters. // return super.visitDeclared(type, tree); } return super.visitDeclared(type, tree); }
/** * Get the type of element from the realTypeFactory. Copy it's annotations to inferenceType. Add * a @VarAnnot to all definite type use locations (locations that can be defaulted) in * inferenceType and add an equality constraint between it and the "real" annotations * * @param element The bytecode declaration from which inferenceType was created * @param inferenceType The type of element. inferenceType will be annotated by this method */ public void annotate(final Element element, final AnnotatedTypeMirror inferenceType) { final AnnotatedTypeMirror realType = realTypeFactory.getAnnotatedType(element); CopyUtil.copyAnnotations(realType, inferenceType); inferenceTypeFactory.getNewConstantToVariableAnnotator().visit(inferenceType); }